From 61cc0dc8f229b97128ce171de341679e1faa5ebf Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Tue, 2 Nov 2021 06:57:24 +0900 Subject: [PATCH 01/27] glow painter webgl support. egui_web add `use_glow_painter` feature. egui_glow add `painter_only` feature. --- egui_glow/CHANGELOG.md | 3 +- egui_glow/Cargo.toml | 15 +- egui_glow/src/create_context_for_canvas.rs | 42 ++ egui_glow/src/epi_backend.rs | 35 +- egui_glow/src/lib.rs | 26 +- egui_glow/src/misc_util.rs | 130 +++++ egui_glow/src/painter.rs | 453 ++++++++---------- egui_glow/src/post_process.rs | 240 ++++++++++ egui_glow/src/shader/fragment.glsl | 97 ++-- egui_glow/src/shader/post_fragment_100es.glsl | 22 + egui_glow/src/shader/post_vertex_100es.glsl | 8 + egui_glow/src/shader/vertex.glsl | 2 +- egui_glow/src/shader_version.rs | 73 +++ egui_glow/src/vao_emulate.rs | 58 +++ egui_web/CHANGELOG.md | 2 +- egui_web/Cargo.toml | 3 +- egui_web/src/backend.rs | 26 +- egui_web/src/glow_wrapping.rs | 53 ++ egui_web/src/lib.rs | 4 + 19 files changed, 982 insertions(+), 310 deletions(-) create mode 100644 egui_glow/src/create_context_for_canvas.rs create mode 100644 egui_glow/src/misc_util.rs create mode 100644 egui_glow/src/post_process.rs create mode 100644 egui_glow/src/shader/post_fragment_100es.glsl create mode 100644 egui_glow/src/shader/post_vertex_100es.glsl create mode 100644 egui_glow/src/shader_version.rs create mode 100644 egui_glow/src/vao_emulate.rs create mode 100644 egui_web/src/glow_wrapping.rs diff --git a/egui_glow/CHANGELOG.md b/egui_glow/CHANGELOG.md index 5e34b4a64d0..5f7a650e42d 100644 --- a/egui_glow/CHANGELOG.md +++ b/egui_glow/CHANGELOG.md @@ -3,7 +3,8 @@ All notable changes to the `egui_glow` integration will be noted in this file. ## Unreleased - +* add `painter_only` feature to reuse painter implementation. +* no glutin dependency in painter . ## 0.15.0 - 2021-10-24 `egui_glow` has been newly created, with feature parity to `egui_glium`. diff --git a/egui_glow/Cargo.toml b/egui_glow/Cargo.toml index 6279c884180..c874c2bbf23 100644 --- a/egui_glow/Cargo.toml +++ b/egui_glow/Cargo.toml @@ -23,19 +23,26 @@ 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"] } +glutin = "0.27" + + +[target.'cfg(target_arch = "wasm32")'.dependencies] +web-sys = { version = "0.3", features=["HtmlCanvasElement", "WebGl2RenderingContext","WebGlRenderingContext", "Window","console"] } +wasm-bindgen = { version = "0.2" } + [dev-dependencies] image = { version = "0.23", default-features = false, features = ["png"] } [features] default = ["clipboard", "default_fonts", "links", "persistence"] - +painter_only = [] # enable cut/copy/paste to OS clipboard. # if disabled a clipboard will be simulated so you can still copy/paste within the egui app. clipboard = ["egui-winit/clipboard"] diff --git a/egui_glow/src/create_context_for_canvas.rs b/egui_glow/src/create_context_for_canvas.rs new file mode 100644 index 00000000000..78a8cc2052e --- /dev/null +++ b/egui_glow/src/create_context_for_canvas.rs @@ -0,0 +1,42 @@ +#[cfg(target_arch = "wasm32")] +use web_sys::HtmlCanvasElement; + +/// Create glow context from given canvas. +/// Automatically choose webgl or webgl2 context. +/// first try webgl2 falling back to webgl1 +#[cfg(target_arch = "wasm32")] +pub fn init_glow_context_from_canvas(canvas: &HtmlCanvasElement) -> glow::Context { + use crate::misc_util::glow_debug_print; + use wasm_bindgen::JsCast; + let ctx = canvas.get_context("webgl2"); + if let Ok(ctx) = ctx { + glow_debug_print("webgl found"); + if let Some(ctx) = ctx { + glow_debug_print("webgl 2 selected"); + let gl_ctx = ctx.dyn_into::().unwrap(); + glow::Context::from_webgl2_context(gl_ctx) + } else { + let ctx = canvas.get_context("webgl"); + if let Ok(ctx) = ctx { + glow_debug_print("falling back to webgl1"); + if let Some(ctx) = ctx { + glow_debug_print("webgl selected"); + let gl_ctx = ctx.dyn_into::().unwrap(); + glow_debug_print("success"); + glow::Context::from_webgl1_context(gl_ctx) + } else { + panic!("tried webgl1 but can't get context"); + } + } else { + panic!("tried webgl1 but can't get context"); + } + } + } else { + panic!("tried webgl2 but something went wrong"); + } +} +/// dummy for clippy +#[cfg(not(target_arch = "wasm32"))] +pub fn init_glow_context_from_canvas(_: T) -> glow::Context { + unimplemented!("this is only enabled wasm target") +} diff --git a/egui_glow/src/epi_backend.rs b/egui_glow/src/epi_backend.rs index 86c867618f4..74a419e65fe 100644 --- a/egui_glow/src/epi_backend.rs +++ b/egui_glow/src/epi_backend.rs @@ -1,7 +1,10 @@ +use crate::painter::UserTexture; use crate::*; use egui::Color32; +use egui::TextureId; #[cfg(target_os = "windows")] use glutin::platform::windows::WindowBuilderExtWindows; +#[cfg(not(feature = "painter_only"))] use std::time::Instant; impl epi::TextureAllocator for Painter { @@ -20,16 +23,36 @@ impl epi::TextureAllocator for Painter { } } -struct RequestRepaintEvent; +impl epi::NativeTexture for Painter { + type Texture = glow::Texture; -struct GlowRepaintSignal(std::sync::Mutex>); + fn register_native_texture(&mut self, native: Self::Texture) -> TextureId { + self.register_glow_texture(native) + } + fn replace_native_texture(&mut self, id: TextureId, replacing: Self::Texture) { + if let egui::TextureId::User(id) = id { + if let Some(Some(user_texture)) = self.user_textures.get_mut(id as usize) { + *user_texture = UserTexture { + data: vec![], + gl_texture: Some(replacing), + size: (0, 0), + }; + } + } + } +} +#[cfg(not(feature = "painter_only"))] +struct RequestRepaintEvent; +#[cfg(not(feature = "painter_only"))] +struct GlowRepaintSignal(std::sync::Mutex>); +#[cfg(not(feature = "painter_only"))] impl epi::RepaintSignal for GlowRepaintSignal { fn request_repaint(&self) { self.0.lock().unwrap().send_event(RequestRepaintEvent).ok(); } } - +#[cfg(not(feature = "painter_only"))] #[allow(unsafe_code)] fn create_display( window_builder: glutin::window::WindowBuilder, @@ -59,7 +82,7 @@ fn create_display( (gl_window, gl) } - +#[cfg(not(feature = "painter_only"))] fn integration_info( window: &glutin::window::Window, previous_frame_time: Option, @@ -74,9 +97,9 @@ fn integration_info( } // ---------------------------------------------------------------------------- - +#[cfg(not(feature = "painter_only"))] pub use epi::NativeOptions; - +#[cfg(not(feature = "painter_only"))] /// Run an egui app #[allow(unsafe_code)] pub fn run(mut app: Box, native_options: &epi::NativeOptions) -> ! { diff --git a/egui_glow/src/lib.rs b/egui_glow/src/lib.rs index ae3f3bd0f9d..466dc47d34c 100644 --- a/egui_glow/src/lib.rs +++ b/egui_glow/src/lib.rs @@ -87,25 +87,33 @@ #![allow(clippy::float_cmp)] #![allow(clippy::manual_range_contains)] -mod painter; +pub mod painter; +pub use glow::Context; pub use painter::Painter; - #[cfg(feature = "epi")] mod epi_backend; -#[cfg(feature = "epi")] -pub use epi_backend::{run, NativeOptions}; +mod misc_util; +mod post_process; +mod shader_version; +mod vao_emulate; +pub mod create_context_for_canvas; + +#[cfg(not(target_arch = "wasm32"))] pub use egui_winit; +#[cfg(all(feature = "epi", not(feature = "painter_only")))] +pub use epi_backend::{run, NativeOptions}; // ---------------------------------------------------------------------------- /// Use [`egui`] from a [`glow`] app. +#[cfg(not(feature = "painter_only"))] pub struct EguiGlow { egui_ctx: egui::CtxRef, egui_winit: egui_winit::State, painter: crate::Painter, } - +#[cfg(not(feature = "painter_only"))] impl EguiGlow { pub fn new( gl_window: &glutin::WindowedContext, @@ -114,7 +122,7 @@ 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), } } @@ -190,12 +198,14 @@ impl EguiGlow { shapes: Vec, ) { 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(), ); } diff --git a/egui_glow/src/misc_util.rs b/egui_glow/src/misc_util.rs new file mode 100644 index 00000000000..8a94be4ebdc --- /dev/null +++ b/egui_glow/src/misc_util.rs @@ -0,0 +1,130 @@ +#![allow(unsafe_code)] +use glow::HasContext; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::JsValue; + +pub(crate) fn srgbtexture2d( + gl: &glow::Context, + compatibility_mode: 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 compatibility_mode { + glow_debug_print(format!("w : {} h : {}", w as i32, h as i32)); + 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(s: &[T]) -> &[u8] { + std::slice::from_raw_parts(s.as_ptr().cast::(), s.len() * std::mem::size_of::()) +} + +#[cfg(target_arch = "wasm32")] +pub(crate) fn glow_debug_print(s: impl Into) { + 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) fn compile_shader( + gl: &glow::Context, + shader_type: u32, + source: &str, +) -> Result { + let shader = unsafe { gl.create_shader(shader_type) }?; + unsafe { + gl.shader_source(shader, source); + } + unsafe { + gl.compile_shader(shader); + } + + if unsafe { gl.get_shader_compile_status(shader) } { + Ok(shader) + } else { + Err(unsafe { gl.get_shader_info_log(shader) }) + } +} + +pub(crate) fn link_program<'a, T: IntoIterator>( + gl: &glow::Context, + shaders: T, +) -> Result { + let program = unsafe { gl.create_program() }?; + unsafe { + for shader in shaders { + gl.attach_shader(program, *shader) + } + } + unsafe { + gl.link_program(program); + } + + if unsafe { gl.get_program_link_status(program) } { + Ok(program) + } else { + Err(unsafe { gl.get_program_info_log(program) }) + } +} diff --git a/egui_glow/src/painter.rs b/egui_glow/src/painter.rs index 7ee250aa511..008b1e146ae 100644 --- a/egui_glow/src/painter.rs +++ b/egui_glow/src/painter.rs @@ -4,246 +4,167 @@ use egui::{ emath::Rect, epaint::{Color32, Mesh, Vertex}, }; +pub use glow::Context; + use memoffset::offset_of; -use std::convert::TryInto; +use glow::HasContext; -use glow::HasContext as _; +use crate::misc_util::{ + as_u8_slice, compile_shader, glow_debug_print, link_program, srgbtexture2d, +}; +use crate::post_process::PostProcess; +use crate::shader_version::ShaderVersion; +use crate::vao_emulate; const VERT_SRC: &str = include_str!("shader/vertex.glsl"); const FRAG_SRC: &str = include_str!("shader/fragment.glsl"); -fn srgbtexture2d(gl: &glow::Context, data: &[u8], w: usize, h: usize) -> glow::NativeTexture { - 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)); - - // The texture coordinates for text are so that both nearest and linear should work with the egui font texture. - // For user textures linear sampling is more likely to be the right choice. - - gl.tex_parameter_i32( - glow::TEXTURE_2D, - glow::TEXTURE_MAG_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, - ); - - 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 - } -} - -unsafe fn as_u8_slice(s: &[T]) -> &[u8] { - std::slice::from_raw_parts(s.as_ptr().cast::(), s.len() * std::mem::size_of::()) -} - /// OpenGL painter /// /// This struct must be destroyed with [`Painter::destroy`] before dropping, to ensure OpenGL /// objects have been properly deleted and are not leaked. pub struct Painter { - program: glow::NativeProgram, + program: glow::Program, u_screen_size: glow::UniformLocation, u_sampler: glow::UniformLocation, - egui_texture: Option, + egui_texture: Option, egui_texture_version: Option, - + is_webgl_1: bool, + vertex_array: vao_emulate::EmulatedVao, + srgb_support: bool, /// `None` means unallocated (freed) slot. - user_textures: Vec>, - - vertex_array: glow::NativeVertexArray, - vertex_buffer: glow::NativeBuffer, - element_array_buffer: glow::NativeBuffer, + pub(crate) user_textures: Vec>, + post_process: Option, + vertex_buffer: glow::Buffer, + element_array_buffer: glow::Buffer, // Stores outdated OpenGL textures that are yet to be deleted - old_textures: Vec, - // Only used in debug builds, to make sure we are destroyed correctly. + old_textures: Vec, + // Only in debug builds, to make sure we are destroyed correctly. + #[cfg(debug_assertions)] destroyed: bool, } #[derive(Default)] -struct UserTexture { +pub(crate) struct UserTexture { /// Pending upload (will be emptied later). /// This is the format glow likes. - data: Vec, - size: (usize, usize), + pub(crate) data: Vec, + pub(crate) size: (usize, usize), /// Lazily uploaded - gl_texture: Option, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[allow(dead_code)] -enum ShaderVersion { - Gl120, - Gl140, - Es100, - Es300, -} - -impl ShaderVersion { - fn get(gl: &glow::Context) -> Self { - Self::parse(unsafe { &gl.get_parameter_string(glow::SHADING_LANGUAGE_VERSION) }) - } - - #[inline] - fn parse(glsl_ver: &str) -> Self { - let start = glsl_ver.find(|c| char::is_ascii_digit(&c)).unwrap(); - let es = glsl_ver[..start].contains(" ES "); - let ver = glsl_ver[start..].splitn(2, ' ').next().unwrap(); - let [maj, min]: [u8; 2] = ver - .splitn(3, '.') - .take(2) - .map(|x| x.parse().unwrap_or_default()) - .collect::>() - .try_into() - .unwrap(); - if es { - if maj >= 3 { - Self::Es300 - } else { - Self::Es100 - } - } else if maj > 1 || (maj == 1 && min >= 40) { - Self::Gl140 - } else { - Self::Gl120 - } - } - - fn version(&self) -> &'static str { - match self { - Self::Gl120 => "#version 120\n", - Self::Gl140 => "#version 140\n", - Self::Es100 => "#version 100\n", - Self::Es300 => "#version 300 es\n", - } - } -} - -#[test] -fn test_shader_version() { - use ShaderVersion::{Es100, Es300, Gl120, Gl140}; - for (s, v) in [ - ("1.2 OpenGL foo bar", Gl120), - ("3.0", Gl140), - ("0.0", Gl120), - ("OpenGL ES GLSL 3.00 (WebGL2)", Es300), - ("OpenGL ES GLSL 1.00 (WebGL)", Es100), - ("OpenGL ES GLSL ES 1.00 foo bar", Es100), - ("WebGL GLSL ES 3.00 foo bar", Es300), - ("WebGL GLSL ES 3.00", Es300), - ("WebGL GLSL ES 1.0 foo bar", Es100), - ] { - assert_eq!(ShaderVersion::parse(s), v); - } + pub(crate) gl_texture: Option, } impl Painter { - pub fn new(gl: &glow::Context) -> Painter { - let header = ShaderVersion::get(gl).version(); - - unsafe { - let vert = gl.create_shader(glow::VERTEX_SHADER).unwrap(); - gl.shader_source(vert, &format!("{}\n{}", header, VERT_SRC)); - gl.compile_shader(vert); - if !gl.get_shader_compile_status(vert) { - panic!( - "Failed to compile vertex shader: {}", - gl.get_shader_info_log(vert) - ); + /// create painter + /// + /// if pp_fb_extent is none post process disabled . + /// when post process disabled sRGB invalid color appeared on OpenGL ES and WebGL . + /// + /// to enable post process set framebuffer dimension to pp_fb_extent. + pub fn new(gl: &glow::Context, pp_fb_extent: Option<[i32; 2]>) -> Painter { + let shader_version = ShaderVersion::get(gl); + let is_webgl_1 = shader_version == ShaderVersion::Es100; + let header = shader_version.version(); + glow_debug_print(header); + let mut v_src = header.to_owned(); + v_src.push_str(VERT_SRC); + let mut f_src = header.to_owned(); + + let srgb_support = gl.supported_extensions().contains("EXT_sRGB"); + let post_process = match (shader_version, srgb_support) { + //WebGL2 support sRGB default + (ShaderVersion::Es300, _) | (ShaderVersion::Es100, true) => { + //Add sRGB support marker for fragment shader + f_src.push_str("#define SRGB_SUPPORTED \n"); + if let Some([width, height]) = pp_fb_extent { + glow_debug_print("WebGL with sRGB enabled so turn on post process"); + //install post process to correct sRGB color + PostProcess::new(gl, is_webgl_1, width, height).ok() + } else { + glow_debug_print("WebGL or OpenGL ES detected but PostProcess disabled because dimension is None"); + None + } } - - let frag = gl.create_shader(glow::FRAGMENT_SHADER).unwrap(); - gl.shader_source(frag, &format!("{}\n{}", header, FRAG_SRC)); - gl.compile_shader(frag); - if !gl.get_shader_compile_status(frag) { - panic!( - "Failed to compile fragment shader: {}", - gl.get_shader_info_log(frag) - ); + //WebGL1 without sRGB support disable postprocess and use fallback shader + (ShaderVersion::Es100, false) => None, + //OpenGL 2.1 or above always support sRGB so add sRGB support marker + _ => { + f_src.push_str("#define SRGB_SUPPORTED \n"); + None } + }; - let program = gl.create_program().unwrap(); - gl.attach_shader(program, vert); - gl.attach_shader(program, frag); - gl.link_program(program); - if !gl.get_program_link_status(program) { - panic!("{}", gl.get_program_info_log(program)); - } - gl.detach_shader(program, vert); - gl.detach_shader(program, frag); - gl.delete_shader(vert); - gl.delete_shader(frag); + f_src.push_str(FRAG_SRC); + unsafe { + let v = compile_shader(gl, glow::VERTEX_SHADER, &v_src) + .map_err(|problems| { + glow_debug_print(format!( + "failed to compile vertex shader due to errors \n {}", + problems + )) + }) + .unwrap(); + let f = compile_shader(gl, glow::FRAGMENT_SHADER, &f_src) + .map_err(|problems| { + glow_debug_print(format!( + "failed to compile fragment shader due to errors \n {}", + problems + )) + }) + .unwrap(); + let program = link_program(gl, [v, f].iter()) + .map_err(|problems| { + glow_debug_print(format!( + "failed to link shaders due to errors \n {}", + problems + )) + }) + .unwrap(); + gl.detach_shader(program, v); + gl.detach_shader(program, f); + gl.delete_shader(v); + gl.delete_shader(f); let u_screen_size = gl.get_uniform_location(program, "u_screen_size").unwrap(); let u_sampler = gl.get_uniform_location(program, "u_sampler").unwrap(); - - let vertex_array = gl.create_vertex_array().unwrap(); let vertex_buffer = gl.create_buffer().unwrap(); let element_array_buffer = gl.create_buffer().unwrap(); - - gl.bind_vertex_array(Some(vertex_array)); gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buffer)); - let a_pos_loc = gl.get_attrib_location(program, "a_pos").unwrap(); let a_tc_loc = gl.get_attrib_location(program, "a_tc").unwrap(); let a_srgba_loc = gl.get_attrib_location(program, "a_srgba").unwrap(); - - gl.vertex_attrib_pointer_f32( - a_pos_loc, - 2, - glow::FLOAT, - false, - std::mem::size_of::() as i32, - offset_of!(Vertex, pos) as i32, - ); - gl.enable_vertex_attrib_array(a_pos_loc); - - gl.vertex_attrib_pointer_f32( - a_tc_loc, - 2, - glow::FLOAT, - false, - std::mem::size_of::() as i32, - offset_of!(Vertex, uv) as i32, - ); - gl.enable_vertex_attrib_array(a_tc_loc); - - gl.vertex_attrib_pointer_f32( - a_srgba_loc, - 4, - glow::UNSIGNED_BYTE, - false, - std::mem::size_of::() as i32, - offset_of!(Vertex, color) as i32, - ); - gl.enable_vertex_attrib_array(a_srgba_loc); + let mut vertex_array = vao_emulate::EmulatedVao::new(vertex_buffer); + let stride = std::mem::size_of::() as i32; + let position_buffer_info = vao_emulate::BufferInfo { + location: a_pos_loc, + vector_size: 2, + data_type: glow::FLOAT, + normalized: false, + stride, + offset: offset_of!(Vertex, pos) as i32, + }; + let tex_coord_buffer_info = vao_emulate::BufferInfo { + location: a_tc_loc, + vector_size: 2, + data_type: glow::FLOAT, + normalized: false, + stride, + offset: offset_of!(Vertex, uv) as i32, + }; + let color_buffer_info = vao_emulate::BufferInfo { + location: a_srgba_loc, + vector_size: 4, + data_type: glow::UNSIGNED_BYTE, + normalized: false, + stride, + offset: offset_of!(Vertex, color) as i32, + }; + vertex_array.add_new_attribute(position_buffer_info); + vertex_array.add_new_attribute(tex_coord_buffer_info); + vertex_array.add_new_attribute(color_buffer_info); assert_eq!(gl.get_error(), glow::NO_ERROR, "OpenGL error occurred!"); Painter { @@ -252,11 +173,15 @@ impl Painter { u_sampler, egui_texture: None, egui_texture_version: None, - user_textures: Default::default(), + is_webgl_1, vertex_array, + srgb_support, + user_textures: Default::default(), + post_process, vertex_buffer, element_array_buffer, old_textures: Vec::new(), + #[cfg(debug_assertions)] destroyed: false, } } @@ -268,16 +193,26 @@ impl Painter { if self.egui_texture_version == Some(texture.version) { return; // No change } - + let gamma = if self.post_process.is_none() { + 1.0 / 2.0 + } else { + 1.0 + }; let pixels: Vec = texture - .pixels - .iter() - .flat_map(|a| Vec::from(Color32::from_white_alpha(*a).to_array())) + .srgba_pixels(gamma) + .flat_map(|a| Vec::from(a.to_array())) .collect(); if let Some(old_tex) = std::mem::replace( &mut self.egui_texture, - Some(srgbtexture2d(gl, &pixels, texture.width, texture.height)), + Some(srgbtexture2d( + gl, + self.is_webgl_1, + self.srgb_support, + &pixels, + texture.width, + texture.height, + )), ) { unsafe { gl.delete_texture(old_tex); @@ -288,12 +223,12 @@ impl Painter { unsafe fn prepare_painting( &mut self, - gl_window: &glutin::WindowedContext, + inner_size: [u32; 2], gl: &glow::Context, pixels_per_point: f32, ) -> (u32, u32) { gl.enable(glow::SCISSOR_TEST); - // egui outputs mesh in both winding orders + // egui outputs mesh in both winding orders: gl.disable(glow::CULL_FACE); gl.enable(glow::BLEND); @@ -308,10 +243,7 @@ impl Painter { glow::ONE, ); - let glutin::dpi::PhysicalSize { - width: width_in_pixels, - height: height_in_pixels, - } = gl_window.window().inner_size(); + let [width_in_pixels, height_in_pixels] = inner_size; let width_in_points = width_in_pixels as f32 / pixels_per_point; let height_in_points = height_in_pixels as f32 / pixels_per_point; @@ -319,12 +251,13 @@ impl Painter { gl.use_program(Some(self.program)); + // The texture coordinates for text are so that both nearest and linear should work with the egui font texture. + // For user textures linear sampling is more likely to be the right choice. gl.uniform_2_f32(Some(&self.u_screen_size), width_in_points, height_in_points); gl.uniform_1_i32(Some(&self.u_sampler), 0); gl.active_texture(glow::TEXTURE0); + self.vertex_array.bind_vertex_array(gl); - gl.bind_vertex_array(Some(self.vertex_array)); - gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vertex_buffer)); gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.element_array_buffer)); (width_in_pixels, height_in_pixels) @@ -341,8 +274,7 @@ impl Painter { /// /// The scissor area and blend parameters will be changed. /// - /// As well as this, the following objects will be rebound: - /// - Vertex Array + /// As well as this, the following objects will be unset: /// - Vertex Buffer /// - Element Buffer /// - Texture (and active texture will be set to 0) @@ -352,27 +284,30 @@ impl Painter { /// of the effects your program might have on this code. Look at the source if in doubt. pub fn paint_meshes( &mut self, - gl_window: &glutin::WindowedContext, + inner_size: [u32; 2], gl: &glow::Context, pixels_per_point: f32, clipped_meshes: Vec, - egui_texture: &egui::Texture, ) { + //chimera of egui_glow and egui_web self.assert_not_destroyed(); - self.upload_egui_texture(gl, egui_texture); self.upload_pending_user_textures(gl); - - let size_in_pixels = unsafe { self.prepare_painting(gl_window, gl, pixels_per_point) }; + if let Some(ref mut post_process) = self.post_process { + post_process.begin(gl, inner_size[0] as i32, inner_size[1] as i32); + } + let size_in_pixels = unsafe { self.prepare_painting(inner_size, gl, pixels_per_point) }; for egui::ClippedMesh(clip_rect, mesh) in clipped_meshes { - self.paint_mesh(gl, size_in_pixels, pixels_per_point, clip_rect, &mesh); + self.paint_mesh(gl, size_in_pixels, pixels_per_point, clip_rect, &mesh) } - - assert_eq!( - unsafe { gl.get_error() }, - glow::NO_ERROR, - "OpenGL error occurred!" - ); + self.vertex_array.unbind_vertex_array(gl); + unsafe { + gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None); + } + if let Some(ref post_process) = self.post_process { + post_process.end(gl); + } + unsafe { assert_eq!(glow::NO_ERROR, gl.get_error(), "GL error occurred!") } } #[inline(never)] // Easier profiling @@ -385,15 +320,16 @@ impl Painter { mesh: &Mesh, ) { debug_assert!(mesh.is_valid()); - if let Some(texture) = self.get_texture(mesh.texture_id) { unsafe { + gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vertex_buffer)); gl.buffer_data_u8_slice( glow::ARRAY_BUFFER, as_u8_slice(mesh.vertices.as_slice()), glow::STREAM_DRAW, ); + gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.element_array_buffer)); gl.buffer_data_u8_slice( glow::ELEMENT_ARRAY_BUFFER, as_u8_slice(mesh.indices.as_slice()), @@ -402,7 +338,6 @@ impl Painter { gl.bind_texture(glow::TEXTURE_2D, Some(texture)); } - // Transform clip rect to physical pixels: let clip_min_x = pixels_per_point * clip_rect.min.x; let clip_min_y = pixels_per_point * clip_rect.min.y; @@ -457,7 +392,7 @@ impl Painter { /// register glow texture as egui texture /// Usable for render to image rectangle - pub fn register_glow_texture(&mut self, texture: glow::NativeTexture) -> egui::TextureId { + pub fn register_glow_texture(&mut self, texture: glow::Texture) -> egui::TextureId { self.assert_not_destroyed(); let id = self.alloc_user_texture(); @@ -488,11 +423,10 @@ impl Painter { pixels: &[Color32], ) { self.assert_not_destroyed(); - assert_eq!( size.0 * size.1, pixels.len(), - "Mismatch between texture size and texel count" + "Mismatch between size and texel count" ); if let egui::TextureId::User(id) = id { @@ -530,7 +464,7 @@ impl Painter { } } - pub fn get_texture(&self, texture_id: egui::TextureId) -> Option { + pub fn get_texture(&self, texture_id: egui::TextureId) -> Option { self.assert_not_destroyed(); match texture_id { @@ -547,6 +481,8 @@ impl Painter { let data = std::mem::take(&mut user_texture.data); user_texture.gl_texture = Some(srgbtexture2d( gl, + self.is_webgl_1, + self.srgb_support, &data, user_texture.size.0, user_texture.size.1, @@ -571,7 +507,6 @@ impl Painter { gl.delete_texture(t); } } - gl.delete_vertex_array(self.vertex_array); gl.delete_buffer(self.vertex_buffer); gl.delete_buffer(self.element_array_buffer); for t in &self.old_textures { @@ -581,29 +516,61 @@ impl Painter { /// This function must be called before Painter is dropped, as Painter has some OpenGL objects /// that should be deleted. + #[cfg(debug_assertions)] pub fn destroy(&mut self, gl: &glow::Context) { - debug_assert!(!self.destroyed, "Only destroy the egui glow painter once!"); - + assert!(!self.destroyed, "Only destroy once!"); unsafe { self.destroy_gl(gl); + if let Some(ref post_process) = self.post_process { + post_process.destroy(gl); + } } - self.destroyed = true; } + #[cfg(not(debug_assertions))] + pub fn destroy(&self, gl: &glow::Context) { + unsafe { + self.destroy_gl(gl); + if let Some(ref post_process) = self.post_process { + post_process.destroy(gl); + } + } + } + + #[cfg(debug_assertions)] fn assert_not_destroyed(&self) { - debug_assert!( - !self.destroyed, - "the egui glow painter has already been destroyed!" - ); + assert!(!self.destroyed, "egui has already been destroyed!"); } + + #[inline(always)] + #[cfg(not(debug_assertions))] + #[allow(clippy::unused_self)] + fn assert_not_destroyed(&self) {} } +// ported from egui_web +pub fn clear(gl: &glow::Context, dimension: [u32; 2], clear_color: egui::Rgba) { + unsafe { + gl.disable(glow::SCISSOR_TEST); + + gl.viewport(0, 0, dimension[0] as i32, dimension[1] as i32); + let clear_color: Color32 = clear_color.into(); + gl.clear_color( + clear_color[0] as f32 / 255.0, + clear_color[1] as f32 / 255.0, + clear_color[2] as f32 / 255.0, + clear_color[3] as f32 / 255.0, + ); + gl.clear(glow::COLOR_BUFFER_BIT); + } +} impl Drop for Painter { fn drop(&mut self) { - debug_assert!( + #[cfg(debug_assertions)] + assert!( self.destroyed, - "Make sure to call destroy() before dropping to avoid leaking OpenGL objects!" + "Make sure to destroy() rather than dropping, to avoid leaking OpenGL objects!" ); } } diff --git a/egui_glow/src/post_process.rs b/egui_glow/src/post_process.rs new file mode 100644 index 00000000000..2ccd5039555 --- /dev/null +++ b/egui_glow/src/post_process.rs @@ -0,0 +1,240 @@ +#![allow(unsafe_code)] +use crate::misc_util::{compile_shader, link_program}; +use crate::vao_emulate::BufferInfo; +use glow::HasContext; + +/// Uses a framebuffer to render everything in linear color space and convert it back to sRGB +/// in a separate "post processing" step +pub(crate) struct PostProcess { + pos_buffer: glow::Buffer, + index_buffer: glow::Buffer, + vertex_array: crate::vao_emulate::EmulatedVao, + is_webgl_1: bool, + texture: glow::Texture, + texture_size: (i32, i32), + fbo: glow::Framebuffer, + program: glow::Program, +} + +impl PostProcess { + pub(crate) fn new( + gl: &glow::Context, + is_webgl_1: bool, + width: i32, + height: i32, + ) -> Result { + let fbo = unsafe { gl.create_framebuffer() }?; + unsafe { + gl.bind_framebuffer(glow::FRAMEBUFFER, Some(fbo)); + } + + let texture = unsafe { gl.create_texture() }.unwrap(); + unsafe { + gl.bind_texture(glow::TEXTURE_2D, Some(texture)); + } + unsafe { + gl.tex_parameter_i32( + glow::TEXTURE_2D, + glow::TEXTURE_WRAP_S, + glow::CLAMP_TO_EDGE as i32, + ); + } + unsafe { + gl.tex_parameter_i32( + glow::TEXTURE_2D, + glow::TEXTURE_WRAP_T, + glow::CLAMP_TO_EDGE as i32, + ); + } + unsafe { + gl.tex_parameter_i32( + glow::TEXTURE_2D, + glow::TEXTURE_MIN_FILTER, + glow::NEAREST as i32, + ); + } + unsafe { + gl.tex_parameter_i32( + glow::TEXTURE_2D, + glow::TEXTURE_MAG_FILTER, + glow::NEAREST as i32, + ); + } + unsafe { + gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1); + } + let (internal_format, format) = if is_webgl_1 { + (glow::SRGB_ALPHA, glow::SRGB_ALPHA) + } else { + (glow::SRGB8_ALPHA8, glow::RGBA) + }; + + unsafe { + gl.tex_image_2d( + glow::TEXTURE_2D, + 0, + internal_format as i32, + width, + height, + 0, + format, + glow::UNSIGNED_BYTE, + None, + ); + let error_code = gl.get_error(); + assert_eq!( + error_code, + glow::NO_ERROR, + "Error occurred in post process texture initialization. code : 0x{:x}", + error_code + ); + } + unsafe { + gl.framebuffer_texture_2d( + glow::FRAMEBUFFER, + glow::COLOR_ATTACHMENT0, + glow::TEXTURE_2D, + Some(texture), + 0, + ); + } + + unsafe { gl.bind_texture(glow::TEXTURE_2D, None) } + unsafe { gl.bind_framebuffer(glow::FRAMEBUFFER, None) } + + let vert_shader = compile_shader( + gl, + glow::VERTEX_SHADER, + include_str!("shader/post_vertex_100es.glsl"), + )?; + let frag_shader = compile_shader( + gl, + glow::FRAGMENT_SHADER, + include_str!("shader/post_fragment_100es.glsl"), + )?; + let program = link_program(gl, [vert_shader, frag_shader].iter())?; + + let positions = vec![0.0f32, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0]; + + let indices = vec![0u8, 1, 2, 1, 2, 3]; + unsafe { + let pos_buffer = gl.create_buffer()?; + gl.bind_buffer(glow::ARRAY_BUFFER, Some(pos_buffer)); + gl.buffer_data_u8_slice( + glow::ARRAY_BUFFER, + crate::misc_util::as_u8_slice(&positions), + glow::STATIC_DRAW, + ); + + let a_pos_loc = gl + .get_attrib_location(program, "a_pos") + .ok_or_else(|| "failed to get location of a_pos".to_string())?; + let mut vertex_array = crate::vao_emulate::EmulatedVao::new(pos_buffer); + let buffer_info_a_pos = BufferInfo { + location: a_pos_loc, + vector_size: 2, + data_type: glow::FLOAT, + normalized: false, + stride: 0, + offset: 0, + }; + vertex_array.add_new_attribute(buffer_info_a_pos); + + gl.bind_buffer(glow::ARRAY_BUFFER, None); + + let index_buffer = gl.create_buffer()?; + gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(index_buffer)); + gl.buffer_data_u8_slice(glow::ELEMENT_ARRAY_BUFFER, &indices, glow::STATIC_DRAW); + + gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None); + let error_code = gl.get_error(); + assert_eq!( + error_code, + glow::NO_ERROR, + "Error occurred in post process initialization. code : 0x{:x}", + error_code + ); + + Ok(PostProcess { + pos_buffer, + index_buffer, + vertex_array, + is_webgl_1, + texture, + texture_size: (width, height), + fbo, + program, + }) + } + } + + pub(crate) fn begin(&mut self, gl: &glow::Context, width: i32, height: i32) { + if (width, height) != self.texture_size { + unsafe { + gl.bind_texture(glow::TEXTURE_2D, Some(self.texture)); + } + unsafe { + gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1); + } + let (internal_format, format) = if self.is_webgl_1 { + (glow::SRGB_ALPHA, glow::SRGB_ALPHA) + } else { + (glow::SRGB8_ALPHA8, glow::RGBA) + }; + unsafe { + gl.tex_image_2d( + glow::TEXTURE_2D, + 0, + internal_format as i32, + width, + height, + 0, + format, + glow::UNSIGNED_BYTE, + None, + ); + } + unsafe { + gl.bind_texture(glow::TEXTURE_2D, None); + } + + self.texture_size = (width, height); + } + unsafe { + gl.bind_framebuffer(glow::FRAMEBUFFER, Some(self.fbo)); + gl.clear_color(0.0, 0.0, 0.0, 0.0); + gl.clear(glow::COLOR_BUFFER_BIT); + } + } + + pub(crate) fn end(&self, gl: &glow::Context) { + unsafe { + gl.bind_framebuffer(glow::FRAMEBUFFER, None); + gl.disable(glow::SCISSOR_TEST); + + gl.use_program(Some(self.program)); + + gl.active_texture(glow::TEXTURE0); + gl.bind_texture(glow::TEXTURE_2D, Some(self.texture)); + let u_sampler_loc = gl.get_uniform_location(self.program, "u_sampler").unwrap(); + gl.uniform_1_i32(Some(&u_sampler_loc), 0); + self.vertex_array.bind_vertex_array(gl); + + gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.index_buffer)); + gl.draw_elements(glow::TRIANGLES, 6, glow::UNSIGNED_BYTE, 0); + self.vertex_array.unbind_vertex_array(gl); + gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None); + gl.bind_texture(glow::TEXTURE_2D, None); + gl.use_program(None); + } + } + pub(crate) fn destroy(&self, gl: &glow::Context) { + unsafe { + gl.delete_buffer(self.pos_buffer); + gl.delete_buffer(self.index_buffer); + gl.delete_program(self.program); + gl.delete_framebuffer(self.fbo); + gl.delete_texture(self.texture); + } + } +} diff --git a/egui_glow/src/shader/fragment.glsl b/egui_glow/src/shader/fragment.glsl index e7c3c95d297..229af5a13ff 100644 --- a/egui_glow/src/shader/fragment.glsl +++ b/egui_glow/src/shader/fragment.glsl @@ -3,71 +3,84 @@ precision mediump float; #endif uniform sampler2D u_sampler; -#if defined(GL_ES) || __VERSION__ < 140 -varying vec4 v_rgba; -varying vec2 v_tc; +#if defined(GL_ES) && __VERSION__ >=300 +#define USE_NEW_SHADER +#elif defined(GL_ES) && __VERSION__ <300 #else +#define USE_NEW_SHADER +#endif + +#ifdef USE_NEW_SHADER in vec4 v_rgba; in vec2 v_tc; out vec4 f_color; +// a dirty hack applied to support webGL2 +#define gl_FragColor f_color +#define texture2D texture +#else +varying vec4 v_rgba; +varying vec2 v_tc; #endif -#ifdef GL_ES +#ifndef SRGB_SUPPORTED // 0-255 sRGB from 0-1 linear vec3 srgb_from_linear(vec3 rgb) { - bvec3 cutoff = lessThan(rgb, vec3(0.0031308)); - vec3 lower = rgb * vec3(3294.6); - vec3 higher = vec3(269.025) * pow(rgb, vec3(1.0 / 2.4)) - vec3(14.025); - return mix(higher, lower, vec3(cutoff)); + bvec3 cutoff = lessThan(rgb, vec3(0.0031308)); + vec3 lower = rgb * vec3(3294.6); + vec3 higher = vec3(269.025) * pow(rgb, vec3(1.0 / 2.4)) - vec3(14.025); + return mix(higher, lower, vec3(cutoff)); } vec4 srgba_from_linear(vec4 rgba) { - return vec4(srgb_from_linear(rgba.rgb), 255.0 * rgba.a); + return vec4(srgb_from_linear(rgba.rgb), 255.0 * rgba.a); } - -#if __VERSION__ < 300 // 0-1 linear from 0-255 sRGB vec3 linear_from_srgb(vec3 srgb) { - bvec3 cutoff = lessThan(srgb, vec3(10.31475)); - vec3 lower = srgb / vec3(3294.6); - vec3 higher = pow((srgb + vec3(14.025)) / vec3(269.025), vec3(2.4)); - return mix(higher, lower, vec3(cutoff)); + bvec3 cutoff = lessThan(srgb, vec3(10.31475)); + vec3 lower = srgb / vec3(3294.6); + vec3 higher = pow((srgb + vec3(14.025)) / vec3(269.025), vec3(2.4)); + return mix(higher, lower, vec3(cutoff)); } vec4 linear_from_srgba(vec4 srgba) { - return vec4(linear_from_srgb(srgba.rgb), srgba.a / 255.0); + return vec4(linear_from_srgb(srgba.rgb), srgba.a / 255.0); } -#endif -#endif -#ifdef GL_ES void main() { -#if __VERSION__ < 300 - // We must decode the colors, since WebGL doesn't come with sRGBA textures: - vec4 texture_rgba = linear_from_srgba(texture2D(u_sampler, v_tc) * 255.0); -#else - // The texture is set up with `SRGB8_ALPHA8`, so no need to decode here! - vec4 texture_rgba = texture2D(u_sampler, v_tc); -#endif + // We must decode the colors, since WebGL doesn't come with sRGBA textures: + vec4 texture_rgba = linear_from_srgba(texture2D(u_sampler, v_tc) * 255.0); + /// Multiply vertex color with texture color (in linear space). + gl_FragColor = v_rgba * texture_rgba; + + /// Multiply vertex color with texture color (in linear space). + gl_FragColor = v_rgba * texture_rgba; - /// Multiply vertex color with texture color (in linear space). - gl_FragColor = v_rgba * texture_rgba; + // WebGL doesn't support linear blending in the framebuffer, + // so we do a hack here where we change the premultiplied alpha + // to do the multiplication in gamma space instead: - // We must gamma-encode again since WebGL doesn't support linear blending in the framebuffer. - gl_FragColor = srgba_from_linear(v_rgba * texture_rgba) / 255.0; + // Unmultiply alpha: + if (gl_FragColor.a > 0.0) { + gl_FragColor.rgb /= gl_FragColor.a; + } - // WebGL doesn't support linear blending in the framebuffer, - // so we apply this hack to at least get a bit closer to the desired blending: - gl_FragColor.a = pow(gl_FragColor.a, 1.6); // Empiric nonsense + // Empiric tweak to make e.g. shadows look more like they should: + gl_FragColor.a *= sqrt(gl_FragColor.a); + + // To gamma: + gl_FragColor = srgba_from_linear(gl_FragColor) / 255.0; + + // Premultiply alpha, this time in gamma space: + if (gl_FragColor.a > 0.0) { + gl_FragColor.rgb *= gl_FragColor.a; + } } -#else + #else void main() { - // The texture sampler is sRGB aware, and OpenGL already expects linear rgba output - // so no need for any sRGB conversions here: -#if __VERSION__ < 140 - gl_FragColor = v_rgba * texture2D(u_sampler, v_tc); -#else - f_color = v_rgba * texture(u_sampler, v_tc); -#endif + // The texture sampler is sRGB aware, and OpenGL already expects linear rgba output + // so no need for any sRGB conversions here: + + gl_FragColor = v_rgba * texture2D(u_sampler, v_tc); + } -#endif + #endif \ No newline at end of file diff --git a/egui_glow/src/shader/post_fragment_100es.glsl b/egui_glow/src/shader/post_fragment_100es.glsl new file mode 100644 index 00000000000..56b6ac59a4f --- /dev/null +++ b/egui_glow/src/shader/post_fragment_100es.glsl @@ -0,0 +1,22 @@ +precision mediump float; +uniform sampler2D u_sampler; +varying vec2 v_tc; + +// 0-255 sRGB from 0-1 linear +vec3 srgb_from_linear(vec3 rgb) { + bvec3 cutoff = lessThan(rgb, vec3(0.0031308)); + vec3 lower = rgb * vec3(3294.6); + vec3 higher = vec3(269.025) * pow(rgb, vec3(1.0 / 2.4)) - vec3(14.025); + return mix(higher, lower, vec3(cutoff)); +} + +// 0-255 sRGBA from 0-1 linear +vec4 srgba_from_linear(vec4 rgba) { + return vec4(srgb_from_linear(rgba.rgb), 255.0 * rgba.a); +} + +void main() { + gl_FragColor = texture2D(u_sampler, v_tc); + + gl_FragColor = srgba_from_linear(gl_FragColor) / 255.; +} \ No newline at end of file diff --git a/egui_glow/src/shader/post_vertex_100es.glsl b/egui_glow/src/shader/post_vertex_100es.glsl new file mode 100644 index 00000000000..b07658173c7 --- /dev/null +++ b/egui_glow/src/shader/post_vertex_100es.glsl @@ -0,0 +1,8 @@ +precision mediump float; +attribute vec2 a_pos; +varying vec2 v_tc; + +void main() { + gl_Position = vec4(a_pos * 2. - 1., 0.0, 1.0); + v_tc = a_pos; +} \ No newline at end of file diff --git a/egui_glow/src/shader/vertex.glsl b/egui_glow/src/shader/vertex.glsl index cf2309a8376..2610f931fd5 100644 --- a/egui_glow/src/shader/vertex.glsl +++ b/egui_glow/src/shader/vertex.glsl @@ -1,4 +1,4 @@ -#if !defined(GL_ES) && __VERSION__ >= 140 +#if (!defined(GL_ES) && __VERSION__ >= 140) || (defined(GL_ES) && __VERSION__ >=300) #define I in #define O out #define V(x) x diff --git a/egui_glow/src/shader_version.rs b/egui_glow/src/shader_version.rs new file mode 100644 index 00000000000..79a06911469 --- /dev/null +++ b/egui_glow/src/shader_version.rs @@ -0,0 +1,73 @@ +#![allow(unsafe_code)] +use crate::misc_util::glow_debug_print; +use glow::HasContext; +use std::convert::TryInto; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[allow(dead_code)] +pub(crate) enum ShaderVersion { + Gl120, + Gl140, + Es100, + Es300, +} + +impl ShaderVersion { + pub(crate) fn get(gl: &glow::Context) -> Self { + let shading_lang = unsafe { gl.get_parameter_string(glow::SHADING_LANGUAGE_VERSION) }; + glow_debug_print(&shading_lang); + Self::parse(&shading_lang) + } + + #[inline] + pub(crate) fn parse(glsl_ver: &str) -> Self { + let start = glsl_ver.find(|c| char::is_ascii_digit(&c)).unwrap(); + let es = glsl_ver[..start].contains(" ES "); + let ver = glsl_ver[start..].splitn(2, ' ').next().unwrap(); + let [maj, min]: [u8; 2] = ver + .splitn(3, '.') + .take(2) + .map(|x| x.parse().unwrap_or_default()) + .collect::>() + .try_into() + .unwrap(); + if es { + if maj >= 3 { + Self::Es300 + } else { + Self::Es100 + } + } else if maj > 1 || (maj == 1 && min >= 40) { + Self::Gl140 + } else { + Self::Gl120 + } + } + + pub(crate) fn version(&self) -> &'static str { + match self { + Self::Gl120 => "#version 120\n", + Self::Gl140 => "#version 140\n", + Self::Es100 => "#version 100\n", + Self::Es300 => "#version 300 es\n", + } + } +} + +#[test] +fn test_shader_version() { + use ShaderVersion::{Es100, Es300, Gl120, Gl140}; + for (s, v) in [ + ("1.2 OpenGL foo bar", Gl120), + ("3.0", Gl140), + ("0.0", Gl120), + ("OpenGL ES GLSL 3.00 (WebGL2)", Es300), + ("OpenGL ES GLSL 1.00 (WebGL)", Es100), + ("OpenGL ES GLSL ES 1.00 foo bar", Es100), + ("WebGL GLSL ES 3.00 foo bar", Es300), + ("WebGL GLSL ES 3.00", Es300), + ("WebGL GLSL ES 1.0 foo bar", Es100), + ] { + assert_eq!(ShaderVersion::parse(s), v); + } +} diff --git a/egui_glow/src/vao_emulate.rs b/egui_glow/src/vao_emulate.rs new file mode 100644 index 00000000000..6629fbfdb0c --- /dev/null +++ b/egui_glow/src/vao_emulate.rs @@ -0,0 +1,58 @@ +#![allow(unsafe_code)] +use glow::HasContext; + +pub(crate) struct BufferInfo { + pub location: u32, // + pub vector_size: i32, + pub data_type: u32, //GL_FLOAT,GL_UNSIGNED_BYTE + pub normalized: bool, + pub stride: i32, + pub offset: i32, +} +pub struct EmulatedVao { + buffer: glow::Buffer, + buffer_infos: Vec, + taught: bool, +} +impl EmulatedVao { + pub(crate) fn new(buffer: glow::Buffer) -> Self { + Self { + buffer, + buffer_infos: vec![], + taught: false, + } + } + pub(crate) fn add_new_attribute(&mut self, buffer_info: BufferInfo) { + self.buffer_infos.push(buffer_info); + } + pub(crate) fn bind_vertex_array(&self, gl: &glow::Context) { + unsafe { + gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.buffer)); + } + for attribute in self.buffer_infos.iter() { + unsafe { + if !self.taught { + gl.vertex_attrib_pointer_f32( + attribute.location, + attribute.vector_size, + attribute.data_type, + attribute.normalized, + attribute.stride, + attribute.offset, + ); + } + gl.enable_vertex_attrib_array(attribute.location) + } + } + } + pub(crate) fn unbind_vertex_array(&self, gl: &glow::Context) { + for attribute in self.buffer_infos.iter() { + unsafe { + gl.disable_vertex_attrib_array(attribute.location); + } + } + unsafe { + gl.bind_buffer(glow::ARRAY_BUFFER, None); + } + } +} diff --git a/egui_web/CHANGELOG.md b/egui_web/CHANGELOG.md index d43b9beb84c..c62ac03b0b1 100644 --- a/egui_web/CHANGELOG.md +++ b/egui_web/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to the `egui_web` integration will be noted in this file. ## Unreleased - +* add feature `use_glow_painter` if enabled painting switched to `egui_glow::painter::Painter`. ## 0.15.0 - 2021-10-24 ### Added diff --git a/egui_web/Cargo.toml b/egui_web/Cargo.toml index 4781372b29f..833eb222b28 100644 --- a/egui_web/Cargo.toml +++ b/egui_web/Cargo.toml @@ -28,6 +28,7 @@ crate-type = ["cdylib", "rlib"] egui = { version = "0.15.0", path = "../egui", default-features = false, features = [ "single_threaded", ] } +egui_glow = { version = "0.15.0",path = "../egui_glow", default-features = false , optional = true } epi = { version = "0.15.0", path = "../epi" } js-sys = "0.3" ron = { version = "0.7", optional = true } @@ -44,7 +45,7 @@ default = ["default_fonts"] default_fonts = ["egui/default_fonts"] persistence = ["egui/persistence", "ron", "serde"] screen_reader = ["tts"] # experimental - +use_glow_painter =["egui_glow/painter_only","egui_glow/epi"] [dependencies.web-sys] version = "0.3.52" features = [ diff --git a/egui_web/src/backend.rs b/egui_web/src/backend.rs index 4c3220d1990..9de9041e87a 100644 --- a/egui_web/src/backend.rs +++ b/egui_web/src/backend.rs @@ -15,14 +15,30 @@ impl WebBackend { pub fn new(canvas_id: &str) -> Result { let ctx = egui::CtxRef::default(); - let painter: Box = + #[cfg(feature = "use_glow_painter")] + let painter: Box = { + let canvas = canvas_element_or_die(canvas_id); + let gl_ctx = + egui_glow::create_context_for_canvas::init_glow_context_from_canvas(&canvas); + let dimension = [canvas.width() as i32, canvas.height() as i32]; + let painter = egui_glow::Painter::new(&gl_ctx, Some(dimension)); + Box::new(crate::glow_wrapping::WrappedGlowPainter { + gl_ctx, + canvas, + canvas_id: canvas_id.to_owned(), + painter, + }) + }; + #[cfg(not(feature = "use_glow_painter"))] + let painter: Box = { if let Ok(webgl2_painter) = webgl2::WebGl2Painter::new(canvas_id) { console_log("Using WebGL2 backend"); Box::new(webgl2_painter) } else { console_log("Falling back to WebGL1 backend"); Box::new(webgl1::WebGlPainter::new(canvas_id)?) - }; + } + }; Ok(Self { egui_ctx: ctx, @@ -223,8 +239,12 @@ impl AppRunner { } fn integration_info(&self) -> epi::IntegrationInfo { + #[cfg(not(feature = "use_glow_painter"))] + const NAME: &'static str = "egui_web"; + #[cfg(feature = "use_glow_painter")] + const NAME: &'static str = "egui_web(painted by glow)"; epi::IntegrationInfo { - name: "egui_web", + name: NAME, web_info: Some(epi::WebInfo { web_location_hash: location_hash().unwrap_or_default(), }), diff --git a/egui_web/src/glow_wrapping.rs b/egui_web/src/glow_wrapping.rs new file mode 100644 index 00000000000..92de8d95676 --- /dev/null +++ b/egui_web/src/glow_wrapping.rs @@ -0,0 +1,53 @@ +use egui::{ClippedMesh, Rgba, Texture}; +use epi::TextureAllocator; +use wasm_bindgen::JsValue; +use web_sys::HtmlCanvasElement; + +pub(crate) struct WrappedGlowPainter { + pub(crate) gl_ctx: egui_glow::Context, + pub(crate) canvas: HtmlCanvasElement, + pub(crate) canvas_id: String, + pub(crate) painter: egui_glow::Painter, +} + +impl crate::Painter for WrappedGlowPainter { + fn as_tex_allocator(&mut self) -> &mut dyn TextureAllocator { + &mut self.painter + } + + fn debug_info(&self) -> String { + format!( + "Stored canvas size: {} x {}", + self.canvas.width(), + self.canvas.height(), + ) + } + + fn canvas_id(&self) -> &str { + &self.canvas_id + } + + fn upload_egui_texture(&mut self, texture: &Texture) { + self.painter.upload_egui_texture(&self.gl_ctx, texture) + } + + fn clear(&mut self, clear_color: Rgba) { + let canvas_dimension = [self.canvas.width(), self.canvas.height()]; + egui_glow::painter::clear(&self.gl_ctx, canvas_dimension, clear_color) + } + + fn paint_meshes( + &mut self, + clipped_meshes: Vec, + pixels_per_point: f32, + ) -> Result<(), JsValue> { + let canvas_dimension = [self.canvas.width(), self.canvas.height()]; + self.painter.paint_meshes( + canvas_dimension, + &self.gl_ctx, + pixels_per_point, + clipped_meshes, + ); + Ok(()) + } +} diff --git a/egui_web/src/lib.rs b/egui_web/src/lib.rs index 8325de61a60..23ed78bf479 100644 --- a/egui_web/src/lib.rs +++ b/egui_web/src/lib.rs @@ -15,6 +15,8 @@ #![warn(clippy::all, missing_crate_level_docs, rust_2018_idioms)] pub mod backend; +#[cfg(feature = "use_glow_painter")] +mod glow_wrapping; mod painter; pub mod screen_reader; pub mod webgl1; @@ -26,6 +28,8 @@ use egui::mutex::Mutex; pub use wasm_bindgen; pub use web_sys; +#[cfg(feature = "use_glow_painter")] +use egui_glow; pub use painter::Painter; use std::cell::Cell; use std::rc::Rc; From 33648f71a387c94900569eaaaeb687ca34d732a3 Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Tue, 2 Nov 2021 07:11:09 +0900 Subject: [PATCH 02/27] some clippy fix --- egui_glow/src/misc_util.rs | 4 ++-- egui_glow/src/painter.rs | 18 ++++++++++-------- egui_glow/src/post_process.rs | 10 +++++++--- egui_glow/src/vao_emulate.rs | 2 +- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/egui_glow/src/misc_util.rs b/egui_glow/src/misc_util.rs index 8a94be4ebdc..fd83f08cab0 100644 --- a/egui_glow/src/misc_util.rs +++ b/egui_glow/src/misc_util.rs @@ -85,7 +85,7 @@ pub(crate) fn glow_debug_print(s: impl Into) { } #[cfg(not(target_arch = "wasm32"))] pub(crate) fn glow_debug_print(s: impl std::fmt::Display) { - println!("{}", s) + println!("{}", s); } pub(crate) fn compile_shader( @@ -115,7 +115,7 @@ pub(crate) fn link_program<'a, T: IntoIterator>( let program = unsafe { gl.create_program() }?; unsafe { for shader in shaders { - gl.attach_shader(program, *shader) + gl.attach_shader(program, *shader); } } unsafe { diff --git a/egui_glow/src/painter.rs b/egui_glow/src/painter.rs index 008b1e146ae..d0c80535fae 100644 --- a/egui_glow/src/painter.rs +++ b/egui_glow/src/painter.rs @@ -60,10 +60,10 @@ pub(crate) struct UserTexture { impl Painter { /// create painter /// - /// if pp_fb_extent is none post process disabled . - /// when post process disabled sRGB invalid color appeared on OpenGL ES and WebGL . + /// if `pp_fb_extent` is none post process disabled . + /// when post process disabled `sRGB` invalid color appeared on OpenGL ES and WebGL . /// - /// to enable post process set framebuffer dimension to pp_fb_extent. + /// to enable post process set framebuffer dimension to `pp_fb_extent`. pub fn new(gl: &glow::Context, pp_fb_extent: Option<[i32; 2]>) -> Painter { let shader_version = ShaderVersion::get(gl); let is_webgl_1 = shader_version == ShaderVersion::Es100; @@ -105,7 +105,7 @@ impl Painter { glow_debug_print(format!( "failed to compile vertex shader due to errors \n {}", problems - )) + )); }) .unwrap(); let f = compile_shader(gl, glow::FRAGMENT_SHADER, &f_src) @@ -113,7 +113,7 @@ impl Painter { glow_debug_print(format!( "failed to compile fragment shader due to errors \n {}", problems - )) + )); }) .unwrap(); let program = link_program(gl, [v, f].iter()) @@ -121,7 +121,7 @@ impl Painter { glow_debug_print(format!( "failed to link shaders due to errors \n {}", problems - )) + )); }) .unwrap(); gl.detach_shader(program, v); @@ -298,7 +298,7 @@ impl Painter { } let size_in_pixels = unsafe { self.prepare_painting(inner_size, gl, pixels_per_point) }; for egui::ClippedMesh(clip_rect, mesh) in clipped_meshes { - self.paint_mesh(gl, size_in_pixels, pixels_per_point, clip_rect, &mesh) + self.paint_mesh(gl, size_in_pixels, pixels_per_point, clip_rect, &mesh); } self.vertex_array.unbind_vertex_array(gl); unsafe { @@ -307,7 +307,9 @@ impl Painter { if let Some(ref post_process) = self.post_process { post_process.end(gl); } - unsafe { assert_eq!(glow::NO_ERROR, gl.get_error(), "GL error occurred!") } + unsafe { + assert_eq!(glow::NO_ERROR, gl.get_error(), "GL error occurred!"); + } } #[inline(never)] // Easier profiling diff --git a/egui_glow/src/post_process.rs b/egui_glow/src/post_process.rs index 2ccd5039555..56098dbda5e 100644 --- a/egui_glow/src/post_process.rs +++ b/egui_glow/src/post_process.rs @@ -3,7 +3,7 @@ use crate::misc_util::{compile_shader, link_program}; use crate::vao_emulate::BufferInfo; use glow::HasContext; -/// Uses a framebuffer to render everything in linear color space and convert it back to sRGB +/// Uses a framebuffer to render everything in linear color space and convert it back to `sRGB` /// in a separate "post processing" step pub(crate) struct PostProcess { pos_buffer: glow::Buffer, @@ -99,8 +99,12 @@ impl PostProcess { ); } - unsafe { gl.bind_texture(glow::TEXTURE_2D, None) } - unsafe { gl.bind_framebuffer(glow::FRAMEBUFFER, None) } + unsafe { + gl.bind_texture(glow::TEXTURE_2D, None); + } + unsafe { + gl.bind_framebuffer(glow::FRAMEBUFFER, None); + } let vert_shader = compile_shader( gl, diff --git a/egui_glow/src/vao_emulate.rs b/egui_glow/src/vao_emulate.rs index 6629fbfdb0c..054a81b46f8 100644 --- a/egui_glow/src/vao_emulate.rs +++ b/egui_glow/src/vao_emulate.rs @@ -41,7 +41,7 @@ impl EmulatedVao { attribute.offset, ); } - gl.enable_vertex_attrib_array(attribute.location) + gl.enable_vertex_attrib_array(attribute.location); } } } From eaf58a058d3ab9119d137008d3ed12204401eaa1 Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Tue, 2 Nov 2021 12:38:27 +0900 Subject: [PATCH 03/27] some clippy workaround --- egui_glow/src/create_context_for_canvas.rs | 2 +- egui_glow/src/painter.rs | 1 + egui_glow/src/vao_emulate.rs | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/egui_glow/src/create_context_for_canvas.rs b/egui_glow/src/create_context_for_canvas.rs index 78a8cc2052e..f37b8e4b0c4 100644 --- a/egui_glow/src/create_context_for_canvas.rs +++ b/egui_glow/src/create_context_for_canvas.rs @@ -38,5 +38,5 @@ pub fn init_glow_context_from_canvas(canvas: &HtmlCanvasElement) -> glow::Contex /// dummy for clippy #[cfg(not(target_arch = "wasm32"))] pub fn init_glow_context_from_canvas(_: T) -> glow::Context { - unimplemented!("this is only enabled wasm target") + panic!("this is only enabled wasm target") } diff --git a/egui_glow/src/painter.rs b/egui_glow/src/painter.rs index d0c80535fae..1867dd8486f 100644 --- a/egui_glow/src/painter.rs +++ b/egui_glow/src/painter.rs @@ -394,6 +394,7 @@ impl Painter { /// register glow texture as egui texture /// Usable for render to image rectangle + #[allow(clippy::needless_pass_by_value)] pub fn register_glow_texture(&mut self, texture: glow::Texture) -> egui::TextureId { self.assert_not_destroyed(); diff --git a/egui_glow/src/vao_emulate.rs b/egui_glow/src/vao_emulate.rs index 054a81b46f8..339ffd5b4f2 100644 --- a/egui_glow/src/vao_emulate.rs +++ b/egui_glow/src/vao_emulate.rs @@ -15,6 +15,7 @@ pub struct EmulatedVao { taught: bool, } impl EmulatedVao { + #[allow(clippy::needless_pass_by_value)] pub(crate) fn new(buffer: glow::Buffer) -> Self { Self { buffer, From 4eb841eeeb198f92233dd89a206c5a70b6f658db Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Tue, 2 Nov 2021 12:45:04 +0900 Subject: [PATCH 04/27] clippy fix for web --- egui_web/src/backend.rs | 4 ++-- egui_web/src/lib.rs | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/egui_web/src/backend.rs b/egui_web/src/backend.rs index 9de9041e87a..4fd28acfbcf 100644 --- a/egui_web/src/backend.rs +++ b/egui_web/src/backend.rs @@ -240,9 +240,9 @@ impl AppRunner { fn integration_info(&self) -> epi::IntegrationInfo { #[cfg(not(feature = "use_glow_painter"))] - const NAME: &'static str = "egui_web"; + const NAME: &str = "egui_web"; #[cfg(feature = "use_glow_painter")] - const NAME: &'static str = "egui_web(painted by glow)"; + const NAME: &str = "egui_web(painted by glow)"; epi::IntegrationInfo { name: NAME, web_info: Some(epi::WebInfo { diff --git a/egui_web/src/lib.rs b/egui_web/src/lib.rs index 23ed78bf479..c430a43ce7e 100644 --- a/egui_web/src/lib.rs +++ b/egui_web/src/lib.rs @@ -28,8 +28,6 @@ use egui::mutex::Mutex; pub use wasm_bindgen; pub use web_sys; -#[cfg(feature = "use_glow_painter")] -use egui_glow; pub use painter::Painter; use std::cell::Cell; use std::rc::Rc; From d71656e8ef0b496af892bcf9812c38b04dd25502 Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Tue, 2 Nov 2021 12:57:58 +0900 Subject: [PATCH 05/27] add workaround for painter_only --- egui_glow/examples/pure_glow.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/egui_glow/examples/pure_glow.rs b/egui_glow/examples/pure_glow.rs index 9ca2c6bed4e..2fd71e291c2 100644 --- a/egui_glow/examples/pure_glow.rs +++ b/egui_glow/examples/pure_glow.rs @@ -1,5 +1,5 @@ //! Example how to use pure `egui_glow` without [`epi`]. - +#[cfg(not(feature = "painter_only"))] fn create_display( event_loop: &glutin::event_loop::EventLoop<()>, ) -> ( @@ -35,7 +35,7 @@ fn create_display( (gl_window, gl) } - +#[cfg(not(feature = "painter_only"))] fn main() { let event_loop = glutin::event_loop::EventLoop::with_user_event(); let (gl_window, gl) = create_display(&event_loop); @@ -112,3 +112,8 @@ fn main() { } }); } +//dummy +#[cfg(feature = "painter_only")] +fn main() { + println!("painter_only feature enabled so we can't create window"); +} From e3b4120d792bce25a36b281b8e0daa0a28327d1b Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Tue, 2 Nov 2021 13:50:48 +0900 Subject: [PATCH 06/27] fix compatibility problem in fragment shader GLSL __VERSION__ <140 --- egui_glow/src/misc_util.rs | 1 - egui_glow/src/painter.rs | 4 +++- egui_glow/src/shader/fragment.glsl | 8 +------- egui_glow/src/shader/vertex.glsl | 2 +- egui_glow/src/shader_version.rs | 6 ++++++ 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/egui_glow/src/misc_util.rs b/egui_glow/src/misc_util.rs index fd83f08cab0..2f5e67a1e37 100644 --- a/egui_glow/src/misc_util.rs +++ b/egui_glow/src/misc_util.rs @@ -39,7 +39,6 @@ pub(crate) fn srgbtexture2d( glow::CLAMP_TO_EDGE as i32, ); if compatibility_mode { - glow_debug_print(format!("w : {} h : {}", w as i32, h as i32)); let format = if srgb_support { glow::SRGB_ALPHA } else { diff --git a/egui_glow/src/painter.rs b/egui_glow/src/painter.rs index 1867dd8486f..3a865ce5ee9 100644 --- a/egui_glow/src/painter.rs +++ b/egui_glow/src/painter.rs @@ -70,9 +70,11 @@ impl Painter { let header = shader_version.version(); glow_debug_print(header); let mut v_src = header.to_owned(); + v_src.push_str(shader_version.is_new_shader_interface()); v_src.push_str(VERT_SRC); - let mut f_src = header.to_owned(); + let mut f_src = header.to_owned(); + f_src.push_str(shader_version.is_new_shader_interface()); let srgb_support = gl.supported_extensions().contains("EXT_sRGB"); let post_process = match (shader_version, srgb_support) { //WebGL2 support sRGB default diff --git a/egui_glow/src/shader/fragment.glsl b/egui_glow/src/shader/fragment.glsl index 229af5a13ff..cc0e7c056a4 100644 --- a/egui_glow/src/shader/fragment.glsl +++ b/egui_glow/src/shader/fragment.glsl @@ -3,14 +3,8 @@ precision mediump float; #endif uniform sampler2D u_sampler; -#if defined(GL_ES) && __VERSION__ >=300 -#define USE_NEW_SHADER -#elif defined(GL_ES) && __VERSION__ <300 -#else -#define USE_NEW_SHADER -#endif -#ifdef USE_NEW_SHADER +#ifdef NEW_SHADER_INTERFACE in vec4 v_rgba; in vec2 v_tc; out vec4 f_color; diff --git a/egui_glow/src/shader/vertex.glsl b/egui_glow/src/shader/vertex.glsl index 2610f931fd5..15ea4c26697 100644 --- a/egui_glow/src/shader/vertex.glsl +++ b/egui_glow/src/shader/vertex.glsl @@ -1,4 +1,4 @@ -#if (!defined(GL_ES) && __VERSION__ >= 140) || (defined(GL_ES) && __VERSION__ >=300) +#ifdef NEW_SHADER_INTERFACE #define I in #define O out #define V(x) x diff --git a/egui_glow/src/shader_version.rs b/egui_glow/src/shader_version.rs index 79a06911469..4e89d54330e 100644 --- a/egui_glow/src/shader_version.rs +++ b/egui_glow/src/shader_version.rs @@ -52,6 +52,12 @@ impl ShaderVersion { Self::Es300 => "#version 300 es\n", } } + pub(crate) fn is_new_shader_interface(&self) -> &'static str { + match self { + ShaderVersion::Es300 | ShaderVersion::Gl140 => "#define NEW_SHADER_INTERFACE\n", + _ => "", + } + } } #[test] From 188776862e18a48ad6c536c6d36bf8d76ab7778e Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Tue, 2 Nov 2021 15:38:58 +0900 Subject: [PATCH 07/27] remove unneeded `gl_FragColor = v_rgba * texture_rgba;` --- egui_glow/src/shader/fragment.glsl | 3 --- 1 file changed, 3 deletions(-) diff --git a/egui_glow/src/shader/fragment.glsl b/egui_glow/src/shader/fragment.glsl index cc0e7c056a4..0fff62ccd36 100644 --- a/egui_glow/src/shader/fragment.glsl +++ b/egui_glow/src/shader/fragment.glsl @@ -46,9 +46,6 @@ void main() { /// Multiply vertex color with texture color (in linear space). gl_FragColor = v_rgba * texture_rgba; - /// Multiply vertex color with texture color (in linear space). - gl_FragColor = v_rgba * texture_rgba; - // WebGL doesn't support linear blending in the framebuffer, // so we do a hack here where we change the premultiplied alpha // to do the multiplication in gamma space instead: From 901cfa0c52c3074c464292efe9c41e16ca13d0e3 Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 07:27:19 +0900 Subject: [PATCH 08/27] almost fixed issue --- eframe/src/lib.rs | 1 + egui_glow/Cargo.toml | 8 +- egui_glow/examples/pure_glow.rs | 8 +- egui_glow/src/create_context_for_canvas.rs | 42 --- egui_glow/src/epi_backend.rs | 53 +-- egui_glow/src/lib.rs | 13 +- egui_glow/src/misc_util.rs | 57 ++- egui_glow/src/painter.rs | 178 +++++----- egui_glow/src/post_process.rs | 331 ++++++++---------- egui_glow/src/shader/fragment.glsl | 2 +- egui_glow/src/shader/post_fragment_100es.glsl | 2 +- egui_glow/src/shader/post_vertex_100es.glsl | 2 +- egui_glow/src/vao_emulate.rs | 32 +- egui_web/Cargo.toml | 4 +- egui_web/src/backend.rs | 21 +- egui_web/src/glow_wrapping.rs | 53 ++- egui_web/src/painter.rs | 2 + egui_web/src/webgl1.rs | 4 + egui_web/src/webgl2.rs | 4 + 19 files changed, 411 insertions(+), 406 deletions(-) delete mode 100644 egui_glow/src/create_context_for_canvas.rs diff --git a/eframe/src/lib.rs b/eframe/src/lib.rs index dd9036db38f..1586f5d4704 100644 --- a/eframe/src/lib.rs +++ b/eframe/src/lib.rs @@ -97,6 +97,7 @@ pub use egui_web::wasm_bindgen; /// #[cfg(target_arch = "wasm32")] /// #[wasm_bindgen] /// pub fn start(canvas_id: &str) -> Result<(), eframe::wasm_bindgen::JsValue> { +/// std::panic::set_hook(console_error_panic_hook::hook); /// let app = MyEguiApp::default(); /// eframe::start_web(canvas_id, Box::new(app)) /// } diff --git a/egui_glow/Cargo.toml b/egui_glow/Cargo.toml index c874c2bbf23..014986fd539 100644 --- a/egui_glow/Cargo.toml +++ b/egui_glow/Cargo.toml @@ -29,8 +29,8 @@ glow = "0.11" memoffset = "0.6" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -egui-winit = { version = "0.15.0", path = "../egui-winit", default-features = false, features = ["epi"] } -glutin = "0.27" +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] @@ -41,8 +41,8 @@ wasm-bindgen = { version = "0.2" } image = { version = "0.23", default-features = false, features = ["png"] } [features] -default = ["clipboard", "default_fonts", "links", "persistence"] -painter_only = [] +default = ["clipboard", "default_fonts", "links", "persistence", "winit"] +winit= ["egui-winit","glutin"] # enable cut/copy/paste to OS clipboard. # if disabled a clipboard will be simulated so you can still copy/paste within the egui app. clipboard = ["egui-winit/clipboard"] diff --git a/egui_glow/examples/pure_glow.rs b/egui_glow/examples/pure_glow.rs index 2fd71e291c2..ff139807dd3 100644 --- a/egui_glow/examples/pure_glow.rs +++ b/egui_glow/examples/pure_glow.rs @@ -1,5 +1,5 @@ //! Example how to use pure `egui_glow` without [`epi`]. -#[cfg(not(feature = "painter_only"))] + fn create_display( event_loop: &glutin::event_loop::EventLoop<()>, ) -> ( @@ -35,7 +35,6 @@ fn create_display( (gl_window, gl) } -#[cfg(not(feature = "painter_only"))] fn main() { let event_loop = glutin::event_loop::EventLoop::with_user_event(); let (gl_window, gl) = create_display(&event_loop); @@ -112,8 +111,3 @@ fn main() { } }); } -//dummy -#[cfg(feature = "painter_only")] -fn main() { - println!("painter_only feature enabled so we can't create window"); -} diff --git a/egui_glow/src/create_context_for_canvas.rs b/egui_glow/src/create_context_for_canvas.rs deleted file mode 100644 index f37b8e4b0c4..00000000000 --- a/egui_glow/src/create_context_for_canvas.rs +++ /dev/null @@ -1,42 +0,0 @@ -#[cfg(target_arch = "wasm32")] -use web_sys::HtmlCanvasElement; - -/// Create glow context from given canvas. -/// Automatically choose webgl or webgl2 context. -/// first try webgl2 falling back to webgl1 -#[cfg(target_arch = "wasm32")] -pub fn init_glow_context_from_canvas(canvas: &HtmlCanvasElement) -> glow::Context { - use crate::misc_util::glow_debug_print; - use wasm_bindgen::JsCast; - let ctx = canvas.get_context("webgl2"); - if let Ok(ctx) = ctx { - glow_debug_print("webgl found"); - if let Some(ctx) = ctx { - glow_debug_print("webgl 2 selected"); - let gl_ctx = ctx.dyn_into::().unwrap(); - glow::Context::from_webgl2_context(gl_ctx) - } else { - let ctx = canvas.get_context("webgl"); - if let Ok(ctx) = ctx { - glow_debug_print("falling back to webgl1"); - if let Some(ctx) = ctx { - glow_debug_print("webgl selected"); - let gl_ctx = ctx.dyn_into::().unwrap(); - glow_debug_print("success"); - glow::Context::from_webgl1_context(gl_ctx) - } else { - panic!("tried webgl1 but can't get context"); - } - } else { - panic!("tried webgl1 but can't get context"); - } - } - } else { - panic!("tried webgl2 but something went wrong"); - } -} -/// dummy for clippy -#[cfg(not(target_arch = "wasm32"))] -pub fn init_glow_context_from_canvas(_: T) -> glow::Context { - panic!("this is only enabled wasm target") -} diff --git a/egui_glow/src/epi_backend.rs b/egui_glow/src/epi_backend.rs index 74a419e65fe..c6e40615cbd 100644 --- a/egui_glow/src/epi_backend.rs +++ b/egui_glow/src/epi_backend.rs @@ -1,58 +1,19 @@ -use crate::painter::UserTexture; use crate::*; -use egui::Color32; -use egui::TextureId; #[cfg(target_os = "windows")] use glutin::platform::windows::WindowBuilderExtWindows; -#[cfg(not(feature = "painter_only"))] -use std::time::Instant; - -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); - } -} - -impl epi::NativeTexture for Painter { - type Texture = glow::Texture; - fn register_native_texture(&mut self, native: Self::Texture) -> TextureId { - self.register_glow_texture(native) - } +use std::time::Instant; - fn replace_native_texture(&mut self, id: TextureId, replacing: Self::Texture) { - if let egui::TextureId::User(id) = id { - if let Some(Some(user_texture)) = self.user_textures.get_mut(id as usize) { - *user_texture = UserTexture { - data: vec![], - gl_texture: Some(replacing), - size: (0, 0), - }; - } - } - } -} -#[cfg(not(feature = "painter_only"))] struct RequestRepaintEvent; -#[cfg(not(feature = "painter_only"))] + struct GlowRepaintSignal(std::sync::Mutex>); -#[cfg(not(feature = "painter_only"))] + impl epi::RepaintSignal for GlowRepaintSignal { fn request_repaint(&self) { self.0.lock().unwrap().send_event(RequestRepaintEvent).ok(); } } -#[cfg(not(feature = "painter_only"))] + #[allow(unsafe_code)] fn create_display( window_builder: glutin::window::WindowBuilder, @@ -82,7 +43,7 @@ fn create_display( (gl_window, gl) } -#[cfg(not(feature = "painter_only"))] + fn integration_info( window: &glutin::window::Window, previous_frame_time: Option, @@ -97,9 +58,9 @@ fn integration_info( } // ---------------------------------------------------------------------------- -#[cfg(not(feature = "painter_only"))] + pub use epi::NativeOptions; -#[cfg(not(feature = "painter_only"))] + /// Run an egui app #[allow(unsafe_code)] pub fn run(mut app: Box, native_options: &epi::NativeOptions) -> ! { diff --git a/egui_glow/src/lib.rs b/egui_glow/src/lib.rs index 466dc47d34c..6177441710b 100644 --- a/egui_glow/src/lib.rs +++ b/egui_glow/src/lib.rs @@ -88,32 +88,31 @@ #![allow(clippy::manual_range_contains)] pub mod painter; -pub use glow::Context; +pub use glow; pub use painter::Painter; -#[cfg(feature = "epi")] +#[cfg(feature = "winit")] mod epi_backend; mod misc_util; mod post_process; mod shader_version; mod vao_emulate; -pub mod create_context_for_canvas; - #[cfg(not(target_arch = "wasm32"))] pub use egui_winit; -#[cfg(all(feature = "epi", not(feature = "painter_only")))] +#[cfg(all(feature = "epi", feature = "winit"))] pub use epi_backend::{run, NativeOptions}; // ---------------------------------------------------------------------------- /// Use [`egui`] from a [`glow`] app. -#[cfg(not(feature = "painter_only"))] +#[cfg(feature = "winit")] pub struct EguiGlow { egui_ctx: egui::CtxRef, egui_winit: egui_winit::State, painter: crate::Painter, } -#[cfg(not(feature = "painter_only"))] + +#[cfg(feature = "winit")] impl EguiGlow { pub fn new( gl_window: &glutin::WindowedContext, diff --git a/egui_glow/src/misc_util.rs b/egui_glow/src/misc_util.rs index 2f5e67a1e37..254fee1e77b 100644 --- a/egui_glow/src/misc_util.rs +++ b/egui_glow/src/misc_util.rs @@ -5,7 +5,7 @@ use wasm_bindgen::JsValue; pub(crate) fn srgbtexture2d( gl: &glow::Context, - compatibility_mode: bool, + is_webgl_1: bool, srgb_support: bool, data: &[u8], w: usize, @@ -38,7 +38,7 @@ pub(crate) fn srgbtexture2d( glow::TEXTURE_WRAP_T, glow::CLAMP_TO_EDGE as i32, ); - if compatibility_mode { + if is_webgl_1 { let format = if srgb_support { glow::SRGB_ALPHA } else { @@ -127,3 +127,56 @@ pub(crate) fn link_program<'a, T: IntoIterator>( Err(unsafe { 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 new(gl: &glow::Context, is_native_vao: bool) -> Self { + if is_native_vao { + Self::Native(gl.create_vertex_array().unwrap()) + } else { + 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) fn bind_buffer(&mut self, gl: &glow::Context, buffer: glow::Buffer) { + match self { + VAO::Emulated(vao) => vao.bind_buffer(buffer), + VAO::Native(_) => unsafe { gl.bind_buffer(glow::ARRAY_BUFFER, Some(buffer)) }, + } + } + pub(crate) 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(_) => unsafe { + 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) fn unbind_vertex_array(&self, gl: &glow::Context) { + match self { + VAO::Emulated(vao) => vao.unbind_vertex_array(gl), + VAO::Native(_) => unsafe { gl.bind_vertex_array(None) }, + } + } +} diff --git a/egui_glow/src/painter.rs b/egui_glow/src/painter.rs index 3a865ce5ee9..ca41acc6f70 100644 --- a/egui_glow/src/painter.rs +++ b/egui_glow/src/painter.rs @@ -3,6 +3,7 @@ use egui::{ emath::Rect, epaint::{Color32, Mesh, Vertex}, + TextureId, }; pub use glow::Context; @@ -31,7 +32,7 @@ pub struct Painter { egui_texture: Option, egui_texture_version: Option, is_webgl_1: bool, - vertex_array: vao_emulate::EmulatedVao, + vertex_array: crate::misc_util::VAO, srgb_support: bool, /// `None` means unallocated (freed) slot. pub(crate) user_textures: Vec>, @@ -41,8 +42,6 @@ pub struct Painter { // Stores outdated OpenGL textures that are yet to be deleted old_textures: Vec, - // Only in debug builds, to make sure we are destroyed correctly. - #[cfg(debug_assertions)] destroyed: bool, } @@ -69,67 +68,68 @@ impl Painter { let is_webgl_1 = shader_version == ShaderVersion::Es100; let header = shader_version.version(); glow_debug_print(header); - let mut v_src = header.to_owned(); - v_src.push_str(shader_version.is_new_shader_interface()); - v_src.push_str(VERT_SRC); - - let mut f_src = header.to_owned(); - f_src.push_str(shader_version.is_new_shader_interface()); let srgb_support = gl.supported_extensions().contains("EXT_sRGB"); - let post_process = match (shader_version, srgb_support) { + let (post_process, srgb_support_define) = match (shader_version, srgb_support) { //WebGL2 support sRGB default (ShaderVersion::Es300, _) | (ShaderVersion::Es100, true) => { //Add sRGB support marker for fragment shader - f_src.push_str("#define SRGB_SUPPORTED \n"); if let Some([width, height]) = pp_fb_extent { glow_debug_print("WebGL with sRGB enabled so turn on post process"); //install post process to correct sRGB color - PostProcess::new(gl, is_webgl_1, width, height).ok() + ( + unsafe { PostProcess::new(gl, is_webgl_1, width, height) }.ok(), + "#define SRGB_SUPPORTED", + ) } else { glow_debug_print("WebGL or OpenGL ES detected but PostProcess disabled because dimension is None"); - None + (None, "") } } //WebGL1 without sRGB support disable postprocess and use fallback shader - (ShaderVersion::Es100, false) => None, + (ShaderVersion::Es100, false) => (None, ""), //OpenGL 2.1 or above always support sRGB so add sRGB support marker - _ => { - f_src.push_str("#define SRGB_SUPPORTED \n"); - None - } + _ => (None, "#define SRGB_SUPPORTED"), }; - f_src.push_str(FRAG_SRC); - unsafe { - let v = compile_shader(gl, glow::VERTEX_SHADER, &v_src) - .map_err(|problems| { - glow_debug_print(format!( - "failed to compile vertex shader due to errors \n {}", - problems - )); - }) - .unwrap(); - let f = compile_shader(gl, glow::FRAGMENT_SHADER, &f_src) - .map_err(|problems| { - glow_debug_print(format!( - "failed to compile fragment shader due to errors \n {}", - problems - )); - }) - .unwrap(); - let program = link_program(gl, [v, f].iter()) + let vert = compile_shader( + gl, + glow::VERTEX_SHADER, + &format!( + "{}\n{}\n{}", + header, + shader_version.is_new_shader_interface(), + VERT_SRC + ), + ) + .map_err(|problems| { + glow_debug_print(format!("failed to compile vertex shader \n {}", problems)); + }) + .unwrap(); + let frag = compile_shader( + gl, + glow::FRAGMENT_SHADER, + &format!( + "{}\n{}\n{}\n{}", + header, + srgb_support_define, + shader_version.is_new_shader_interface(), + FRAG_SRC + ), + ) + .map_err(|problems| { + glow_debug_print(format!("failed to compile fragment shader \n {}", problems)); + }) + .unwrap(); + let program = link_program(gl, [vert, frag].iter()) .map_err(|problems| { - glow_debug_print(format!( - "failed to link shaders due to errors \n {}", - problems - )); + glow_debug_print(format!("failed to link shaders \n {}", problems)); }) .unwrap(); - gl.detach_shader(program, v); - gl.detach_shader(program, f); - gl.delete_shader(v); - gl.delete_shader(f); + gl.detach_shader(program, vert); + gl.detach_shader(program, frag); + gl.delete_shader(vert); + gl.delete_shader(frag); let u_screen_size = gl.get_uniform_location(program, "u_screen_size").unwrap(); let u_sampler = gl.get_uniform_location(program, "u_sampler").unwrap(); let vertex_buffer = gl.create_buffer().unwrap(); @@ -138,7 +138,9 @@ impl Painter { let a_pos_loc = gl.get_attrib_location(program, "a_pos").unwrap(); let a_tc_loc = gl.get_attrib_location(program, "a_tc").unwrap(); let a_srgba_loc = gl.get_attrib_location(program, "a_srgba").unwrap(); - let mut vertex_array = vao_emulate::EmulatedVao::new(vertex_buffer); + let mut vertex_array = crate::misc_util::VAO::new(gl, true); + vertex_array.bind_vertex_array(gl); + vertex_array.bind_buffer(gl, vertex_buffer); let stride = std::mem::size_of::() as i32; let position_buffer_info = vao_emulate::BufferInfo { location: a_pos_loc, @@ -164,9 +166,9 @@ impl Painter { stride, offset: offset_of!(Vertex, color) as i32, }; - vertex_array.add_new_attribute(position_buffer_info); - vertex_array.add_new_attribute(tex_coord_buffer_info); - vertex_array.add_new_attribute(color_buffer_info); + vertex_array.add_new_attribute(gl, position_buffer_info); + vertex_array.add_new_attribute(gl, tex_coord_buffer_info); + vertex_array.add_new_attribute(gl, color_buffer_info); assert_eq!(gl.get_error(), glow::NO_ERROR, "OpenGL error occurred!"); Painter { @@ -183,7 +185,6 @@ impl Painter { vertex_buffer, element_array_buffer, old_textures: Vec::new(), - #[cfg(debug_assertions)] destroyed: false, } } @@ -196,7 +197,7 @@ impl Painter { return; // No change } let gamma = if self.post_process.is_none() { - 1.0 / 2.0 + 1.0 / 2.2 } else { 1.0 }; @@ -230,7 +231,7 @@ impl Painter { pixels_per_point: f32, ) -> (u32, u32) { gl.enable(glow::SCISSOR_TEST); - // egui outputs mesh in both winding orders: + // egui outputs mesh in both winding orders gl.disable(glow::CULL_FACE); gl.enable(glow::BLEND); @@ -253,8 +254,6 @@ impl Painter { gl.use_program(Some(self.program)); - // The texture coordinates for text are so that both nearest and linear should work with the egui font texture. - // For user textures linear sampling is more likely to be the right choice. gl.uniform_2_f32(Some(&self.u_screen_size), width_in_points, height_in_points); gl.uniform_1_i32(Some(&self.u_sampler), 0); gl.active_texture(glow::TEXTURE0); @@ -296,7 +295,9 @@ impl Painter { self.upload_pending_user_textures(gl); if let Some(ref mut post_process) = self.post_process { - post_process.begin(gl, inner_size[0] as i32, inner_size[1] as i32); + unsafe { + post_process.begin(gl, inner_size[0] as i32, inner_size[1] as i32); + } } let size_in_pixels = unsafe { self.prepare_painting(inner_size, gl, pixels_per_point) }; for egui::ClippedMesh(clip_rect, mesh) in clipped_meshes { @@ -307,7 +308,9 @@ impl Painter { gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None); } if let Some(ref post_process) = self.post_process { - post_process.end(gl); + unsafe { + post_process.end(gl); + } } unsafe { assert_eq!(glow::NO_ERROR, gl.get_error(), "GL error occurred!"); @@ -521,9 +524,9 @@ impl Painter { /// This function must be called before Painter is dropped, as Painter has some OpenGL objects /// that should be deleted. - #[cfg(debug_assertions)] + pub fn destroy(&mut self, gl: &glow::Context) { - assert!(!self.destroyed, "Only destroy once!"); + debug_assert!(!self.destroyed, "Only destroy once!"); unsafe { self.destroy_gl(gl); if let Some(ref post_process) = self.post_process { @@ -533,25 +536,9 @@ impl Painter { self.destroyed = true; } - #[cfg(not(debug_assertions))] - pub fn destroy(&self, gl: &glow::Context) { - unsafe { - self.destroy_gl(gl); - if let Some(ref post_process) = self.post_process { - post_process.destroy(gl); - } - } - } - - #[cfg(debug_assertions)] fn assert_not_destroyed(&self) { - assert!(!self.destroyed, "egui has already been destroyed!"); + debug_assert!(!self.destroyed, "the egui glow has already been destroyed!"); } - - #[inline(always)] - #[cfg(not(debug_assertions))] - #[allow(clippy::unused_self)] - fn assert_not_destroyed(&self) {} } // ported from egui_web pub fn clear(gl: &glow::Context, dimension: [u32; 2], clear_color: egui::Rgba) { @@ -572,10 +559,45 @@ pub fn clear(gl: &glow::Context, dimension: [u32; 2], clear_color: egui::Rgba) { } impl Drop for Painter { fn drop(&mut self) { - #[cfg(debug_assertions)] - assert!( + debug_assert!( self.destroyed, - "Make sure to destroy() rather than dropping, to avoid leaking OpenGL objects!" + "Make sure to call destroy() before dropping to avoid leaking OpenGL objects!" ); } } + +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); + } +} + +impl epi::NativeTexture for Painter { + type Texture = glow::Texture; + + fn register_native_texture(&mut self, native: Self::Texture) -> TextureId { + self.register_glow_texture(native) + } + + fn replace_native_texture(&mut self, id: TextureId, replacing: Self::Texture) { + if let egui::TextureId::User(id) = id { + if let Some(Some(user_texture)) = self.user_textures.get_mut(id as usize) { + *user_texture = UserTexture { + data: vec![], + gl_texture: Some(replacing), + size: (0, 0), + }; + } + } + } +} diff --git a/egui_glow/src/post_process.rs b/egui_glow/src/post_process.rs index 56098dbda5e..ac67349b45d 100644 --- a/egui_glow/src/post_process.rs +++ b/egui_glow/src/post_process.rs @@ -8,7 +8,7 @@ use glow::HasContext; pub(crate) struct PostProcess { pos_buffer: glow::Buffer, index_buffer: glow::Buffer, - vertex_array: crate::vao_emulate::EmulatedVao, + vertex_array: crate::misc_util::VAO, is_webgl_1: bool, texture: glow::Texture, texture_size: (i32, i32), @@ -17,94 +17,80 @@ pub(crate) struct PostProcess { } impl PostProcess { - pub(crate) fn new( + pub(crate) unsafe fn new( gl: &glow::Context, is_webgl_1: bool, width: i32, height: i32, ) -> Result { - let fbo = unsafe { gl.create_framebuffer() }?; - unsafe { - gl.bind_framebuffer(glow::FRAMEBUFFER, Some(fbo)); - } + let fbo = gl.create_framebuffer()?; + + gl.bind_framebuffer(glow::FRAMEBUFFER, Some(fbo)); + + let texture = gl.create_texture().unwrap(); + + gl.bind_texture(glow::TEXTURE_2D, Some(texture)); + + 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, + ); + + gl.tex_parameter_i32( + glow::TEXTURE_2D, + glow::TEXTURE_MIN_FILTER, + glow::NEAREST as i32, + ); + + gl.tex_parameter_i32( + glow::TEXTURE_2D, + glow::TEXTURE_MAG_FILTER, + glow::NEAREST as i32, + ); + + gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1); - let texture = unsafe { gl.create_texture() }.unwrap(); - unsafe { - gl.bind_texture(glow::TEXTURE_2D, Some(texture)); - } - unsafe { - gl.tex_parameter_i32( - glow::TEXTURE_2D, - glow::TEXTURE_WRAP_S, - glow::CLAMP_TO_EDGE as i32, - ); - } - unsafe { - gl.tex_parameter_i32( - glow::TEXTURE_2D, - glow::TEXTURE_WRAP_T, - glow::CLAMP_TO_EDGE as i32, - ); - } - unsafe { - gl.tex_parameter_i32( - glow::TEXTURE_2D, - glow::TEXTURE_MIN_FILTER, - glow::NEAREST as i32, - ); - } - unsafe { - gl.tex_parameter_i32( - glow::TEXTURE_2D, - glow::TEXTURE_MAG_FILTER, - glow::NEAREST as i32, - ); - } - unsafe { - gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1); - } let (internal_format, format) = if is_webgl_1 { (glow::SRGB_ALPHA, glow::SRGB_ALPHA) } else { (glow::SRGB8_ALPHA8, glow::RGBA) }; - unsafe { - gl.tex_image_2d( - glow::TEXTURE_2D, - 0, - internal_format as i32, - width, - height, - 0, - format, - glow::UNSIGNED_BYTE, - None, - ); - let error_code = gl.get_error(); - assert_eq!( - error_code, - glow::NO_ERROR, - "Error occurred in post process texture initialization. code : 0x{:x}", - error_code - ); - } - unsafe { - gl.framebuffer_texture_2d( - glow::FRAMEBUFFER, - glow::COLOR_ATTACHMENT0, - glow::TEXTURE_2D, - Some(texture), - 0, - ); - } - - unsafe { - gl.bind_texture(glow::TEXTURE_2D, None); - } - unsafe { - gl.bind_framebuffer(glow::FRAMEBUFFER, None); - } + gl.tex_image_2d( + glow::TEXTURE_2D, + 0, + internal_format as i32, + width, + height, + 0, + format, + glow::UNSIGNED_BYTE, + None, + ); + let error_code = gl.get_error(); + assert_eq!( + error_code, + glow::NO_ERROR, + "Error occurred in post process texture initialization. code : 0x{:x}", + error_code + ); + + gl.framebuffer_texture_2d( + glow::FRAMEBUFFER, + glow::COLOR_ATTACHMENT0, + glow::TEXTURE_2D, + Some(texture), + 0, + ); + gl.bind_texture(glow::TEXTURE_2D, None); + gl.bind_framebuffer(glow::FRAMEBUFFER, None); let vert_shader = compile_shader( gl, @@ -121,124 +107,111 @@ impl PostProcess { let positions = vec![0.0f32, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0]; let indices = vec![0u8, 1, 2, 1, 2, 3]; - unsafe { - let pos_buffer = gl.create_buffer()?; - gl.bind_buffer(glow::ARRAY_BUFFER, Some(pos_buffer)); - gl.buffer_data_u8_slice( - glow::ARRAY_BUFFER, - crate::misc_util::as_u8_slice(&positions), - glow::STATIC_DRAW, - ); - - let a_pos_loc = gl - .get_attrib_location(program, "a_pos") - .ok_or_else(|| "failed to get location of a_pos".to_string())?; - let mut vertex_array = crate::vao_emulate::EmulatedVao::new(pos_buffer); - let buffer_info_a_pos = BufferInfo { - location: a_pos_loc, - vector_size: 2, - data_type: glow::FLOAT, - normalized: false, - stride: 0, - offset: 0, - }; - vertex_array.add_new_attribute(buffer_info_a_pos); - gl.bind_buffer(glow::ARRAY_BUFFER, None); - - let index_buffer = gl.create_buffer()?; - gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(index_buffer)); - gl.buffer_data_u8_slice(glow::ELEMENT_ARRAY_BUFFER, &indices, glow::STATIC_DRAW); - - gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None); - let error_code = gl.get_error(); - assert_eq!( - error_code, - glow::NO_ERROR, - "Error occurred in post process initialization. code : 0x{:x}", - error_code - ); - - Ok(PostProcess { - pos_buffer, - index_buffer, - vertex_array, - is_webgl_1, - texture, - texture_size: (width, height), - fbo, - program, - }) - } + let pos_buffer = gl.create_buffer()?; + gl.bind_buffer(glow::ARRAY_BUFFER, Some(pos_buffer)); + gl.buffer_data_u8_slice( + glow::ARRAY_BUFFER, + crate::misc_util::as_u8_slice(&positions), + glow::STATIC_DRAW, + ); + + let a_pos_loc = gl + .get_attrib_location(program, "a_pos") + .ok_or_else(|| "failed to get location of a_pos".to_string())?; + let mut vertex_array = crate::misc_util::VAO::new(gl, true); + vertex_array.bind_vertex_array(gl); + vertex_array.bind_buffer(gl, pos_buffer); + let buffer_info_a_pos = BufferInfo { + location: a_pos_loc, + vector_size: 2, + data_type: glow::FLOAT, + normalized: false, + stride: 0, + offset: 0, + }; + vertex_array.add_new_attribute(gl, buffer_info_a_pos); + + let index_buffer = gl.create_buffer()?; + gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(index_buffer)); + gl.buffer_data_u8_slice(glow::ELEMENT_ARRAY_BUFFER, &indices, glow::STATIC_DRAW); + + gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None); + let error_code = gl.get_error(); + assert_eq!( + error_code, + glow::NO_ERROR, + "Error occurred in post process initialization. code : 0x{:x}", + error_code + ); + + Ok(PostProcess { + pos_buffer, + index_buffer, + vertex_array, + is_webgl_1, + texture, + texture_size: (width, height), + fbo, + program, + }) } - pub(crate) fn begin(&mut self, gl: &glow::Context, width: i32, height: i32) { + pub(crate) unsafe fn begin(&mut self, gl: &glow::Context, width: i32, height: i32) { if (width, height) != self.texture_size { - unsafe { - gl.bind_texture(glow::TEXTURE_2D, Some(self.texture)); - } - unsafe { - gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1); - } + gl.bind_texture(glow::TEXTURE_2D, Some(self.texture)); + gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1); + let (internal_format, format) = if self.is_webgl_1 { (glow::SRGB_ALPHA, glow::SRGB_ALPHA) } else { (glow::SRGB8_ALPHA8, glow::RGBA) }; - unsafe { - gl.tex_image_2d( - glow::TEXTURE_2D, - 0, - internal_format as i32, - width, - height, - 0, - format, - glow::UNSIGNED_BYTE, - None, - ); - } - unsafe { - gl.bind_texture(glow::TEXTURE_2D, None); - } + gl.tex_image_2d( + glow::TEXTURE_2D, + 0, + internal_format as i32, + width, + height, + 0, + format, + glow::UNSIGNED_BYTE, + None, + ); + gl.bind_texture(glow::TEXTURE_2D, None); self.texture_size = (width, height); } - unsafe { - gl.bind_framebuffer(glow::FRAMEBUFFER, Some(self.fbo)); - gl.clear_color(0.0, 0.0, 0.0, 0.0); - gl.clear(glow::COLOR_BUFFER_BIT); - } + + gl.bind_framebuffer(glow::FRAMEBUFFER, Some(self.fbo)); + gl.clear_color(0.0, 0.0, 0.0, 0.0); + gl.clear(glow::COLOR_BUFFER_BIT); } - pub(crate) fn end(&self, gl: &glow::Context) { - unsafe { - gl.bind_framebuffer(glow::FRAMEBUFFER, None); - gl.disable(glow::SCISSOR_TEST); + pub(crate) unsafe fn end(&self, gl: &glow::Context) { + gl.bind_framebuffer(glow::FRAMEBUFFER, None); + gl.disable(glow::SCISSOR_TEST); - gl.use_program(Some(self.program)); + gl.use_program(Some(self.program)); - gl.active_texture(glow::TEXTURE0); - gl.bind_texture(glow::TEXTURE_2D, Some(self.texture)); - let u_sampler_loc = gl.get_uniform_location(self.program, "u_sampler").unwrap(); - gl.uniform_1_i32(Some(&u_sampler_loc), 0); - self.vertex_array.bind_vertex_array(gl); - - gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.index_buffer)); - gl.draw_elements(glow::TRIANGLES, 6, glow::UNSIGNED_BYTE, 0); - self.vertex_array.unbind_vertex_array(gl); - gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None); - gl.bind_texture(glow::TEXTURE_2D, None); - gl.use_program(None); - } + gl.active_texture(glow::TEXTURE0); + gl.bind_texture(glow::TEXTURE_2D, Some(self.texture)); + let u_sampler_loc = gl.get_uniform_location(self.program, "u_sampler").unwrap(); + gl.uniform_1_i32(Some(&u_sampler_loc), 0); + self.vertex_array.bind_vertex_array(gl); + + gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.index_buffer)); + gl.draw_elements(glow::TRIANGLES, 6, glow::UNSIGNED_BYTE, 0); + self.vertex_array.unbind_vertex_array(gl); + gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None); + gl.bind_texture(glow::TEXTURE_2D, None); + gl.use_program(None); } - pub(crate) fn destroy(&self, gl: &glow::Context) { - unsafe { - gl.delete_buffer(self.pos_buffer); - gl.delete_buffer(self.index_buffer); - gl.delete_program(self.program); - gl.delete_framebuffer(self.fbo); - gl.delete_texture(self.texture); - } + pub(crate) unsafe fn destroy(&self, gl: &glow::Context) { + gl.delete_buffer(self.pos_buffer); + gl.delete_buffer(self.index_buffer); + gl.delete_program(self.program); + gl.delete_framebuffer(self.fbo); + gl.delete_texture(self.texture); } } diff --git a/egui_glow/src/shader/fragment.glsl b/egui_glow/src/shader/fragment.glsl index 0fff62ccd36..288cdf0cda2 100644 --- a/egui_glow/src/shader/fragment.glsl +++ b/egui_glow/src/shader/fragment.glsl @@ -74,4 +74,4 @@ void main() { gl_FragColor = v_rgba * texture2D(u_sampler, v_tc); } - #endif \ No newline at end of file + #endif diff --git a/egui_glow/src/shader/post_fragment_100es.glsl b/egui_glow/src/shader/post_fragment_100es.glsl index 56b6ac59a4f..001f8a3f236 100644 --- a/egui_glow/src/shader/post_fragment_100es.glsl +++ b/egui_glow/src/shader/post_fragment_100es.glsl @@ -19,4 +19,4 @@ void main() { gl_FragColor = texture2D(u_sampler, v_tc); gl_FragColor = srgba_from_linear(gl_FragColor) / 255.; -} \ No newline at end of file +} diff --git a/egui_glow/src/shader/post_vertex_100es.glsl b/egui_glow/src/shader/post_vertex_100es.glsl index b07658173c7..37280bc1dad 100644 --- a/egui_glow/src/shader/post_vertex_100es.glsl +++ b/egui_glow/src/shader/post_vertex_100es.glsl @@ -5,4 +5,4 @@ varying vec2 v_tc; void main() { gl_Position = vec4(a_pos * 2. - 1., 0.0, 1.0); v_tc = a_pos; -} \ No newline at end of file +} diff --git a/egui_glow/src/vao_emulate.rs b/egui_glow/src/vao_emulate.rs index 339ffd5b4f2..3a454087d0a 100644 --- a/egui_glow/src/vao_emulate.rs +++ b/egui_glow/src/vao_emulate.rs @@ -10,38 +10,36 @@ pub(crate) struct BufferInfo { pub offset: i32, } pub struct EmulatedVao { - buffer: glow::Buffer, + buffer: Option, buffer_infos: Vec, - taught: bool, } impl EmulatedVao { - #[allow(clippy::needless_pass_by_value)] - pub(crate) fn new(buffer: glow::Buffer) -> Self { + pub(crate) fn new() -> Self { Self { - buffer, + buffer: None, buffer_infos: vec![], - taught: false, } } + pub(crate) fn bind_buffer(&mut self, buffer: glow::Buffer) { + let _old = self.buffer.replace(buffer); + } pub(crate) fn add_new_attribute(&mut self, buffer_info: BufferInfo) { self.buffer_infos.push(buffer_info); } pub(crate) fn bind_vertex_array(&self, gl: &glow::Context) { unsafe { - gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.buffer)); + gl.bind_buffer(glow::ARRAY_BUFFER, self.buffer); } for attribute in self.buffer_infos.iter() { unsafe { - if !self.taught { - gl.vertex_attrib_pointer_f32( - attribute.location, - attribute.vector_size, - attribute.data_type, - attribute.normalized, - attribute.stride, - attribute.offset, - ); - } + gl.vertex_attrib_pointer_f32( + attribute.location, + attribute.vector_size, + attribute.data_type, + attribute.normalized, + attribute.stride, + attribute.offset, + ); gl.enable_vertex_attrib_array(attribute.location); } } diff --git a/egui_web/Cargo.toml b/egui_web/Cargo.toml index 833eb222b28..38a714f9205 100644 --- a/egui_web/Cargo.toml +++ b/egui_web/Cargo.toml @@ -28,7 +28,7 @@ crate-type = ["cdylib", "rlib"] egui = { version = "0.15.0", path = "../egui", default-features = false, features = [ "single_threaded", ] } -egui_glow = { version = "0.15.0",path = "../egui_glow", default-features = false , optional = true } +egui_glow = { version = "0.15.0",path = "../egui_glow", default-features = false, optional = true } epi = { version = "0.15.0", path = "../epi" } js-sys = "0.3" ron = { version = "0.7", optional = true } @@ -45,7 +45,7 @@ default = ["default_fonts"] default_fonts = ["egui/default_fonts"] persistence = ["egui/persistence", "ron", "serde"] screen_reader = ["tts"] # experimental -use_glow_painter =["egui_glow/painter_only","egui_glow/epi"] +glow =["egui_glow","egui_glow/epi"] [dependencies.web-sys] version = "0.3.52" features = [ diff --git a/egui_web/src/backend.rs b/egui_web/src/backend.rs index 4fd28acfbcf..66c409a33ea 100644 --- a/egui_web/src/backend.rs +++ b/egui_web/src/backend.rs @@ -16,19 +16,8 @@ impl WebBackend { let ctx = egui::CtxRef::default(); #[cfg(feature = "use_glow_painter")] - let painter: Box = { - let canvas = canvas_element_or_die(canvas_id); - let gl_ctx = - egui_glow::create_context_for_canvas::init_glow_context_from_canvas(&canvas); - let dimension = [canvas.width() as i32, canvas.height() as i32]; - let painter = egui_glow::Painter::new(&gl_ctx, Some(dimension)); - Box::new(crate::glow_wrapping::WrappedGlowPainter { - gl_ctx, - canvas, - canvas_id: canvas_id.to_owned(), - painter, - }) - }; + let painter: Box = + { Box::new(glow_wrapping::WrappedGlowPainter::new(canvas_id)) }; #[cfg(not(feature = "use_glow_painter"))] let painter: Box = { if let Ok(webgl2_painter) = webgl2::WebGl2Painter::new(canvas_id) { @@ -239,12 +228,8 @@ impl AppRunner { } fn integration_info(&self) -> epi::IntegrationInfo { - #[cfg(not(feature = "use_glow_painter"))] - const NAME: &str = "egui_web"; - #[cfg(feature = "use_glow_painter")] - const NAME: &str = "egui_web(painted by glow)"; epi::IntegrationInfo { - name: NAME, + name: self.web_backend.painter.name(), web_info: Some(epi::WebInfo { web_location_hash: location_hash().unwrap_or_default(), }), diff --git a/egui_web/src/glow_wrapping.rs b/egui_web/src/glow_wrapping.rs index 92de8d95676..fa38f2c00fd 100644 --- a/egui_web/src/glow_wrapping.rs +++ b/egui_web/src/glow_wrapping.rs @@ -1,15 +1,31 @@ +use crate::canvas_element_or_die; use egui::{ClippedMesh, Rgba, Texture}; +use egui_glow::glow; use epi::TextureAllocator; use wasm_bindgen::JsValue; use web_sys::HtmlCanvasElement; pub(crate) struct WrappedGlowPainter { - pub(crate) gl_ctx: egui_glow::Context, + pub(crate) gl_ctx: glow::Context, pub(crate) canvas: HtmlCanvasElement, pub(crate) canvas_id: String, pub(crate) painter: egui_glow::Painter, } +impl WrappedGlowPainter { + pub fn new(canvas_id: &str) -> Self { + let canvas = canvas_element_or_die(canvas_id); + let gl_ctx = init_glow_context_from_canvas(&canvas); + let dimension = [canvas.width() as i32, canvas.height() as i32]; + let painter = egui_glow::Painter::new(&gl_ctx, Some(dimension)); + Self { + gl_ctx, + canvas, + canvas_id: canvas_id.to_owned(), + painter, + } + } +} impl crate::Painter for WrappedGlowPainter { fn as_tex_allocator(&mut self) -> &mut dyn TextureAllocator { &mut self.painter @@ -50,4 +66,39 @@ impl crate::Painter for WrappedGlowPainter { ); Ok(()) } + + fn name(&self) -> &'static str { + "egui_web(glow)" + } +} + +pub fn init_glow_context_from_canvas(canvas: &HtmlCanvasElement) -> glow::Context { + use wasm_bindgen::JsCast; + let ctx = canvas.get_context("webgl2"); + if let Ok(ctx) = ctx { + crate::console_log("webgl found"); + if let Some(ctx) = ctx { + crate::console_log("webgl 2 selected"); + let gl_ctx = ctx.dyn_into::().unwrap(); + glow::Context::from_webgl2_context(gl_ctx) + } else { + let ctx = canvas.get_context("webgl"); + if let Ok(ctx) = ctx { + crate::console_log("falling back to webgl1"); + if let Some(ctx) = ctx { + crate::console_log("webgl1 selected"); + + let gl_ctx = ctx.dyn_into::().unwrap(); + crate::console_log("success"); + glow::Context::from_webgl1_context(gl_ctx) + } else { + panic!("tried webgl1 but can't get context"); + } + } else { + panic!("tried webgl1 but can't get context"); + } + } + } else { + panic!("tried webgl2 but something went wrong"); + } } diff --git a/egui_web/src/painter.rs b/egui_web/src/painter.rs index fa1d84b2d50..0a414b949c5 100644 --- a/egui_web/src/painter.rs +++ b/egui_web/src/painter.rs @@ -17,4 +17,6 @@ pub trait Painter { clipped_meshes: Vec, pixels_per_point: f32, ) -> Result<(), JsValue>; + + fn name(&self) -> &'static str; } diff --git a/egui_web/src/webgl1.rs b/egui_web/src/webgl1.rs index aee2fadaf7a..91343dc29a7 100644 --- a/egui_web/src/webgl1.rs +++ b/egui_web/src/webgl1.rs @@ -538,6 +538,10 @@ impl crate::Painter for WebGlPainter { Ok(()) } + + fn name(&self) -> &'static str { + "egui_web(webgl1)" + } } struct PostProcess { diff --git a/egui_web/src/webgl2.rs b/egui_web/src/webgl2.rs index 4d76cc05f9c..b6d0ec90924 100644 --- a/egui_web/src/webgl2.rs +++ b/egui_web/src/webgl2.rs @@ -516,6 +516,10 @@ impl crate::Painter for WebGl2Painter { Ok(()) } + + fn name(&self) -> &'static str { + "egui_glow(webgl2)" + } } /// Uses a framebuffer to render everything in linear color space and convert it back to sRGB From 5bd0d5da62eff7a2d88285093388fb7cc9a303f5 Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 07:42:22 +0900 Subject: [PATCH 09/27] revert eframe change. --- eframe/src/lib.rs | 1 - egui_web/Cargo.toml | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/eframe/src/lib.rs b/eframe/src/lib.rs index 1586f5d4704..dd9036db38f 100644 --- a/eframe/src/lib.rs +++ b/eframe/src/lib.rs @@ -97,7 +97,6 @@ pub use egui_web::wasm_bindgen; /// #[cfg(target_arch = "wasm32")] /// #[wasm_bindgen] /// pub fn start(canvas_id: &str) -> Result<(), eframe::wasm_bindgen::JsValue> { -/// std::panic::set_hook(console_error_panic_hook::hook); /// let app = MyEguiApp::default(); /// eframe::start_web(canvas_id, Box::new(app)) /// } diff --git a/egui_web/Cargo.toml b/egui_web/Cargo.toml index 38a714f9205..3f1322c3d0c 100644 --- a/egui_web/Cargo.toml +++ b/egui_web/Cargo.toml @@ -46,6 +46,7 @@ default_fonts = ["egui/default_fonts"] persistence = ["egui/persistence", "ron", "serde"] screen_reader = ["tts"] # experimental glow =["egui_glow","egui_glow/epi"] + [dependencies.web-sys] version = "0.3.52" features = [ From 88027b160bf805153fc8fa6c898c61c410fb0bf4 Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 07:51:50 +0900 Subject: [PATCH 10/27] small clippy fix ,toml fix and rewrite changelog --- egui_glow/CHANGELOG.md | 1 - egui_glow/Cargo.toml | 6 +----- egui_glow/src/misc_util.rs | 12 ++++++++---- egui_glow/src/vao_emulate.rs | 4 ++-- egui_web/CHANGELOG.md | 2 +- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/egui_glow/CHANGELOG.md b/egui_glow/CHANGELOG.md index 5f7a650e42d..137d91a5937 100644 --- a/egui_glow/CHANGELOG.md +++ b/egui_glow/CHANGELOG.md @@ -3,7 +3,6 @@ All notable changes to the `egui_glow` integration will be noted in this file. ## Unreleased -* add `painter_only` feature to reuse painter implementation. * no glutin dependency in painter . ## 0.15.0 - 2021-10-24 diff --git a/egui_glow/Cargo.toml b/egui_glow/Cargo.toml index 014986fd539..fc5e906057d 100644 --- a/egui_glow/Cargo.toml +++ b/egui_glow/Cargo.toml @@ -32,17 +32,13 @@ memoffset = "0.6" 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=["HtmlCanvasElement", "WebGl2RenderingContext","WebGlRenderingContext", "Window","console"] } -wasm-bindgen = { version = "0.2" } - [dev-dependencies] image = { version = "0.23", default-features = false, features = ["png"] } [features] default = ["clipboard", "default_fonts", "links", "persistence", "winit"] winit= ["egui-winit","glutin"] + # enable cut/copy/paste to OS clipboard. # if disabled a clipboard will be simulated so you can still copy/paste within the egui app. clipboard = ["egui-winit/clipboard"] diff --git a/egui_glow/src/misc_util.rs b/egui_glow/src/misc_util.rs index 254fee1e77b..c6329d9d68b 100644 --- a/egui_glow/src/misc_util.rs +++ b/egui_glow/src/misc_util.rs @@ -149,8 +149,10 @@ impl VAO { } pub(crate) fn bind_buffer(&mut self, gl: &glow::Context, buffer: glow::Buffer) { match self { - VAO::Emulated(vao) => vao.bind_buffer(buffer), - VAO::Native(_) => unsafe { gl.bind_buffer(glow::ARRAY_BUFFER, Some(buffer)) }, + VAO::Emulated(vao) => vao.bind_buffer(&buffer), + VAO::Native(_) => unsafe { + gl.bind_buffer(glow::ARRAY_BUFFER, Some(buffer)); + }, } } pub(crate) fn add_new_attribute( @@ -169,14 +171,16 @@ impl VAO { buffer_info.stride, buffer_info.offset, ); - gl.enable_vertex_attrib_array(buffer_info.location) + gl.enable_vertex_attrib_array(buffer_info.location); }, } } pub(crate) fn unbind_vertex_array(&self, gl: &glow::Context) { match self { VAO::Emulated(vao) => vao.unbind_vertex_array(gl), - VAO::Native(_) => unsafe { gl.bind_vertex_array(None) }, + VAO::Native(_) => unsafe { + gl.bind_vertex_array(None); + }, } } } diff --git a/egui_glow/src/vao_emulate.rs b/egui_glow/src/vao_emulate.rs index 3a454087d0a..590134945e5 100644 --- a/egui_glow/src/vao_emulate.rs +++ b/egui_glow/src/vao_emulate.rs @@ -20,8 +20,8 @@ impl EmulatedVao { buffer_infos: vec![], } } - pub(crate) fn bind_buffer(&mut self, buffer: glow::Buffer) { - let _old = self.buffer.replace(buffer); + pub(crate) fn bind_buffer(&mut self, buffer: &glow::Buffer) { + let _old = self.buffer.replace(*buffer); } pub(crate) fn add_new_attribute(&mut self, buffer_info: BufferInfo) { self.buffer_infos.push(buffer_info); diff --git a/egui_web/CHANGELOG.md b/egui_web/CHANGELOG.md index c62ac03b0b1..42f94b27841 100644 --- a/egui_web/CHANGELOG.md +++ b/egui_web/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to the `egui_web` integration will be noted in this file. ## Unreleased -* add feature `use_glow_painter` if enabled painting switched to `egui_glow::painter::Painter`. +* add feature `glow` if enabled painting switched to `egui_glow::painter::Painter`. ## 0.15.0 - 2021-10-24 ### Added From fb42a660bc4cd3cb45e89dc2809519436620ea49 Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 07:54:17 +0900 Subject: [PATCH 11/27] webgl2 return correct name. --- egui_web/src/webgl2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/egui_web/src/webgl2.rs b/egui_web/src/webgl2.rs index b6d0ec90924..d32c9bc2da6 100644 --- a/egui_web/src/webgl2.rs +++ b/egui_web/src/webgl2.rs @@ -518,7 +518,7 @@ impl crate::Painter for WebGl2Painter { } fn name(&self) -> &'static str { - "egui_glow(webgl2)" + "egui_web(webgl2)" } } From 51981c5a07d250fe66654037071216f9afac661a Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 08:02:34 +0900 Subject: [PATCH 12/27] correct Cargo.toml for egui_glow. --- egui_glow/Cargo.toml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/egui_glow/Cargo.toml b/egui_glow/Cargo.toml index fc5e906057d..6e18afefffd 100644 --- a/egui_glow/Cargo.toml +++ b/egui_glow/Cargo.toml @@ -32,12 +32,15 @@ memoffset = "0.6" 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", "winit"] -winit= ["egui-winit","glutin"] # enable cut/copy/paste to OS clipboard. # if disabled a clipboard will be simulated so you can still copy/paste within the egui app. @@ -61,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"] \ No newline at end of file From 18b05b9a33852ef10514d4816e34e27f8b05e294 Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 08:05:33 +0900 Subject: [PATCH 13/27] clippy::needless-pass-by-value fix --- egui_glow/src/misc_util.rs | 6 +++--- egui_glow/src/painter.rs | 2 +- egui_glow/src/post_process.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/egui_glow/src/misc_util.rs b/egui_glow/src/misc_util.rs index c6329d9d68b..ccf75796498 100644 --- a/egui_glow/src/misc_util.rs +++ b/egui_glow/src/misc_util.rs @@ -147,11 +147,11 @@ impl VAO { VAO::Native(vao) => gl.bind_vertex_array(Some(*vao)), } } - pub(crate) fn bind_buffer(&mut self, gl: &glow::Context, buffer: glow::Buffer) { + pub(crate) fn bind_buffer(&mut self, gl: &glow::Context, buffer: &glow::Buffer) { match self { - VAO::Emulated(vao) => vao.bind_buffer(&buffer), + VAO::Emulated(vao) => vao.bind_buffer(buffer), VAO::Native(_) => unsafe { - gl.bind_buffer(glow::ARRAY_BUFFER, Some(buffer)); + gl.bind_buffer(glow::ARRAY_BUFFER, Some(*buffer)); }, } } diff --git a/egui_glow/src/painter.rs b/egui_glow/src/painter.rs index ca41acc6f70..7df03145c12 100644 --- a/egui_glow/src/painter.rs +++ b/egui_glow/src/painter.rs @@ -140,7 +140,7 @@ impl Painter { let a_srgba_loc = gl.get_attrib_location(program, "a_srgba").unwrap(); let mut vertex_array = crate::misc_util::VAO::new(gl, true); vertex_array.bind_vertex_array(gl); - vertex_array.bind_buffer(gl, vertex_buffer); + vertex_array.bind_buffer(gl, &vertex_buffer); let stride = std::mem::size_of::() as i32; let position_buffer_info = vao_emulate::BufferInfo { location: a_pos_loc, diff --git a/egui_glow/src/post_process.rs b/egui_glow/src/post_process.rs index ac67349b45d..23583621589 100644 --- a/egui_glow/src/post_process.rs +++ b/egui_glow/src/post_process.rs @@ -121,7 +121,7 @@ impl PostProcess { .ok_or_else(|| "failed to get location of a_pos".to_string())?; let mut vertex_array = crate::misc_util::VAO::new(gl, true); vertex_array.bind_vertex_array(gl); - vertex_array.bind_buffer(gl, pos_buffer); + vertex_array.bind_buffer(gl, &pos_buffer); let buffer_info_a_pos = BufferInfo { location: a_pos_loc, vector_size: 2, From dd2f8a62ea3be929a83400310db0a42f021c2cc4 Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 21:39:35 +0900 Subject: [PATCH 14/27] Update egui_web/CHANGELOG.md Co-authored-by: Emil Ernerfeldt --- egui_web/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/egui_web/CHANGELOG.md b/egui_web/CHANGELOG.md index 42f94b27841..94773637f52 100644 --- a/egui_web/CHANGELOG.md +++ b/egui_web/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to the `egui_web` integration will be noted in this file. ## Unreleased -* add feature `glow` if enabled painting switched to `egui_glow::painter::Painter`. +*Add feature `glow` to switch to a [`glow`](https://github.com/grovesNL/glow) based painter ([#868](https://github.com/emilk/egui/pull/868)). ## 0.15.0 - 2021-10-24 ### Added From a4d71463f4d109af52f2bb49e9582e48808dcc09 Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 21:40:26 +0900 Subject: [PATCH 15/27] Update egui_glow/src/painter.rs Co-authored-by: Emil Ernerfeldt --- egui_glow/src/painter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/egui_glow/src/painter.rs b/egui_glow/src/painter.rs index 7df03145c12..abd67ffabd7 100644 --- a/egui_glow/src/painter.rs +++ b/egui_glow/src/painter.rs @@ -226,7 +226,7 @@ impl Painter { unsafe fn prepare_painting( &mut self, - inner_size: [u32; 2], + [width_in_pixels, height_in_pixels]: [u32; 2], gl: &glow::Context, pixels_per_point: f32, ) -> (u32, u32) { From 09c32b63833617a02203cb1e60e3577a7b1b9ff3 Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 21:40:58 +0900 Subject: [PATCH 16/27] Update egui_glow/CHANGELOG.md Co-authored-by: Emil Ernerfeldt --- egui_glow/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/egui_glow/CHANGELOG.md b/egui_glow/CHANGELOG.md index 137d91a5937..7ae2e9175d1 100644 --- a/egui_glow/CHANGELOG.md +++ b/egui_glow/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to the `egui_glow` integration will be noted in this file. ## Unreleased -* no glutin dependency in painter . +* Make winit/glutin an optional dependency ([#868](https://github.com/emilk/egui/pull/868)). ## 0.15.0 - 2021-10-24 `egui_glow` has been newly created, with feature parity to `egui_glium`. From a47e7e553396dc24ee75e12aa0c656e80f31b7b3 Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 21:41:20 +0900 Subject: [PATCH 17/27] Update egui_glow/src/painter.rs Co-authored-by: Emil Ernerfeldt --- egui_glow/src/painter.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/egui_glow/src/painter.rs b/egui_glow/src/painter.rs index abd67ffabd7..0633bd97076 100644 --- a/egui_glow/src/painter.rs +++ b/egui_glow/src/painter.rs @@ -42,6 +42,7 @@ pub struct Painter { // Stores outdated OpenGL textures that are yet to be deleted old_textures: Vec, + // Only used in debug builds, to make sure we are destroyed correctly. destroyed: bool, } From ff2592945d419848f2852e41bde79ca280ea0dfb Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 21:41:35 +0900 Subject: [PATCH 18/27] Update egui_glow/examples/pure_glow.rs Co-authored-by: Emil Ernerfeldt --- egui_glow/examples/pure_glow.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/egui_glow/examples/pure_glow.rs b/egui_glow/examples/pure_glow.rs index ff139807dd3..9ca2c6bed4e 100644 --- a/egui_glow/examples/pure_glow.rs +++ b/egui_glow/examples/pure_glow.rs @@ -35,6 +35,7 @@ fn create_display( (gl_window, gl) } + fn main() { let event_loop = glutin::event_loop::EventLoop::with_user_event(); let (gl_window, gl) = create_display(&event_loop); From 22245bedf3b4cd264cf9653e8bd086db750b169a Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 21:42:08 +0900 Subject: [PATCH 19/27] Update egui_glow/src/painter.rs Co-authored-by: Emil Ernerfeldt --- egui_glow/src/painter.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/egui_glow/src/painter.rs b/egui_glow/src/painter.rs index 0633bd97076..682f2179519 100644 --- a/egui_glow/src/painter.rs +++ b/egui_glow/src/painter.rs @@ -58,12 +58,9 @@ pub(crate) struct UserTexture { } impl Painter { - /// create painter + /// Create painter. /// - /// if `pp_fb_extent` is none post process disabled . - /// when post process disabled `sRGB` invalid color appeared on OpenGL ES and WebGL . - /// - /// to enable post process set framebuffer dimension to `pp_fb_extent`. + /// Set `pp_fb_extent` to the framebuffer size to enable `sRGB` support on OpenGL ES and WebGL. pub fn new(gl: &glow::Context, pp_fb_extent: Option<[i32; 2]>) -> Painter { let shader_version = ShaderVersion::get(gl); let is_webgl_1 = shader_version == ShaderVersion::Es100; From dc72728687a3b04718276f2477070cf67e40b02c Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 21:42:35 +0900 Subject: [PATCH 20/27] Update egui_glow/src/painter.rs Co-authored-by: Emil Ernerfeldt --- egui_glow/src/painter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/egui_glow/src/painter.rs b/egui_glow/src/painter.rs index 682f2179519..9675609db7d 100644 --- a/egui_glow/src/painter.rs +++ b/egui_glow/src/painter.rs @@ -194,7 +194,7 @@ impl Painter { if self.egui_texture_version == Some(texture.version) { return; // No change } - let gamma = if self.post_process.is_none() { + let gamma = if self.is_web && self.post_process.is_none() { 1.0 / 2.2 } else { 1.0 From e95ecb6919518c2164f0e6f2e48d51a18734bc96 Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 21:57:37 +0900 Subject: [PATCH 21/27] test we have to use emulated vao. --- egui_glow/src/misc_util.rs | 119 +++++++++++++++++++++++----------- egui_glow/src/painter.rs | 26 +++++--- egui_glow/src/post_process.rs | 7 +- egui_web/src/backend.rs | 4 +- egui_web/src/lib.rs | 2 +- 5 files changed, 107 insertions(+), 51 deletions(-) diff --git a/egui_glow/src/misc_util.rs b/egui_glow/src/misc_util.rs index ccf75796498..c6e6923c0b1 100644 --- a/egui_glow/src/misc_util.rs +++ b/egui_glow/src/misc_util.rs @@ -1,5 +1,6 @@ #![allow(unsafe_code)] use glow::HasContext; +use std::option::Option::Some; #[cfg(target_arch = "wasm32")] use wasm_bindgen::JsValue; @@ -87,44 +88,40 @@ pub(crate) fn glow_debug_print(s: impl std::fmt::Display) { println!("{}", s); } -pub(crate) fn compile_shader( +pub(crate) unsafe fn compile_shader( gl: &glow::Context, shader_type: u32, source: &str, ) -> Result { - let shader = unsafe { gl.create_shader(shader_type) }?; - unsafe { - gl.shader_source(shader, source); - } - unsafe { - gl.compile_shader(shader); - } + let shader = gl.create_shader(shader_type)?; - if unsafe { gl.get_shader_compile_status(shader) } { + gl.shader_source(shader, source); + + gl.compile_shader(shader); + + if gl.get_shader_compile_status(shader) { Ok(shader) } else { - Err(unsafe { gl.get_shader_info_log(shader) }) + Err(gl.get_shader_info_log(shader)) } } -pub(crate) fn link_program<'a, T: IntoIterator>( +pub(crate) unsafe fn link_program<'a, T: IntoIterator>( gl: &glow::Context, shaders: T, ) -> Result { - let program = unsafe { gl.create_program() }?; - unsafe { - for shader in shaders { - gl.attach_shader(program, *shader); - } - } - unsafe { - gl.link_program(program); + let program = gl.create_program()?; + + for shader in shaders { + gl.attach_shader(program, *shader); } - if unsafe { gl.get_program_link_status(program) } { + gl.link_program(program); + + if gl.get_program_link_status(program) { Ok(program) } else { - Err(unsafe { gl.get_program_info_log(program) }) + Err(gl.get_program_info_log(program)) } } ///Wrapper around Emulated VAO and GL's VAO @@ -134,35 +131,36 @@ pub(crate) enum VAO { } impl VAO { - pub(crate) unsafe fn new(gl: &glow::Context, is_native_vao: bool) -> Self { - if is_native_vao { - Self::Native(gl.create_vertex_array().unwrap()) - } else { - Self::Emulated(crate::vao_emulate::EmulatedVao::new()) - } + 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) fn bind_buffer(&mut self, gl: &glow::Context, buffer: &glow::Buffer) { + + 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(_) => unsafe { - gl.bind_buffer(glow::ARRAY_BUFFER, Some(*buffer)); - }, + VAO::Native(_) => gl.bind_buffer(glow::ARRAY_BUFFER, Some(*buffer)), } } - pub(crate) fn add_new_attribute( + + 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(_) => unsafe { + VAO::Native(_) => { gl.vertex_attrib_pointer_f32( buffer_info.location, buffer_info.vector_size, @@ -172,15 +170,62 @@ impl VAO { buffer_info.offset, ); gl.enable_vertex_attrib_array(buffer_info.location); - }, + } } } - pub(crate) fn unbind_vertex_array(&self, gl: &glow::Context) { + + pub(crate) unsafe fn unbind_vertex_array(&self, gl: &glow::Context) { match self { VAO::Emulated(vao) => vao.unbind_vertex_array(gl), - VAO::Native(_) => unsafe { + 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 } } } diff --git a/egui_glow/src/painter.rs b/egui_glow/src/painter.rs index 9675609db7d..530bffccc98 100644 --- a/egui_glow/src/painter.rs +++ b/egui_glow/src/painter.rs @@ -32,6 +32,7 @@ pub struct Painter { egui_texture: Option, egui_texture_version: Option, is_webgl_1: bool, + is_embedded: bool, vertex_array: crate::misc_util::VAO, srgb_support: bool, /// `None` means unallocated (freed) slot. @@ -62,6 +63,7 @@ impl Painter { /// /// Set `pp_fb_extent` to the framebuffer size to enable `sRGB` support on OpenGL ES and WebGL. pub fn new(gl: &glow::Context, pp_fb_extent: Option<[i32; 2]>) -> Painter { + let need_to_emulate_vao = unsafe { crate::misc_util::need_to_emulate_vao(gl) }; let shader_version = ShaderVersion::get(gl); let is_webgl_1 = shader_version == ShaderVersion::Es100; let header = shader_version.version(); @@ -75,7 +77,10 @@ impl Painter { glow_debug_print("WebGL with sRGB enabled so turn on post process"); //install post process to correct sRGB color ( - unsafe { PostProcess::new(gl, is_webgl_1, width, height) }.ok(), + unsafe { + PostProcess::new(gl, need_to_emulate_vao, is_webgl_1, width, height) + } + .ok(), "#define SRGB_SUPPORTED", ) } else { @@ -136,7 +141,11 @@ impl Painter { let a_pos_loc = gl.get_attrib_location(program, "a_pos").unwrap(); let a_tc_loc = gl.get_attrib_location(program, "a_tc").unwrap(); let a_srgba_loc = gl.get_attrib_location(program, "a_srgba").unwrap(); - let mut vertex_array = crate::misc_util::VAO::new(gl, true); + let mut vertex_array = if need_to_emulate_vao { + crate::misc_util::VAO::emulated() + } else { + crate::misc_util::VAO::native(gl) + }; vertex_array.bind_vertex_array(gl); vertex_array.bind_buffer(gl, &vertex_buffer); let stride = std::mem::size_of::() as i32; @@ -176,6 +185,7 @@ impl Painter { egui_texture: None, egui_texture_version: None, is_webgl_1, + is_embedded: matches!(shader_version, ShaderVersion::Es100 | ShaderVersion::Es300), vertex_array, srgb_support, user_textures: Default::default(), @@ -194,7 +204,7 @@ impl Painter { if self.egui_texture_version == Some(texture.version) { return; // No change } - let gamma = if self.is_web && self.post_process.is_none() { + let gamma = if self.is_embedded && self.post_process.is_none() { 1.0 / 2.2 } else { 1.0 @@ -244,7 +254,6 @@ impl Painter { glow::ONE, ); - let [width_in_pixels, height_in_pixels] = inner_size; let width_in_points = width_in_pixels as f32 / pixels_per_point; let height_in_points = height_in_pixels as f32 / pixels_per_point; @@ -301,16 +310,13 @@ impl Painter { for egui::ClippedMesh(clip_rect, mesh) in clipped_meshes { self.paint_mesh(gl, size_in_pixels, pixels_per_point, clip_rect, &mesh); } - self.vertex_array.unbind_vertex_array(gl); unsafe { + self.vertex_array.unbind_vertex_array(gl); gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None); - } - if let Some(ref post_process) = self.post_process { - unsafe { + + if let Some(ref post_process) = self.post_process { post_process.end(gl); } - } - unsafe { assert_eq!(glow::NO_ERROR, gl.get_error(), "GL error occurred!"); } } diff --git a/egui_glow/src/post_process.rs b/egui_glow/src/post_process.rs index 23583621589..4c85ff9d2b1 100644 --- a/egui_glow/src/post_process.rs +++ b/egui_glow/src/post_process.rs @@ -19,6 +19,7 @@ pub(crate) struct PostProcess { impl PostProcess { pub(crate) unsafe fn new( gl: &glow::Context, + need_to_emulate_vao: bool, is_webgl_1: bool, width: i32, height: i32, @@ -119,7 +120,11 @@ impl PostProcess { let a_pos_loc = gl .get_attrib_location(program, "a_pos") .ok_or_else(|| "failed to get location of a_pos".to_string())?; - let mut vertex_array = crate::misc_util::VAO::new(gl, true); + let mut vertex_array = if need_to_emulate_vao { + crate::misc_util::VAO::emulated() + } else { + crate::misc_util::VAO::native(gl) + }; vertex_array.bind_vertex_array(gl); vertex_array.bind_buffer(gl, &pos_buffer); let buffer_info_a_pos = BufferInfo { diff --git a/egui_web/src/backend.rs b/egui_web/src/backend.rs index 66c409a33ea..0953a47f102 100644 --- a/egui_web/src/backend.rs +++ b/egui_web/src/backend.rs @@ -15,10 +15,10 @@ impl WebBackend { pub fn new(canvas_id: &str) -> Result { let ctx = egui::CtxRef::default(); - #[cfg(feature = "use_glow_painter")] + #[cfg(feature = "glow")] let painter: Box = { Box::new(glow_wrapping::WrappedGlowPainter::new(canvas_id)) }; - #[cfg(not(feature = "use_glow_painter"))] + #[cfg(not(feature = "glow"))] let painter: Box = { if let Ok(webgl2_painter) = webgl2::WebGl2Painter::new(canvas_id) { console_log("Using WebGL2 backend"); diff --git a/egui_web/src/lib.rs b/egui_web/src/lib.rs index c430a43ce7e..84292637dfd 100644 --- a/egui_web/src/lib.rs +++ b/egui_web/src/lib.rs @@ -15,7 +15,7 @@ #![warn(clippy::all, missing_crate_level_docs, rust_2018_idioms)] pub mod backend; -#[cfg(feature = "use_glow_painter")] +#[cfg(feature = "glow")] mod glow_wrapping; mod painter; pub mod screen_reader; From 7faa359d146614cab4ba01265d391027e4a6966b Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 22:05:55 +0900 Subject: [PATCH 22/27] report painter initializing error to caller. --- egui_glow/src/lib.rs | 6 +++++- egui_glow/src/painter.rs | 24 ++++++------------------ egui_web/src/glow_wrapping.rs | 12 ++++++++++-- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/egui_glow/src/lib.rs b/egui_glow/src/lib.rs index 6177441710b..361c37d4a47 100644 --- a/egui_glow/src/lib.rs +++ b/egui_glow/src/lib.rs @@ -121,7 +121,11 @@ impl EguiGlow { Self { egui_ctx: Default::default(), egui_winit: egui_winit::State::new(gl_window.window()), - painter: crate::Painter::new(gl, None), + painter: crate::Painter::new(gl, None) + .map_err(|error| { + eprintln!("some error occurred in initializing painter\n{}", error) + }) + .unwrap(), } } diff --git a/egui_glow/src/painter.rs b/egui_glow/src/painter.rs index 530bffccc98..0aa7f0cb3e9 100644 --- a/egui_glow/src/painter.rs +++ b/egui_glow/src/painter.rs @@ -62,7 +62,7 @@ impl Painter { /// Create painter. /// /// Set `pp_fb_extent` to the framebuffer size to enable `sRGB` support on OpenGL ES and WebGL. - pub fn new(gl: &glow::Context, pp_fb_extent: Option<[i32; 2]>) -> Painter { + pub fn new(gl: &glow::Context, pp_fb_extent: Option<[i32; 2]>) -> Result { let need_to_emulate_vao = unsafe { crate::misc_util::need_to_emulate_vao(gl) }; let shader_version = ShaderVersion::get(gl); let is_webgl_1 = shader_version == ShaderVersion::Es100; @@ -104,11 +104,7 @@ impl Painter { shader_version.is_new_shader_interface(), VERT_SRC ), - ) - .map_err(|problems| { - glow_debug_print(format!("failed to compile vertex shader \n {}", problems)); - }) - .unwrap(); + )?; let frag = compile_shader( gl, glow::FRAGMENT_SHADER, @@ -119,16 +115,8 @@ impl Painter { shader_version.is_new_shader_interface(), FRAG_SRC ), - ) - .map_err(|problems| { - glow_debug_print(format!("failed to compile fragment shader \n {}", problems)); - }) - .unwrap(); - let program = link_program(gl, [vert, frag].iter()) - .map_err(|problems| { - glow_debug_print(format!("failed to link shaders \n {}", problems)); - }) - .unwrap(); + )?; + let program = link_program(gl, [vert, frag].iter())?; gl.detach_shader(program, vert); gl.detach_shader(program, frag); gl.delete_shader(vert); @@ -178,7 +166,7 @@ impl Painter { vertex_array.add_new_attribute(gl, color_buffer_info); assert_eq!(gl.get_error(), glow::NO_ERROR, "OpenGL error occurred!"); - Painter { + Ok(Painter { program, u_screen_size, u_sampler, @@ -194,7 +182,7 @@ impl Painter { element_array_buffer, old_textures: Vec::new(), destroyed: false, - } + }) } } diff --git a/egui_web/src/glow_wrapping.rs b/egui_web/src/glow_wrapping.rs index fa38f2c00fd..28e85acfe9a 100644 --- a/egui_web/src/glow_wrapping.rs +++ b/egui_web/src/glow_wrapping.rs @@ -1,4 +1,4 @@ -use crate::canvas_element_or_die; +use crate::{canvas_element_or_die, console_error}; use egui::{ClippedMesh, Rgba, Texture}; use egui_glow::glow; use epi::TextureAllocator; @@ -17,7 +17,15 @@ impl WrappedGlowPainter { let canvas = canvas_element_or_die(canvas_id); let gl_ctx = init_glow_context_from_canvas(&canvas); let dimension = [canvas.width() as i32, canvas.height() as i32]; - let painter = egui_glow::Painter::new(&gl_ctx, Some(dimension)); + let painter = egui_glow::Painter::new(&gl_ctx, Some(dimension)) + .map_err(|error| { + console_error(format!( + "some error occurred in initializing glow painter\n {}", + error + )) + }) + .unwrap(); + Self { gl_ctx, canvas, From a9c8b2643d35e1b5a0764a5e0615fb8a86aa3c21 Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 23:26:05 +0900 Subject: [PATCH 23/27] merge upstream --- egui_glow/src/epi_backend.rs | 8 ++++---- egui_web/src/backend.rs | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/egui_glow/src/epi_backend.rs b/egui_glow/src/epi_backend.rs index f39034a952f..8191801dbf3 100644 --- a/egui_glow/src/epi_backend.rs +++ b/egui_glow/src/epi_backend.rs @@ -60,9 +60,9 @@ pub fn run(app: Box, native_options: &epi::NativeOptions) -> ! { event_loop.create_proxy(), ))); - let mut painter = crate::Painter::new(&gl,None).map_err(|error|{ - eprintln!("some OpenGL error occurred {}\n",error) - }).unwrap(); + 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(), @@ -96,7 +96,7 @@ pub fn run(app: Box, 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.upload_egui_texture(&gl, &integration.egui_ctx.texture()); painter.paint_meshes( gl_window.window().inner_size().into(), &gl, diff --git a/egui_web/src/backend.rs b/egui_web/src/backend.rs index 9aae59451fd..cc63f725b02 100644 --- a/egui_web/src/backend.rs +++ b/egui_web/src/backend.rs @@ -6,7 +6,9 @@ pub use egui::{pos2, Color32}; fn create_painter(canvas_id: &str) -> Result, JsValue> { #[cfg(feature = "glow")] - return Ok(Box::new(crate::glow_wrapping::WrappedGlowPainter::new(canvas_id))); + return Ok(Box::new(crate::glow_wrapping::WrappedGlowPainter::new( + canvas_id, + ))); #[cfg(not(feature = "glow"))] if let Ok(webgl2_painter) = webgl2::WebGl2Painter::new(canvas_id) { console_log("Using WebGL2 backend"); From efcadb51270d6e4a26464941ebc2831201936135 Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 23:43:08 +0900 Subject: [PATCH 24/27] workaround for cargo check --all-target --all-feature glow does not provide dummy from_webgl_context for native --- egui_web/src/backend.rs | 6 +++++- egui_web/src/lib.rs | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/egui_web/src/backend.rs b/egui_web/src/backend.rs index cc63f725b02..2812e7c67a8 100644 --- a/egui_web/src/backend.rs +++ b/egui_web/src/backend.rs @@ -5,10 +5,14 @@ pub use egui::{pos2, Color32}; // ---------------------------------------------------------------------------- fn create_painter(canvas_id: &str) -> Result, JsValue> { - #[cfg(feature = "glow")] + //glow does not provide dummy from_webgl_context to native + //so need to disable this code + #[cfg(all(feature = "glow", target_arch = "wasm32"))] return Ok(Box::new(crate::glow_wrapping::WrappedGlowPainter::new( canvas_id, ))); + #[cfg(all(feature = "glow", not(target_arch = "wasm32")))] + return Result::Err(JsValue::from_str("something to give linter")); #[cfg(not(feature = "glow"))] if let Ok(webgl2_painter) = webgl2::WebGl2Painter::new(canvas_id) { console_log("Using WebGL2 backend"); diff --git a/egui_web/src/lib.rs b/egui_web/src/lib.rs index c5c9ecf1142..4392d13ccfa 100644 --- a/egui_web/src/lib.rs +++ b/egui_web/src/lib.rs @@ -15,7 +15,7 @@ #![warn(clippy::all, missing_crate_level_docs, rust_2018_idioms)] pub mod backend; -#[cfg(feature = "glow")] +#[cfg(all(feature = "glow", target_arch = "wasm32"))] mod glow_wrapping; mod painter; pub mod screen_reader; From 58ab9e6a475a4380860f911df4277da7af67331a Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 23:56:03 +0900 Subject: [PATCH 25/27] fix docstring --- egui_glow/src/painter.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/egui_glow/src/painter.rs b/egui_glow/src/painter.rs index 0aa7f0cb3e9..f92be73182a 100644 --- a/egui_glow/src/painter.rs +++ b/egui_glow/src/painter.rs @@ -62,6 +62,11 @@ impl Painter { /// Create painter. /// /// Set `pp_fb_extent` to the framebuffer size to enable `sRGB` support on OpenGL ES and WebGL. + /// # Errors + /// will return `Err` below cases + /// * failed to compile shader + /// * failed to create postprocess on webgl with `sRGB` support + /// * failed to create buffer pub fn new(gl: &glow::Context, pp_fb_extent: Option<[i32; 2]>) -> Result { let need_to_emulate_vao = unsafe { crate::misc_util::need_to_emulate_vao(gl) }; let shader_version = ShaderVersion::get(gl); @@ -71,23 +76,26 @@ impl Painter { let srgb_support = gl.supported_extensions().contains("EXT_sRGB"); let (post_process, srgb_support_define) = match (shader_version, srgb_support) { //WebGL2 support sRGB default - (ShaderVersion::Es300, _) | (ShaderVersion::Es100, true) => { + (ShaderVersion::Es300, _) | (ShaderVersion::Es100, true) => unsafe { //Add sRGB support marker for fragment shader if let Some([width, height]) = pp_fb_extent { glow_debug_print("WebGL with sRGB enabled so turn on post process"); //install post process to correct sRGB color ( - unsafe { - PostProcess::new(gl, need_to_emulate_vao, is_webgl_1, width, height) - } - .ok(), + Some(PostProcess::new( + gl, + need_to_emulate_vao, + is_webgl_1, + width, + height, + )?), "#define SRGB_SUPPORTED", ) } else { glow_debug_print("WebGL or OpenGL ES detected but PostProcess disabled because dimension is None"); (None, "") } - } + }, //WebGL1 without sRGB support disable postprocess and use fallback shader (ShaderVersion::Es100, false) => (None, ""), //OpenGL 2.1 or above always support sRGB so add sRGB support marker @@ -123,8 +131,8 @@ impl Painter { gl.delete_shader(frag); let u_screen_size = gl.get_uniform_location(program, "u_screen_size").unwrap(); let u_sampler = gl.get_uniform_location(program, "u_sampler").unwrap(); - let vertex_buffer = gl.create_buffer().unwrap(); - let element_array_buffer = gl.create_buffer().unwrap(); + let vertex_buffer = gl.create_buffer()?; + let element_array_buffer = gl.create_buffer()?; gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buffer)); let a_pos_loc = gl.get_attrib_location(program, "a_pos").unwrap(); let a_tc_loc = gl.get_attrib_location(program, "a_tc").unwrap(); From c496e92fa71c99397f0aeeb0ae877842563d829f Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Wed, 3 Nov 2021 23:58:01 +0900 Subject: [PATCH 26/27] fix unfixed clippy issue --- egui_glow/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/egui_glow/src/lib.rs b/egui_glow/src/lib.rs index 970a709d363..4f10e579efb 100644 --- a/egui_glow/src/lib.rs +++ b/egui_glow/src/lib.rs @@ -123,7 +123,7 @@ impl EguiGlow { egui_winit: egui_winit::State::new(gl_window.window()), painter: crate::Painter::new(gl, None) .map_err(|error| { - eprintln!("some error occurred in initializing painter\n{}", error) + eprintln!("some error occurred in initializing painter\n{}", error); }) .unwrap(), } From 385c1fb6cdd14b18aca27d81309c43c994e25c7b Mon Sep 17 00:00:00 2001 From: triangle drawer <48007646+t18b219k@users.noreply.github.com> Date: Thu, 4 Nov 2021 00:14:23 +0900 Subject: [PATCH 27/27] check glow backend build --- egui_web/src/backend.rs | 6 +----- egui_web/src/glow_wrapping.rs | 18 ++++++++++++++++++ egui_web/src/lib.rs | 2 +- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/egui_web/src/backend.rs b/egui_web/src/backend.rs index 2812e7c67a8..cc63f725b02 100644 --- a/egui_web/src/backend.rs +++ b/egui_web/src/backend.rs @@ -5,14 +5,10 @@ pub use egui::{pos2, Color32}; // ---------------------------------------------------------------------------- fn create_painter(canvas_id: &str) -> Result, JsValue> { - //glow does not provide dummy from_webgl_context to native - //so need to disable this code - #[cfg(all(feature = "glow", target_arch = "wasm32"))] + #[cfg(feature = "glow")] return Ok(Box::new(crate::glow_wrapping::WrappedGlowPainter::new( canvas_id, ))); - #[cfg(all(feature = "glow", not(target_arch = "wasm32")))] - return Result::Err(JsValue::from_str("something to give linter")); #[cfg(not(feature = "glow"))] if let Ok(webgl2_painter) = webgl2::WebGl2Painter::new(canvas_id) { console_log("Using WebGL2 backend"); diff --git a/egui_web/src/glow_wrapping.rs b/egui_web/src/glow_wrapping.rs index 28e85acfe9a..f9512a5830d 100644 --- a/egui_web/src/glow_wrapping.rs +++ b/egui_web/src/glow_wrapping.rs @@ -1,3 +1,4 @@ +use crate::web_sys::{WebGl2RenderingContext, WebGlRenderingContext}; use crate::{canvas_element_or_die, console_error}; use egui::{ClippedMesh, Rgba, Texture}; use egui_glow::glow; @@ -110,3 +111,20 @@ pub fn init_glow_context_from_canvas(canvas: &HtmlCanvasElement) -> glow::Contex panic!("tried webgl2 but something went wrong"); } } + +trait DummyWebGLConstructor { + fn from_webgl1_context(context: web_sys::WebGlRenderingContext) -> Self; + + fn from_webgl2_context(context: web_sys::WebGl2RenderingContext) -> Self; +} + +#[cfg(not(target_arch = "wasm32"))] +impl DummyWebGLConstructor for glow::Context { + fn from_webgl1_context(_context: WebGlRenderingContext) -> Self { + panic!("you cant use egui_web(glow) on native") + } + + fn from_webgl2_context(_context: WebGl2RenderingContext) -> Self { + panic!("you cant use egui_web(glow) on native") + } +} diff --git a/egui_web/src/lib.rs b/egui_web/src/lib.rs index 4392d13ccfa..c5c9ecf1142 100644 --- a/egui_web/src/lib.rs +++ b/egui_web/src/lib.rs @@ -15,7 +15,7 @@ #![warn(clippy::all, missing_crate_level_docs, rust_2018_idioms)] pub mod backend; -#[cfg(all(feature = "glow", target_arch = "wasm32"))] +#[cfg(feature = "glow")] mod glow_wrapping; mod painter; pub mod screen_reader;