From 6b98a4a2d6610c9ad6ad08e1144f298446f91f2d Mon Sep 17 00:00:00 2001 From: David Cohen Date: Sat, 9 Mar 2024 11:39:32 -0600 Subject: [PATCH 1/7] updated egui to 0.26.2 and sdl2 to 0.36 Signed-off-by: David Cohen --- .vscode/settings.json | 5 ++++- Cargo.toml | 8 ++++---- examples/basic.rs | 10 ++++++++-- examples/demo_lib.rs | 11 +++++++++-- examples/mix/main.rs | 11 +++++++++-- src/lib.rs | 19 ++++++++++++++----- 6 files changed, 48 insertions(+), 16 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index cad7657..f663e8d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,6 @@ { - "cmake.configureOnOpen": false + "cmake.configureOnOpen": false, + "rust-analyzer.cargo.features": [ + "use_epi" + ] } \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index ef64280..c8061ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "egui_sdl2_gl" -version = "0.23.0" +version = "0.26.2" authors = ["Arjun Nair "] edition = "2018" description = "Backend for Egui to use with sdl2-rs and open gl" @@ -15,8 +15,8 @@ include = ["**/*.rs", "Cargo.toml"] [dependencies] ahash = "~0.8" gl = "~0.14" -egui = "~0.23" -sdl2 = { version = "~0.35" } +egui = "~0.26" +sdl2 = { version = "~0.36" } [dependencies.epi] version = "0.17" @@ -40,4 +40,4 @@ sdl2_static-link = ["sdl2/static-link"] use_epi = ["epi"] [dev-dependencies] -egui_demo_lib = "0.23.0" +egui_demo_lib = "~0.26" diff --git a/examples/basic.rs b/examples/basic.rs index fa7df6a..b33d0d5 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -90,9 +90,10 @@ fn main() { let FullOutput { platform_output, - repaint_after, textures_delta, shapes, + pixels_per_point, + viewport_output, } = egui_ctx.end_frame(); // Process ouput @@ -106,10 +107,15 @@ fn main() { // window.set_size(w, h).unwrap(); // } - let paint_jobs = egui_ctx.tessellate(shapes); + let paint_jobs = egui_ctx.tessellate(shapes, pixels_per_point); painter.paint_jobs(None, textures_delta, paint_jobs); window.gl_swap_window(); + let repaint_after = viewport_output + .get(&egui::ViewportId::ROOT) + .expect("Missing ViewportId::ROOT") + .repaint_delay; + if !repaint_after.is_zero() { if let Some(event) = event_pump.wait_event_timeout(5) { match event { diff --git a/examples/demo_lib.rs b/examples/demo_lib.rs index 1772910..e08d124 100644 --- a/examples/demo_lib.rs +++ b/examples/demo_lib.rs @@ -1,6 +1,7 @@ #[cfg(not(feature = "use_epi"))] compile_error!("feature \"use_epi\" must be used"); +use egui::ViewportId; use egui_backend::{ egui::{self, FullOutput}, epi::{Frame, IntegrationInfo}, @@ -84,9 +85,10 @@ fn main() { let FullOutput { platform_output, - repaint_after, textures_delta, shapes, + pixels_per_point, + viewport_output, } = egui_ctx.end_frame(); // Process ouput egui_state.process_output(&window, &platform_output); @@ -95,6 +97,11 @@ fn main() { break 'running; } + let repaint_after = viewport_output + .get(&ViewportId::ROOT) + .expect("Missing ViewportId::ROOT") + .repaint_delay; + if !repaint_after.is_zero() { // Reactive every 1 second. if let Some(event) = event_pump.wait_event_timeout(1000) { @@ -126,7 +133,7 @@ fn main() { // window.set_size(w, h).unwrap(); // } - let paint_jobs = egui_ctx.tessellate(shapes); + let paint_jobs = egui_ctx.tessellate(shapes, pixels_per_point); // An example of how OpenGL can be used to draw custom stuff with egui // overlaying it: diff --git a/examples/mix/main.rs b/examples/mix/main.rs index 0e6f59b..36bf238 100644 --- a/examples/mix/main.rs +++ b/examples/mix/main.rs @@ -2,6 +2,7 @@ use std::time::Instant; //Alias the backend to something less mouthful use egui::load::SizedTexture; +use egui::ViewportId; use egui_backend::egui::{vec2, Color32, FullOutput, Image}; use egui_backend::sdl2::video::GLProfile; use egui_backend::{egui, gl, sdl2}; @@ -139,14 +140,15 @@ fn main() { let FullOutput { platform_output, - repaint_after, textures_delta, shapes, + pixels_per_point, + viewport_output, } = egui_ctx.end_frame(); // Process output egui_state.process_output(&window, &platform_output); - let paint_jobs = egui_ctx.tessellate(shapes); + let paint_jobs = egui_ctx.tessellate(shapes, pixels_per_point); // Note: passing a bg_color to paint_jobs will clear any previously drawn stuff. // Use this only if egui is being used for all drawing and you aren't mixing your own Open GL @@ -156,6 +158,11 @@ fn main() { window.gl_swap_window(); + let repaint_after = viewport_output + .get(&ViewportId::ROOT) + .expect("Missing ViewportId::ROOT") + .repaint_delay; + if !repaint_after.is_zero() { if let Some(event) = event_pump.wait_event_timeout(5) { match event { diff --git a/src/lib.rs b/src/lib.rs index 680aecc..635000e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,15 +107,20 @@ pub fn with_sdl2( impl EguiStateHandler { pub fn new(painter: Painter) -> (Painter, EguiStateHandler) { + let mut input = egui::RawInput { + screen_rect: Some(painter.screen_rect), + ..Default::default() + }; + input + .viewports + .entry(ViewportId::ROOT) + .or_default() + .native_pixels_per_point = Some(painter.pixels_per_point); let native_pixels_per_point = painter.pixels_per_point; let _self = EguiStateHandler { fused_cursor: FusedCursor::default(), pointer_pos: Pos2::new(0f32, 0f32), - input: egui::RawInput { - screen_rect: Some(painter.screen_rect), - pixels_per_point: Some(native_pixels_per_point), - ..Default::default() - }, + input, modifiers: Modifiers::default(), native_pixels_per_point, }; @@ -251,6 +256,8 @@ pub fn input_to_egui( pressed: false, repeat, modifiers: state.modifiers, + // TODO: implement support for physical_key + physical_key: None, }); } @@ -288,6 +295,8 @@ pub fn input_to_egui( pressed: true, repeat, modifiers: state.modifiers, + // TODO: implement support for physical_key + physical_key: None, }); if state.modifiers.command && key == Key::C { From c1cddccbfdd9f90a8203663bd2d1f8a7fbd5a839 Mon Sep 17 00:00:00 2001 From: David Cohen Date: Sat, 9 Mar 2024 12:50:38 -0600 Subject: [PATCH 2/7] examples: windos.subsystem().gl_set_swap_inverval() error is not fatal Signed-off-by: David Cohen --- examples/basic.rs | 31 +++++++++++++++++++------------ examples/demo_lib.rs | 10 ++++++---- examples/mix/main.rs | 10 ++++++---- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/examples/basic.rs b/examples/basic.rs index b33d0d5..bf484e1 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -51,21 +51,28 @@ fn main() { let mut quit = false; let mut slider = 0.0; + if enable_vsync { + if let Err(error) = window.subsystem().gl_set_swap_interval(SwapInterval::VSync) { + println!( + "Failed to gl_set_swap_interval(SwapInterval::VSync): {}", + error + ); + } + } else { + if let Err(error) = window + .subsystem() + .gl_set_swap_interval(SwapInterval::Immediate) + { + println!( + "Failed to gl_set_swap_interval(SwapInterval::Immediate): {}", + error + ); + } + } + let start_time = Instant::now(); 'running: loop { - if enable_vsync { - window - .subsystem() - .gl_set_swap_interval(SwapInterval::VSync) - .unwrap() - } else { - window - .subsystem() - .gl_set_swap_interval(SwapInterval::Immediate) - .unwrap() - } - unsafe { // Clear the screen to green gl::ClearColor(0.3, 0.6, 0.3, 1.0); diff --git a/examples/demo_lib.rs b/examples/demo_lib.rs index e08d124..78f5df0 100644 --- a/examples/demo_lib.rs +++ b/examples/demo_lib.rs @@ -50,10 +50,12 @@ fn main() { debug_assert_eq!(gl_attr.context_version(), (3, 2)); // Enable vsync - window - .subsystem() - .gl_set_swap_interval(SwapInterval::VSync) - .unwrap(); + if let Err(error) = window.subsystem().gl_set_swap_interval(SwapInterval::VSync) { + println!( + "Failed to gl_set_swap_interval(SwapInterval::VSync): {}", + error + ); + } // Init egui stuff let (mut painter, mut egui_state) = diff --git a/examples/mix/main.rs b/examples/mix/main.rs index 36bf238..5bef0dd 100644 --- a/examples/mix/main.rs +++ b/examples/mix/main.rs @@ -49,10 +49,12 @@ fn main() { debug_assert_eq!(gl_attr.context_version(), (3, 2)); // Enable vsync - window - .subsystem() - .gl_set_swap_interval(SwapInterval::VSync) - .unwrap(); + if let Err(error) = window.subsystem().gl_set_swap_interval(SwapInterval::VSync) { + println!( + "Failed to gl_set_swap_interval(SwapInterval::VSync): {}", + error + ); + }; // Init egui stuff let (mut painter, mut egui_state) = From 77c2100ace8fca20b856a7375d3d2c329554c679 Mon Sep 17 00:00:00 2001 From: David Cohen Date: Tue, 12 Mar 2024 20:40:35 -0500 Subject: [PATCH 3/7] move shaders to their own files under src/shader/ Signed-off-by: David Cohen --- src/painter.rs | 205 +++------------------------------------ src/shader/adaptive.frag | 73 ++++++++++++++ src/shader/adaptive.vert | 38 ++++++++ src/shader/default.frag | 29 ++++++ src/shader/default.vert | 26 +++++ 5 files changed, 178 insertions(+), 193 deletions(-) create mode 100644 src/shader/adaptive.frag create mode 100644 src/shader/adaptive.vert create mode 100644 src/shader/default.frag create mode 100644 src/shader/default.vert diff --git a/src/painter.rs b/src/painter.rs index 2481fc2..673150e 100644 --- a/src/painter.rs +++ b/src/painter.rs @@ -12,6 +12,11 @@ use egui::{ use gl::types::{GLchar, GLenum, GLint, GLsizeiptr, GLsync, GLuint}; use std::ffi::CString; +const DEFAULT_VERT_SRC: &str = include_str!("shader/default.vert"); +const DEFAULT_FRAG_SRC: &str = include_str!("shader/default.frag"); +const ADAPTIVE_VERT_SRC: &str = include_str!("shader/adaptive.vert"); +const ADAPTIVE_FRAG_SRC: &str = include_str!("shader/adaptive.frag"); + #[derive(Default)] struct Texture { size: (usize, usize), @@ -32,193 +37,6 @@ struct Texture { dirty: bool, } -const VS_SRC_150: &str = r#" - #version 150 - uniform vec2 u_screen_size; - in vec2 a_pos; - in vec4 a_srgba; // 0-255 sRGB - in vec2 a_tc; - out vec4 v_rgba; - out vec2 v_tc; - - // 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, cutoff); - } - - vec4 linear_from_srgba(vec4 srgba) { - return vec4(linear_from_srgb(srgba.rgb), srgba.a / 255.0); - } - - void main() { - gl_Position = vec4( - 2.0 * a_pos.x / u_screen_size.x - 1.0, - 1.0 - 2.0 * a_pos.y / u_screen_size.y, - 0.0, - 1.0); - v_rgba = linear_from_srgba(a_srgba); - v_tc = a_tc; - } -"#; - -const FS_SRC_150: &str = r#" - #version 150 - uniform sampler2D u_sampler; - in vec4 v_rgba; - in vec2 v_tc; - out vec4 f_color; - - // 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)); - } - - vec4 srgba_from_linear(vec4 rgba) { - return vec4(srgb_from_linear(rgba.rgb), 255.0 * rgba.a); - } - - 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)); - } - - vec4 linear_from_srgba(vec4 srgba) { - return vec4(linear_from_srgb(srgba.rgb), srgba.a / 255.0); - } - - void main() { - // Need to convert from SRGBA to linear. - vec4 texture_rgba = linear_from_srgba(texture(u_sampler, v_tc) * 255.0); - f_color = v_rgba * texture_rgba; - } -"#; - -// VS_SRC and FS_SRC shaders taken from egui_glow crate. -const VS_SRC: &str = r#" -#if !defined(GL_ES) && __VERSION__ >= 140 -#define I in -#define O out -#define V(x) x -#else -#define I attribute -#define O varying -#define V(x) vec3(x) -#endif - -#ifdef GL_ES -precision mediump float; -#endif -uniform vec2 u_screen_size; -I vec2 a_pos; -I vec4 a_srgba; // 0-255 sRGB -I vec2 a_tc; -O vec4 v_rgba; -O vec2 v_tc; - -// 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, V(cutoff)); -} - -vec4 linear_from_srgba(vec4 srgba) { - return vec4(linear_from_srgb(srgba.rgb), srgba.a / 255.0); -} - -void main() { - gl_Position = vec4(2.0 * a_pos.x / u_screen_size.x - 1.0, 1.0 - 2.0 * a_pos.y / u_screen_size.y, 0.0, 1.0); - // egui encodes vertex colors in gamma spaces, so we must decode the colors here: - v_rgba = linear_from_srgba(a_srgba); - v_tc = a_tc; -} -"#; - -const FS_SRC: &str = r#" -#ifdef GL_ES -precision mediump float; -#endif - -uniform sampler2D u_sampler; -#if defined(GL_ES) || __VERSION__ < 140 -varying vec4 v_rgba; -varying vec2 v_tc; -#else -in vec4 v_rgba; -in vec2 v_tc; -out vec4 f_color; -#endif - -#ifdef GL_ES -// 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)); -} - -vec4 srgba_from_linear(vec4 rgba) { - 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)); -} - -vec4 linear_from_srgba(vec4 srgba) { - 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 - - /// Multiply vertex color with texture color (in linear space). - gl_FragColor = v_rgba * texture_rgba; - - // 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; - - // 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 -} -#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 -} -#endif -"#; - pub struct Painter { vertex_array: GLuint, program: GLuint, @@ -310,13 +128,14 @@ impl Painter { gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32); - let (vs_src, fs_src) = if let ShaderVersion::Default = shader_ver { - (VS_SRC_150, FS_SRC_150) - } else { - (VS_SRC, FS_SRC) + let (vs_src, fs_src) = match shader_ver { + ShaderVersion::Default => (DEFAULT_VERT_SRC, DEFAULT_FRAG_SRC), + ShaderVersion::Adaptive => (ADAPTIVE_VERT_SRC, ADAPTIVE_FRAG_SRC), }; - let vert_shader = compile_shader(vs_src, gl::VERTEX_SHADER); - let frag_shader = compile_shader(fs_src, gl::FRAGMENT_SHADER); + let vs_src = CString::new(vs_src).unwrap().to_string_lossy().to_string(); + let fs_src = CString::new(fs_src).unwrap().to_string_lossy().to_string(); + let vert_shader = compile_shader(&vs_src, gl::VERTEX_SHADER); + let frag_shader = compile_shader(&fs_src, gl::FRAGMENT_SHADER); let program = link_program(vert_shader, frag_shader); let mut vertex_array = 0; diff --git a/src/shader/adaptive.frag b/src/shader/adaptive.frag new file mode 100644 index 0000000..e21cffc --- /dev/null +++ b/src/shader/adaptive.frag @@ -0,0 +1,73 @@ +#ifdef GL_ES +precision mediump float; +#endif + +uniform sampler2D u_sampler; +#if defined(GL_ES) || __VERSION__ < 140 +varying vec4 v_rgba; +varying vec2 v_tc; +#else +in vec4 v_rgba; +in vec2 v_tc; +out vec4 f_color; +#endif + +#ifdef GL_ES +// 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)); +} + +vec4 srgba_from_linear(vec4 rgba) { + 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)); +} + +vec4 linear_from_srgba(vec4 srgba) { + 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 + + /// Multiply vertex color with texture color (in linear space). + gl_FragColor = v_rgba * texture_rgba; + + // 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; + + // 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 +} +#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 +} +#endif \ No newline at end of file diff --git a/src/shader/adaptive.vert b/src/shader/adaptive.vert new file mode 100644 index 0000000..31d797a --- /dev/null +++ b/src/shader/adaptive.vert @@ -0,0 +1,38 @@ +#if !defined(GL_ES) && __VERSION__ >= 140 +#define I in +#define O out +#define V(x) x +#else +#define I attribute +#define O varying +#define V(x) vec3(x) +#endif + +#ifdef GL_ES +precision mediump float; +#endif +uniform vec2 u_screen_size; +I vec2 a_pos; +I vec4 a_srgba; // 0-255 sRGB +I vec2 a_tc; +O vec4 v_rgba; +O vec2 v_tc; + +// 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, V(cutoff)); +} + +vec4 linear_from_srgba(vec4 srgba) { + return vec4(linear_from_srgb(srgba.rgb), srgba.a / 255.0); +} + +void main() { + gl_Position = vec4(2.0 * a_pos.x / u_screen_size.x - 1.0, 1.0 - 2.0 * a_pos.y / u_screen_size.y, 0.0, 1.0); + // egui encodes vertex colors in gamma spaces, so we must decode the colors here: + v_rgba = linear_from_srgba(a_srgba); + v_tc = a_tc; +} \ No newline at end of file diff --git a/src/shader/default.frag b/src/shader/default.frag new file mode 100644 index 0000000..28c29a1 --- /dev/null +++ b/src/shader/default.frag @@ -0,0 +1,29 @@ +#version 150 +uniform sampler2D u_sampler; +in vec4 v_rgba; +in vec2 v_tc; +out vec4 f_color; +// 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)); +} +vec4 srgba_from_linear(vec4 rgba) { + return vec4(srgb_from_linear(rgba.rgb), 255.0 * rgba.a); +} +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)); +} +vec4 linear_from_srgba(vec4 srgba) { + return vec4(linear_from_srgb(srgba.rgb), srgba.a / 255.0); +} +void main() { + // Need to convert from SRGBA to linear. + vec4 texture_rgba = linear_from_srgba(texture(u_sampler, v_tc) * 255.0); + f_color = v_rgba * texture_rgba; +} \ No newline at end of file diff --git a/src/shader/default.vert b/src/shader/default.vert new file mode 100644 index 0000000..90a7923 --- /dev/null +++ b/src/shader/default.vert @@ -0,0 +1,26 @@ +#version 150 +uniform vec2 u_screen_size; +in vec2 a_pos; +in vec4 a_srgba; // 0-255 sRGB +in vec2 a_tc; +out vec4 v_rgba; +out vec2 v_tc; +// 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, cutoff); +} +vec4 linear_from_srgba(vec4 srgba) { + return vec4(linear_from_srgb(srgba.rgb), srgba.a / 255.0); +} +void main() { + gl_Position = vec4( + 2.0 * a_pos.x / u_screen_size.x - 1.0, + 1.0 - 2.0 * a_pos.y / u_screen_size.y, + 0.0, + 1.0); + v_rgba = linear_from_srgba(a_srgba); + v_tc = a_tc; +} \ No newline at end of file From f1933b31fecffd4ec07a596ab318dd6fa445bc60 Mon Sep 17 00:00:00 2001 From: David Cohen Date: Tue, 12 Mar 2024 20:41:04 -0500 Subject: [PATCH 4/7] rename feature 'default-features' to 'default' Signed-off-by: David Cohen --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c8061ce..f71c1c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ version = "0.17" optional = true [features] -default-features = ["sdl2_bundled"] +default = ["sdl2_bundled"] sdl2_unsafe_textures = ["sdl2/unsafe_textures"] sdl2_gfx = ["sdl2/gfx"] From 3c33b62fb79610e0632074f7820175b990fa9d9f Mon Sep 17 00:00:00 2001 From: David Cohen Date: Tue, 12 Mar 2024 21:17:38 -0500 Subject: [PATCH 5/7] do not rework egui's vertices and indices egui sends vertices and indices ready to be sent to the shader. Reworking them is unnecessary and wastes CPU cycles each frame. Signed-off-by: David Cohen --- Cargo.toml | 1 + src/painter.rs | 112 ++++++++++++++++--------------------------------- 2 files changed, 36 insertions(+), 77 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f71c1c7..29f7086 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ ahash = "~0.8" gl = "~0.14" egui = "~0.26" sdl2 = { version = "~0.36" } +memoffset = "0.9.0" [dependencies.epi] version = "0.17" diff --git a/src/painter.rs b/src/painter.rs index 673150e..605d9fe 100644 --- a/src/painter.rs +++ b/src/painter.rs @@ -10,6 +10,7 @@ use egui::{ vec2, ClippedPrimitive, Pos2, Rect, }; use gl::types::{GLchar, GLenum, GLint, GLsizeiptr, GLsync, GLuint}; +use std::convert::TryInto; use std::ffi::CString; const DEFAULT_VERT_SRC: &str = include_str!("shader/default.vert"); @@ -41,9 +42,7 @@ pub struct Painter { vertex_array: GLuint, program: GLuint, index_buffer: GLuint, - pos_buffer: GLuint, - tc_buffer: GLuint, - color_buffer: GLuint, + vertex_buffer: GLuint, // Call fence for sdl vsync so the CPU won't heat up if there's no heavy activity. pub gl_sync_fence: GLsync, textures: AHashMap, @@ -140,15 +139,14 @@ impl Painter { let program = link_program(vert_shader, frag_shader); let mut vertex_array = 0; let mut index_buffer = 0; - let mut pos_buffer = 0; - let mut tc_buffer = 0; - let mut color_buffer = 0; + let mut vertex_buffer = 0; gl::GenVertexArrays(1, &mut vertex_array); gl::BindVertexArray(vertex_array); + assert!(vertex_array > 0); gl::GenBuffers(1, &mut index_buffer); - gl::GenBuffers(1, &mut pos_buffer); - gl::GenBuffers(1, &mut tc_buffer); - gl::GenBuffers(1, &mut color_buffer); + assert!(index_buffer > 0); + gl::GenBuffers(1, &mut vertex_buffer); + assert!(vert_shader > 0); let (width, height) = window.size(); let pixels_per_point = scale; @@ -163,9 +161,7 @@ impl Painter { vertex_array, program, index_buffer, - pos_buffer, - tc_buffer, - color_buffer, + vertex_buffer, gl_sync_fence: gl::FenceSync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0), pixels_per_point, textures: Default::default(), @@ -390,9 +386,7 @@ impl Painter { } gl::DeleteProgram(self.program); - gl::DeleteBuffers(1, &self.pos_buffer); - gl::DeleteBuffers(1, &self.tc_buffer); - gl::DeleteBuffers(1, &self.color_buffer); + gl::DeleteBuffers(1, &self.vertex_buffer); gl::DeleteBuffers(1, &self.index_buffer); gl::DeleteVertexArrays(1, &self.vertex_array); } @@ -494,10 +488,8 @@ impl Painter { fn paint_mesh(&self, mesh: &Mesh) { debug_assert!(mesh.is_valid()); unsafe { - let indices: Vec = mesh.indices.iter().map(move |idx| *idx as u16).collect(); - let indices_len = indices.len(); + let indices = &mesh.indices; let vertices = &mesh.vertices; - let vertices_len = vertices.len(); // -------------------------------------------------------------------- @@ -505,35 +497,21 @@ impl Painter { gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.index_buffer); gl::BufferData( gl::ELEMENT_ARRAY_BUFFER, - (indices_len * mem::size_of::()) as GLsizeiptr, - //mem::transmute(&indices.as_ptr()), + (indices.len() * mem::size_of::()) as GLsizeiptr, indices.as_ptr() as *const gl::types::GLvoid, gl::STREAM_DRAW, ); // -------------------------------------------------------------------- - let mut positions: Vec = Vec::with_capacity(2 * vertices_len); - let mut tex_coords: Vec = Vec::with_capacity(2 * vertices_len); - { - for v in &mesh.vertices { - positions.push(v.pos.x); - positions.push(v.pos.y); - tex_coords.push(v.uv.x); - tex_coords.push(v.uv.y); - } - } - - // -------------------------------------------------------------------- - - gl::BindBuffer(gl::ARRAY_BUFFER, self.pos_buffer); + gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer); gl::BufferData( gl::ARRAY_BUFFER, - (positions.len() * mem::size_of::()) as GLsizeiptr, - //mem::transmute(&positions.as_ptr()), - positions.as_ptr() as *const gl::types::GLvoid, + (vertices.len() * mem::size_of::()) as GLsizeiptr, + vertices.as_ptr() as *const gl::types::GLvoid, gl::STREAM_DRAW, ); + let stride: i32 = mem::size_of::().try_into().unwrap(); let a_pos = CString::new("a_pos").unwrap(); let a_pos_ptr = a_pos.as_ptr(); @@ -541,20 +519,15 @@ impl Painter { assert!(a_pos_loc >= 0); let a_pos_loc = a_pos_loc as u32; - let stride = 0; - gl::VertexAttribPointer(a_pos_loc, 2, gl::FLOAT, gl::FALSE, stride, ptr::null()); - gl::EnableVertexAttribArray(a_pos_loc); - - // -------------------------------------------------------------------- - - gl::BindBuffer(gl::ARRAY_BUFFER, self.tc_buffer); - gl::BufferData( - gl::ARRAY_BUFFER, - (tex_coords.len() * mem::size_of::()) as GLsizeiptr, - //mem::transmute(&tex_coords.as_ptr()), - tex_coords.as_ptr() as *const gl::types::GLvoid, - gl::STREAM_DRAW, + gl::VertexAttribPointer( + a_pos_loc, + 2, + gl::FLOAT, + gl::FALSE, + stride, + memoffset::offset_of!(egui::epaint::Vertex, pos) as *const _, ); + gl::EnableVertexAttribArray(a_pos_loc); let a_tc = CString::new("a_tc").unwrap(); let a_tc_ptr = a_tc.as_ptr(); @@ -562,30 +535,15 @@ impl Painter { assert!(a_tc_loc >= 0); let a_tc_loc = a_tc_loc as u32; - let stride = 0; - gl::VertexAttribPointer(a_tc_loc, 2, gl::FLOAT, gl::FALSE, stride, ptr::null()); - gl::EnableVertexAttribArray(a_tc_loc); - - // -------------------------------------------------------------------- - - let mut colors: Vec = Vec::with_capacity(4 * vertices_len); - { - for v in vertices { - colors.push(v.color[0]); - colors.push(v.color[1]); - colors.push(v.color[2]); - colors.push(v.color[3]); - } - } - - gl::BindBuffer(gl::ARRAY_BUFFER, self.color_buffer); - gl::BufferData( - gl::ARRAY_BUFFER, - (colors.len() * mem::size_of::()) as GLsizeiptr, - //mem::transmute(&colors.as_ptr()), - colors.as_ptr() as *const gl::types::GLvoid, - gl::STREAM_DRAW, + gl::VertexAttribPointer( + a_tc_loc, + 2, + gl::FLOAT, + gl::FALSE, + stride, + memoffset::offset_of!(egui::epaint::Vertex, uv) as *const _, ); + gl::EnableVertexAttribArray(a_tc_loc); let a_srgba = CString::new("a_srgba").unwrap(); let a_srgba_ptr = a_srgba.as_ptr(); @@ -593,22 +551,22 @@ impl Painter { assert!(a_srgba_loc >= 0); let a_srgba_loc = a_srgba_loc as u32; - let stride = 0; gl::VertexAttribPointer( a_srgba_loc, 4, gl::UNSIGNED_BYTE, gl::FALSE, stride, - ptr::null(), + memoffset::offset_of!(egui::epaint::Vertex, color) as *const _, ); gl::EnableVertexAttribArray(a_srgba_loc); // -------------------------------------------------------------------- + gl::DrawElements( gl::TRIANGLES, - indices_len as i32, - gl::UNSIGNED_SHORT, + indices.len() as i32, + gl::UNSIGNED_INT, ptr::null(), ); gl::DisableVertexAttribArray(a_pos_loc); From 1208224310457adcc850edb334f7b80a1040e8fa Mon Sep 17 00:00:00 2001 From: David Cohen Date: Tue, 12 Mar 2024 21:48:12 -0500 Subject: [PATCH 6/7] use a common code to retrieve error logs from shaders and program Signed-off-by: David Cohen --- src/painter.rs | 58 ++++++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/src/painter.rs b/src/painter.rs index 605d9fe..ee867e0 100644 --- a/src/painter.rs +++ b/src/painter.rs @@ -51,6 +51,30 @@ pub struct Painter { pub screen_rect: Rect, } +macro_rules! get_gl_error { + ($id:expr, $fnlen:ident, $fnlog:ident) => {{ + let mut len = 0; + unsafe { + gl::$fnlen($id, gl::INFO_LOG_LENGTH, &mut len); + let mut buf = Vec::with_capacity(len as usize); + gl::$fnlog($id, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar); + buf.set_len(len.try_into().unwrap()); + CString::from_vec_with_nul(buf) + .unwrap() + .to_string_lossy() + .to_string() + } + }}; +} + +fn get_shader_error(id: u32) -> String { + get_gl_error!(id, GetShaderiv, GetShaderInfoLog) +} + +fn get_program_error(id: u32) -> String { + get_gl_error!(id, GetProgramiv, GetProgramInfoLog) +} + pub fn compile_shader(src: &str, ty: GLenum) -> GLuint { let shader; unsafe { @@ -65,22 +89,7 @@ pub fn compile_shader(src: &str, ty: GLenum) -> GLuint { // Fail on error if status != (gl::TRUE as GLint) { - let mut len = 0; - gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len); - let mut buf = Vec::with_capacity(len as usize); - // clippy not happy with this, broke the CI: - // error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - // buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character - gl::GetShaderInfoLog( - shader, - len, - ptr::null_mut(), - buf.as_mut_ptr() as *mut GLchar, - ); - panic!( - "{}", - str::from_utf8(&buf).expect("ShaderInfoLog not valid utf8") - ); + panic!("{}", get_shader_error(shader)); } } shader @@ -98,22 +107,7 @@ pub fn link_program(vs: GLuint, fs: GLuint) -> GLuint { // Fail on error if status != (gl::TRUE as GLint) { - let mut len: GLint = 0; - gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len); - let mut buf = Vec::with_capacity(len as usize); - // clippy not happy with this, broke the CI: - // error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - // buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character - gl::GetProgramInfoLog( - program, - len, - ptr::null_mut(), - buf.as_mut_ptr() as *mut GLchar, - ); - panic!( - "{}", - str::from_utf8(&buf).expect("ProgramInfoLog not valid utf8") - ); + panic!("{}", get_program_error(program)); } program } From e0b3b575ea664f052445742e90a48135a3ac50b4 Mon Sep 17 00:00:00 2001 From: David Cohen Date: Tue, 12 Mar 2024 21:51:17 -0500 Subject: [PATCH 7/7] apply clippy fixes Signed-off-by: David Cohen --- examples/basic.rs | 18 ++++++++---------- examples/mix/triangle.rs | 4 ++-- src/painter.rs | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/examples/basic.rs b/examples/basic.rs index bf484e1..dcd2430 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -58,16 +58,14 @@ fn main() { error ); } - } else { - if let Err(error) = window - .subsystem() - .gl_set_swap_interval(SwapInterval::Immediate) - { - println!( - "Failed to gl_set_swap_interval(SwapInterval::Immediate): {}", - error - ); - } + } else if let Err(error) = window + .subsystem() + .gl_set_swap_interval(SwapInterval::Immediate) + { + println!( + "Failed to gl_set_swap_interval(SwapInterval::Immediate): {}", + error + ); } let start_time = Instant::now(); diff --git a/examples/mix/triangle.rs b/examples/mix/triangle.rs index 58fec01..4cf9d7e 100644 --- a/examples/mix/triangle.rs +++ b/examples/mix/triangle.rs @@ -9,7 +9,7 @@ use std::mem; use std::ptr; use std::str; -const VS_SRC: &'static str = " +const VS_SRC: &str = " #version 150 in vec2 position; @@ -17,7 +17,7 @@ void main() { gl_Position = vec4(position, 0.0, 1.0); }"; -const FS_SRC: &'static str = " +const FS_SRC: &str = " #version 150 out vec4 out_color; diff --git a/src/painter.rs b/src/painter.rs index ee867e0..a020486 100644 --- a/src/painter.rs +++ b/src/painter.rs @@ -571,7 +571,7 @@ impl Painter { fn generate_gl_texture2d( gl_id: &mut Option, - pixels: &Vec, + pixels: &[u8], width: i32, height: i32, filtering: bool,