Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make egui_glow painter to work on web #868

Merged
merged 30 commits into from
Nov 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
61cc0dc
glow painter webgl support.
KentaTheBugMaker Nov 1, 2021
fa22f04
Merge branch 'emilk:master' into master
KentaTheBugMaker Nov 1, 2021
33648f7
some clippy fix
KentaTheBugMaker Nov 1, 2021
2c3296e
Merge remote-tracking branch 'origin/master'
KentaTheBugMaker Nov 1, 2021
eaf58a0
some clippy workaround
KentaTheBugMaker Nov 2, 2021
4eb841e
clippy fix for web
KentaTheBugMaker Nov 2, 2021
d71656e
add workaround for painter_only
KentaTheBugMaker Nov 2, 2021
e3b4120
fix compatibility problem in fragment shader GLSL __VERSION__ <140
KentaTheBugMaker Nov 2, 2021
1887768
remove unneeded `gl_FragColor = v_rgba * texture_rgba;`
KentaTheBugMaker Nov 2, 2021
901cfa0
almost fixed issue
KentaTheBugMaker Nov 2, 2021
5bd0d5d
revert eframe change.
KentaTheBugMaker Nov 2, 2021
88027b1
small clippy fix ,toml fix and rewrite changelog
KentaTheBugMaker Nov 2, 2021
fb42a66
webgl2 return correct name.
KentaTheBugMaker Nov 2, 2021
51981c5
correct Cargo.toml for egui_glow.
KentaTheBugMaker Nov 2, 2021
18b05b9
clippy::needless-pass-by-value fix
KentaTheBugMaker Nov 2, 2021
dd2f8a6
Update egui_web/CHANGELOG.md
KentaTheBugMaker Nov 3, 2021
a4d7146
Update egui_glow/src/painter.rs
KentaTheBugMaker Nov 3, 2021
09c32b6
Update egui_glow/CHANGELOG.md
KentaTheBugMaker Nov 3, 2021
a47e7e5
Update egui_glow/src/painter.rs
KentaTheBugMaker Nov 3, 2021
ff25929
Update egui_glow/examples/pure_glow.rs
KentaTheBugMaker Nov 3, 2021
22245be
Update egui_glow/src/painter.rs
KentaTheBugMaker Nov 3, 2021
dc72728
Update egui_glow/src/painter.rs
KentaTheBugMaker Nov 3, 2021
e95ecb6
test we have to use emulated vao.
KentaTheBugMaker Nov 3, 2021
7faa359
report painter initializing error to caller.
KentaTheBugMaker Nov 3, 2021
7ac5c6a
merge upstream
KentaTheBugMaker Nov 3, 2021
a9c8b26
merge upstream
KentaTheBugMaker Nov 3, 2021
efcadb5
workaround for cargo check --all-target --all-feature
KentaTheBugMaker Nov 3, 2021
58ab9e6
fix docstring
KentaTheBugMaker Nov 3, 2021
c496e92
fix unfixed clippy issue
KentaTheBugMaker Nov 3, 2021
385c1fb
check glow backend build
KentaTheBugMaker Nov 3, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions egui_glow/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ All notable changes to the `egui_glow` integration will be noted in this file.


