From 520e42c11cd83f3f0d1e2326c1f666f962c073ea Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 17 Jul 2020 10:29:21 +0200 Subject: [PATCH] [backend] use hardware scissor rect in WebGL and glium --- egui/README.md | 2 +- egui_glium/src/painter.rs | 54 +++++++++++++++------------------------ egui_wasm/src/webgl.rs | 47 ++++++++++++++++------------------ 3 files changed, 43 insertions(+), 60 deletions(-) diff --git a/egui/README.md b/egui/README.md index 86affd9b..0dc718ae 100644 --- a/egui/README.md +++ b/egui/README.md @@ -68,7 +68,7 @@ Add extremely quick animations for some things, maybe 2-3 frames. For instance: * [x] Use clip rectangles when painting * [x] Use clip rectangles when interacting * [x] Adjust clip rects so edges of child widgets aren't clipped -* [ ] Use HW clip rects +* [x] Use HW clip rects ### Modularity * [x] `trait Widget` (`Label`, `Slider`, `Checkbox`, ...) diff --git a/egui_glium/src/painter.rs b/egui_glium/src/painter.rs index fb816a5a..2959a115 100644 --- a/egui_glium/src/painter.rs +++ b/egui_glium/src/painter.rs @@ -2,6 +2,7 @@ use { egui::{ + math::clamp, paint::{PaintBatches, Triangles}, Rect, }, @@ -20,36 +21,29 @@ impl Painter { 140 => { vertex: " #version 140 - uniform vec4 u_clip_rect; // min_x, min_y, max_x, max_y uniform vec2 u_screen_size; uniform vec2 u_tex_size; in vec2 a_pos; in vec4 a_color; in vec2 a_tc; - out vec2 v_pos; out vec4 v_color; out vec2 v_tc; - out vec4 v_clip_rect; 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_pos = a_pos; v_color = a_color / 255.0; v_tc = a_tc / u_tex_size; - v_clip_rect = u_clip_rect; } ", fragment: " #version 140 uniform sampler2D u_sampler; - in vec2 v_pos; in vec4 v_color; in vec2 v_tc; - in vec4 v_clip_rect; out vec4 f_color; // glium expects linear output. @@ -61,10 +55,6 @@ impl Painter { } void main() { - if (v_pos.x < v_clip_rect.x) { discard; } - if (v_pos.y < v_clip_rect.y) { discard; } - if (v_pos.x > v_clip_rect.z) { discard; } - if (v_pos.y > v_clip_rect.w) { discard; } f_color = v_color; f_color.rgb = linear_from_srgb(f_color.rgb); f_color *= texture(u_sampler, v_tc).r; @@ -75,36 +65,29 @@ impl Painter { 110 => { vertex: " #version 110 - uniform vec4 u_clip_rect; // min_x, min_y, max_x, max_y uniform vec2 u_screen_size; uniform vec2 u_tex_size; attribute vec2 a_pos; attribute vec4 a_color; attribute vec2 a_tc; - varying vec2 v_pos; varying vec4 v_color; varying vec2 v_tc; - varying vec4 v_clip_rect; 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_pos = a_pos; v_color = a_color / 255.0; v_tc = a_tc / u_tex_size; - v_clip_rect = u_clip_rect; } ", fragment: " #version 110 uniform sampler2D u_sampler; - varying vec2 v_pos; varying vec4 v_color; varying vec2 v_tc; - varying vec4 v_clip_rect; // glium expects linear output. vec3 linear_from_srgb(vec3 srgb) { @@ -115,10 +98,6 @@ impl Painter { } void main() { - if (v_pos.x < v_clip_rect.x) { discard; } - if (v_pos.y < v_clip_rect.y) { discard; } - if (v_pos.x > v_clip_rect.z) { discard; } - if (v_pos.y > v_clip_rect.w) { discard; } gl_FragColor = v_color; gl_FragColor.rgb = linear_from_srgb(gl_FragColor.rgb); gl_FragColor *= texture2D(u_sampler, v_tc).r; @@ -129,36 +108,29 @@ impl Painter { 100 => { vertex: " #version 100 - uniform mediump vec4 u_clip_rect; // min_x, min_y, max_x, max_y uniform mediump vec2 u_screen_size; uniform mediump vec2 u_tex_size; attribute mediump vec2 a_pos; attribute mediump vec4 a_color; attribute mediump vec2 a_tc; - varying mediump vec2 v_pos; varying mediump vec4 v_color; varying mediump vec2 v_tc; - varying mediump vec4 v_clip_rect; 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_pos = a_pos; v_color = a_color / 255.0; v_tc = a_tc / u_tex_size; - v_clip_rect = u_clip_rect; } ", fragment: " #version 100 uniform sampler2D u_sampler; - varying mediump vec2 v_pos; varying mediump vec4 v_color; varying mediump vec2 v_tc; - varying mediump vec4 v_clip_rect // glium expects linear output. vec3 linear_from_srgb(vec3 srgb) { @@ -169,10 +141,6 @@ impl Painter { } void main() { - if (v_pos.x < v_clip_rect.x) { discard; } - if (v_pos.y < v_clip_rect.y) { discard; } - if (v_pos.x > v_clip_rect.z) { discard; } - if (v_pos.y > v_clip_rect.w) { discard; } gl_FragColor = v_color; gl_FragColor.rgb = linear_from_srgb(gl_FragColor.rgb); gl_FragColor *= texture2D(u_sampler, v_tc).r; @@ -271,7 +239,6 @@ impl Painter { let height_points = height_pixels as f32 / pixels_per_point; let uniforms = uniform! { - u_clip_rect: [clip_rect.min.x, clip_rect.min.y, clip_rect.max.x, clip_rect.max.y], u_screen_size: [width_points, height_points], u_tex_size: [texture.width as f32, texture.height as f32], u_sampler: &self.texture, @@ -288,8 +255,27 @@ impl Painter { ..Default::default() }; + let clip_min_x = pixels_per_point * clip_rect.min.x; + let clip_min_y = pixels_per_point * clip_rect.min.y; + let clip_max_x = pixels_per_point * clip_rect.max.x; + let clip_max_y = pixels_per_point * clip_rect.max.y; + let clip_min_x = clamp(clip_min_x, 0.0..=width_pixels as f32); + let clip_min_y = clamp(clip_min_y, 0.0..=height_pixels as f32); + let clip_max_x = clamp(clip_max_x, clip_min_x..=width_pixels as f32); + let clip_max_y = clamp(clip_max_y, clip_min_y..=height_pixels as f32); + let clip_min_x = clip_min_x.round() as u32; + let clip_min_y = clip_min_y.round() as u32; + let clip_max_x = clip_max_x.round() as u32; + let clip_max_y = clip_max_y.round() as u32; + let params = glium::DrawParameters { blend, + scissor: Some(glium::Rect { + left: clip_min_x, + bottom: height_pixels - clip_max_y, + width: clip_max_x - clip_min_x, + height: clip_max_y - clip_min_y, + }), ..Default::default() }; diff --git a/egui_wasm/src/webgl.rs b/egui_wasm/src/webgl.rs index df88942b..0c69b1ee 100644 --- a/egui_wasm/src/webgl.rs +++ b/egui_wasm/src/webgl.rs @@ -5,8 +5,9 @@ use { }; use egui::{ + math::clamp, paint::{Color, PaintBatches, Texture, Triangles}, - vec2, Pos2, + vec2, }; type Gl = WebGlRenderingContext; @@ -61,26 +62,21 @@ impl Painter { &gl, Gl::VERTEX_SHADER, r#" - uniform vec4 u_clip_rect; // min_x, min_y, max_x, max_y uniform vec2 u_screen_size; uniform vec2 u_tex_size; attribute vec2 a_pos; attribute vec2 a_tc; attribute vec4 a_color; - varying vec2 v_pos; varying vec4 v_color; varying vec2 v_tc; - varying vec4 v_clip_rect; 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_pos = a_pos; v_color = a_color; v_tc = a_tc / u_tex_size; - v_clip_rect = u_clip_rect; } "#, )?; @@ -90,15 +86,9 @@ impl Painter { r#" uniform sampler2D u_sampler; precision highp float; - varying vec2 v_pos; varying vec4 v_color; varying vec2 v_tc; - varying vec4 v_clip_rect; void main() { - if (v_pos.x < v_clip_rect.x) { discard; } - if (v_pos.y < v_clip_rect.y) { discard; } - if (v_pos.x > v_clip_rect.z) { discard; } - if (v_pos.y > v_clip_rect.w) { discard; } gl_FragColor = v_color; gl_FragColor *= texture2D(u_sampler, v_tc).a; } @@ -165,16 +155,13 @@ impl Painter { let gl = &self.gl; + gl.enable(Gl::SCISSOR_TEST); gl.enable(Gl::BLEND); gl.blend_func(Gl::ONE, Gl::ONE_MINUS_SRC_ALPHA); // premultiplied alpha gl.use_program(Some(&self.program)); gl.active_texture(Gl::TEXTURE0); gl.bind_texture(Gl::TEXTURE_2D, Some(&self.texture)); - let u_clip_rect_loc = gl - .get_uniform_location(&self.program, "u_clip_rect") - .unwrap(); - let u_screen_size_loc = gl .get_uniform_location(&self.program, "u_screen_size") .unwrap(); @@ -204,6 +191,7 @@ impl Painter { self.canvas.width() as i32, self.canvas.height() as i32, ); + // TODO: sRGBA gl.clear_color( bg_color.r as f32 / 255.0, bg_color.g as f32 / 255.0, @@ -213,16 +201,25 @@ impl Painter { gl.clear(Gl::COLOR_BUFFER_BIT); for (clip_rect, triangles) in batches { - // Avoid infinities in shader: - let clip_min = clip_rect.min.max(Pos2::default()); - let clip_max = clip_rect.max.min(Pos2::default() + screen_size_points); + let clip_min_x = pixels_per_point * clip_rect.min.x; + let clip_min_y = pixels_per_point * clip_rect.min.y; + let clip_max_x = pixels_per_point * clip_rect.max.x; + let clip_max_y = pixels_per_point * clip_rect.max.y; + let clip_min_x = clamp(clip_min_x, 0.0..=screen_size_pixels.x); + let clip_min_y = clamp(clip_min_y, 0.0..=screen_size_pixels.y); + let clip_max_x = clamp(clip_max_x, clip_min_x..=screen_size_pixels.x); + let clip_max_y = clamp(clip_max_y, clip_min_y..=screen_size_pixels.y); + let clip_min_x = clip_min_x.round() as i32; + let clip_min_y = clip_min_y.round() as i32; + let clip_max_x = clip_max_x.round() as i32; + let clip_max_y = clip_max_y.round() as i32; - gl.uniform4f( - Some(&u_clip_rect_loc), - clip_min.x, - clip_min.y, - clip_max.x, - clip_max.y, + // scissor Y coordinate is from the bottom + gl.scissor( + clip_min_x, + self.canvas.height() as i32 - clip_max_y, + clip_max_x - clip_min_x, + clip_max_y - clip_min_y, ); for triangles in triangles.split_to_u16() {