## Unreleased
* Make winit/glutin an optional dependency ([#868](https://github.com/emilk/egui/pull/868)).
* Simplify `EguiGlow` interface ([#871](https://github.com/emilk/egui/pull/871)).


Expand Down
19 changes: 15 additions & 4 deletions egui_glow/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,24 @@ all-features = true

[dependencies]
egui = { version = "0.15.0", path = "../egui", default-features = false, features = ["single_threaded"] }
egui-winit = { version = "0.15.0", path = "../egui-winit", default-features = false, features = ["epi"] }
epi = { version = "0.15.0", path = "../epi", optional = true }

epi = { version = "0.15.0", path = "../epi", optional = true }
glow = "0.11"
glutin = "0.27"
memoffset = "0.6"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
egui-winit = { version = "0.15.0", path = "../egui-winit", default-features = false, features = ["epi"], optional = true }
glutin = { version = "0.27.0", optional = true }

[target.'cfg(target_arch = "wasm32")'.dependencies]
web-sys = { version = "0.3", features=["console"] }
wasm-bindgen = { version = "0.2" }

[dev-dependencies]
image = { version = "0.23", default-features = false, features = ["png"] }

[features]
default = ["clipboard", "default_fonts", "links", "persistence"]
default = ["clipboard", "default_fonts", "links", "persistence", "winit"]

# enable cut/copy/paste to OS clipboard.
# if disabled a clipboard will be simulated so you can still copy/paste within the egui app.
Expand All @@ -58,3 +64,8 @@ persistence = [

# experimental support for a screen reader
screen_reader = ["egui-winit/screen_reader"]

# enable glutin/winit integration.
# if you want to use glow painter on web disable it.
# if disabled reduce crate size and build time.
winit= ["egui-winit","glutin"]
26 changes: 5 additions & 21 deletions egui_glow/src/epi_backend.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,7 @@
use crate::*;
use egui::Color32;
#[cfg(target_os = "windows")]
use glutin::platform::windows::WindowBuilderExtWindows;

impl epi::TextureAllocator for Painter {
fn alloc_srgba_premultiplied(
&mut self,
size: (usize, usize),
srgba_pixels: &[Color32],
) -> egui::TextureId {
let id = self.alloc_user_texture();
self.set_user_texture(id, size, srgba_pixels);
id
}

fn free(&mut self, id: egui::TextureId) {
self.free_user_texture(id);
}
}

struct RequestRepaintEvent;

struct GlowRepaintSignal(std::sync::Mutex<glutin::event_loop::EventLoopProxy<RequestRepaintEvent>>);
Expand Down Expand Up @@ -77,7 +60,9 @@ pub fn run(app: Box<dyn epi::App>, native_options: &epi::NativeOptions) -> ! {
event_loop.create_proxy(),
)));

let mut painter = crate::Painter::new(&gl);
let mut painter = crate::Painter::new(&gl, None)
.map_err(|error| eprintln!("some OpenGL error occurred {}\n", error))
.unwrap();
let mut integration = egui_winit::epi::EpiIntegration::new(
"egui_glow",
gl_window.window(),
Expand Down Expand Up @@ -111,13 +96,12 @@ pub fn run(app: Box<dyn epi::App>, native_options: &epi::NativeOptions) -> ! {
gl.clear_color(color[0], color[1], color[2], color[3]);
gl.clear(glow::COLOR_BUFFER_BIT);
}

painter.upload_egui_texture(&gl, &integration.egui_ctx.texture());
painter.paint_meshes(
&gl_window,
gl_window.window().inner_size().into(),
&gl,
integration.egui_ctx.pixels_per_point(),
clipped_meshes,
&integration.egui_ctx.texture(),
);

gl_window.swap_buffers().unwrap();
Expand Down
29 changes: 21 additions & 8 deletions egui_glow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,25 +87,32 @@
#![allow(clippy::float_cmp)]
#![allow(clippy::manual_range_contains)]

mod painter;
pub mod painter;
pub use glow;
pub use painter::Painter;

#[cfg(feature = "epi")]
#[cfg(feature = "winit")]
mod epi_backend;
#[cfg(feature = "epi")]
pub use epi_backend::{run, NativeOptions};
mod misc_util;
mod post_process;
mod shader_version;
mod vao_emulate;

#[cfg(not(target_arch = "wasm32"))]
pub use egui_winit;
#[cfg(all(feature = "epi", feature = "winit"))]
pub use epi_backend::{run, NativeOptions};

// ----------------------------------------------------------------------------

/// Use [`egui`] from a [`glow`] app.
#[cfg(feature = "winit")]
pub struct EguiGlow {
pub egui_ctx: egui::CtxRef,
pub egui_winit: egui_winit::State,
pub painter: crate::Painter,
}

#[cfg(feature = "winit")]
impl EguiGlow {
pub fn new(
gl_window: &glutin::WindowedContext<glutin::PossiblyCurrent>,
Expand All @@ -114,7 +121,11 @@ impl EguiGlow {
Self {
egui_ctx: Default::default(),
egui_winit: egui_winit::State::new(gl_window.window()),
painter: crate::Painter::new(gl),
painter: crate::Painter::new(gl, None)
.map_err(|error| {
eprintln!("some error occurred in initializing painter\n{}", error);
})
.unwrap(),
}
}

Expand Down Expand Up @@ -158,12 +169,14 @@ impl EguiGlow {
shapes: Vec<egui::epaint::ClippedShape>,
) {
let clipped_meshes = self.egui_ctx.tessellate(shapes);
let dimensions: [u32; 2] = gl_window.window().inner_size().into();
self.painter
.upload_egui_texture(gl, &self.egui_ctx.texture());
self.painter.paint_meshes(
gl_window,
dimensions,
gl,
self.egui_ctx.pixels_per_point(),
clipped_meshes,
&self.egui_ctx.texture(),
);
}

Expand Down
231 changes: 231 additions & 0 deletions egui_glow/src/misc_util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
#![allow(unsafe_code)]
use glow::HasContext;
use std::option::Option::Some;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::JsValue;

pub(crate) fn srgbtexture2d(
gl: &glow::Context,
is_webgl_1: bool,
srgb_support: bool,
data: &[u8],
w: usize,
h: usize,
) -> glow::Texture {
assert_eq!(data.len(), w * h * 4);
assert!(w >= 1);
assert!(h >= 1);
unsafe {
let tex = gl.create_texture().unwrap();
gl.bind_texture(glow::TEXTURE_2D, Some(tex));

gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_MAG_FILTER,
glow::LINEAR as i32,
);
gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_MIN_FILTER,
glow::LINEAR as i32,
);
gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_WRAP_S,
glow::CLAMP_TO_EDGE as i32,
);
gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_WRAP_T,
glow::CLAMP_TO_EDGE as i32,
);
if is_webgl_1 {
let format = if srgb_support {
glow::SRGB_ALPHA
} else {
glow::RGBA
};
gl.tex_image_2d(
glow::TEXTURE_2D,
0,
format as i32,
w as i32,
h as i32,
0,
format,
glow::UNSIGNED_BYTE,
Some(data),
);
} else {
gl.tex_storage_2d(glow::TEXTURE_2D, 1, glow::SRGB8_ALPHA8, w as i32, h as i32);
gl.tex_sub_image_2d(
glow::TEXTURE_2D,
0,
0,
0,
w as i32,
h as i32,
glow::RGBA,
glow::UNSIGNED_BYTE,
glow::PixelUnpackData::Slice(data),
);
}
assert_eq!(gl.get_error(), glow::NO_ERROR, "OpenGL error occurred!");
tex
}
}

pub(crate) unsafe fn as_u8_slice<T>(s: &[T]) -> &[u8] {
std::slice::from_raw_parts(s.as_ptr().cast::<u8>(), s.len() * std::mem::size_of::<T>())
}

#[cfg(target_arch = "wasm32")]
pub(crate) fn glow_debug_print(s: impl Into<JsValue>) {
web_sys::console::log_1(&s.into());
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn glow_debug_print(s: impl std::fmt::Display) {
println!("{}", s);
}

pub(crate) unsafe fn compile_shader(
gl: &glow::Context,
shader_type: u32,
source: &str,
) -> Result<glow::Shader, String> {
let shader = gl.create_shader(shader_type)?;

gl.shader_source(shader, source);

gl.compile_shader(shader);

if gl.get_shader_compile_status(shader) {
Ok(shader)
} else {
Err(gl.get_shader_info_log(shader))
}
}

pub(crate) unsafe fn link_program<'a, T: IntoIterator<Item = &'a glow::Shader>>(
gl: &glow::Context,
shaders: T,
) -> Result<glow::Program, String> {
let program = gl.create_program()?;

for shader in shaders {
gl.attach_shader(program, *shader);
}

gl.link_program(program);

if gl.get_program_link_status(program) {
Ok(program)
} else {
Err(gl.get_program_info_log(program))
}
}
///Wrapper around Emulated VAO and GL's VAO
pub(crate) enum VAO {
Emulated(crate::vao_emulate::EmulatedVao),
Native(crate::glow::VertexArray),
}

impl VAO {
pub(crate) unsafe fn native(gl: &glow::Context) -> Self {
Self::Native(gl.create_vertex_array().unwrap())
}

pub(crate) unsafe fn emulated() -> Self {
Self::Emulated(crate::vao_emulate::EmulatedVao::new())
}

pub(crate) unsafe fn bind_vertex_array(&self, gl: &glow::Context) {
match self {
VAO::Emulated(vao) => vao.bind_vertex_array(gl),
VAO::Native(vao) => gl.bind_vertex_array(Some(*vao)),
}
}

pub(crate) unsafe fn bind_buffer(&mut self, gl: &glow::Context, buffer: &glow::Buffer) {
match self {
VAO::Emulated(vao) => vao.bind_buffer(buffer),
VAO::Native(_) => gl.bind_buffer(glow::ARRAY_BUFFER, Some(*buffer)),
}
}

pub(crate) unsafe fn add_new_attribute(
&mut self,
gl: &glow::Context,
buffer_info: crate::vao_emulate::BufferInfo,
) {
match self {
VAO::Emulated(vao) => vao.add_new_attribute(buffer_info),
VAO::Native(_) => {
gl.vertex_attrib_pointer_f32(
buffer_info.location,
buffer_info.vector_size,
buffer_info.data_type,
buffer_info.normalized,
buffer_info.stride,
buffer_info.offset,
);
gl.enable_vertex_attrib_array(buffer_info.location);
}
}
}

pub(crate) unsafe fn unbind_vertex_array(&self, gl: &glow::Context) {
match self {
VAO::Emulated(vao) => vao.unbind_vertex_array(gl),
VAO::Native(_) => {
gl.bind_vertex_array(None);
}
}
}
}

pub(crate) unsafe fn need_to_emulate_vao(gl: &glow::Context) -> bool {
let web_sig = "WebGL ";
let es_sig = "OpenGL ES ";
let version_string = gl.get_parameter_string(glow::VERSION);
if let Some(pos) = version_string.rfind(web_sig) {
let version_str = &version_string[pos + web_sig.len()..];
glow_debug_print(format!(
"detected WebGL prefix at {}:{}",
pos + web_sig.len(),
version_str
));
if version_str.contains("1.0") {
//need to test OES_vertex_array_object .
gl.supported_extensions()
.contains("OES_vertex_array_object")
} else {
false
}
} else if let Some(pos) = version_string.rfind(es_sig) {
//glow targets es2.0+ so we don't concern about OpenGL ES-CM,OpenGL ES-CL
glow_debug_print(format!(
"detected OpenGL ES prefix at {}:{}",
pos + es_sig.len(),
&version_string[pos + es_sig.len()..]
));
if version_string.contains("2.0") {
//need to test OES_vertex_array_object .
gl.supported_extensions()
.contains("OES_vertex_array_object")
} else {
false
}
} else {
glow_debug_print(format!("detected OpenGL:{}", version_string));
//from OpenGL 3 vao into core
if version_string.starts_with('2') {
// I found APPLE_vertex_array_object , GL_ATI_vertex_array_object ,ARB_vertex_array_object
// but APPLE's and ATI's very old extension.
gl.supported_extensions()
.contains("ARB_vertex_array_object")
} else {
false
}
}
}
Loading