| author | Dzmitry Malyshau <dmalyshau@mozilla.com> |
| Wed, 18 Dec 2019 23:26:39 +0000 | |
| changeset 507661 | 8b96d1241d111eb9d94446f89df812f81b6bd08d |
| parent 507660 | ad7e08d4884143b7ec9d75552a5c1d479969d456 |
| child 507662 | f245cb8f26c3c058e2bae7cfad2c1e30b5680783 |
| push id | 36931 |
| push user | opoprus@mozilla.com |
| push date | Thu, 19 Dec 2019 09:50:06 +0000 |
| treeherder | mozilla-central@5e8b48c8cd93 [default view] [failures only] |
| perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
| reviewers | gw |
| bugs | 1604664 |
| milestone | 73.0a1 |
| first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
| last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/gfx/wr/webrender/src/device/gl.rs +++ b/gfx/wr/webrender/src/device/gl.rs @@ -1049,16 +1049,31 @@ pub struct Device { // GL extensions extensions: Vec<String>, /// Dumps the source of the shader with the given name dump_shader_source: Option<String>, surface_origin_is_top_left: bool, + + /// A debug boolean for tracking if the shader program has been set after + /// a blend mode change. + /// + /// This is needed for compatibility with next-gen + /// GPU APIs that switch states using "pipeline object" that bundles + /// together the blending state with the shader. + /// + /// Having the constraint of always binding the shader last would allow + /// us to have the "pipeline object" bound at that time. Without this + /// constraint, we'd either have to eagerly bind the "pipeline object" + /// on changing either the shader or the blend more, or lazily bind it + /// at draw call time, neither of which is desirable. + #[cfg(debug_assertions)] + shader_is_ready: bool, } /// Contains the parameters necessary to bind a draw target. #[derive(Clone, Copy, Debug)] pub enum DrawTarget { /// Use the device's default draw target, with the provided dimensions, /// which are used to set the viewport. Default { @@ -1492,16 +1507,19 @@ impl Device { cached_programs, frame_id: GpuFrameId(0), extensions, texture_storage_usage, requires_null_terminated_shader_source, optimal_pbo_stride, dump_shader_source, surface_origin_is_top_left, + + #[cfg(debug_assertions)] + shader_is_ready: false, } } pub fn gl(&self) -> &dyn gl::Gl { &*self.gl } pub fn rc_gl(&self) -> &Rc<dyn gl::Gl> { @@ -1614,16 +1632,20 @@ impl Device { } Ok(id) } } pub fn begin_frame(&mut self) -> GpuFrameId { debug_assert!(!self.inside_frame); self.inside_frame = true; + #[cfg(debug_assertions)] + { + self.shader_is_ready = false; + } // If our profiler state has changed, apply or remove the profiling // wrapper from our GL context. let being_profiled = profiler::thread_is_being_profiled(); let using_wrapper = self.base_gl.is_some(); if being_profiled && !using_wrapper { fn note(name: &str, duration: Duration) { profiler::add_text_marker(cstr!("OpenGL Calls"), name, duration); @@ -1991,16 +2013,20 @@ impl Device { program.u_mode = self.gl.get_uniform_location(program.id, "uMode"); Ok(()) } pub fn bind_program(&mut self, program: &Program) { debug_assert!(self.inside_frame); debug_assert!(program.is_initialized()); + #[cfg(debug_assertions)] + { + self.shader_is_ready = true; + } if self.bound_program != program.id { self.gl.use_program(program.id); self.bound_program = program.id; self.program_mode_id = UniformLocation(program.u_mode); } } @@ -2601,22 +2627,28 @@ impl Device { } pub fn set_uniforms( &self, program: &Program, transform: &Transform3D<f32>, ) { debug_assert!(self.inside_frame); + #[cfg(debug_assertions)] + debug_assert!(self.shader_is_ready); + self.gl .uniform_matrix_4fv(program.u_transform, false, &transform.to_row_major_array()); } pub fn switch_mode(&self, mode: i32) { debug_assert!(self.inside_frame); + #[cfg(debug_assertions)] + debug_assert!(self.shader_is_ready); + self.gl.uniform_1i(self.program_mode_id.0, mode); } pub fn create_pbo(&mut self) -> PBO { let id = self.gl.gen_buffers(1)[0]; PBO { id, reserved_size: 0, @@ -3144,46 +3176,61 @@ impl Device { gl::ELEMENT_ARRAY_BUFFER, indices, usage_hint.to_gl(), ); } pub fn draw_triangles_u16(&mut self, first_vertex: i32, index_count: i32) { debug_assert!(self.inside_frame); + #[cfg(debug_assertions)] + debug_assert!(self.shader_is_ready); + self.gl.draw_elements( gl::TRIANGLES, index_count, gl::UNSIGNED_SHORT, first_vertex as u32 * 2, ); } pub fn draw_triangles_u32(&mut self, first_vertex: i32, index_count: i32) { debug_assert!(self.inside_frame); + #[cfg(debug_assertions)] + debug_assert!(self.shader_is_ready); + self.gl.draw_elements( gl::TRIANGLES, index_count, gl::UNSIGNED_INT, first_vertex as u32 * 4, ); } pub fn draw_nonindexed_points(&mut self, first_vertex: i32, vertex_count: i32) { debug_assert!(self.inside_frame); + #[cfg(debug_assertions)] + debug_assert!(self.shader_is_ready); + self.gl.draw_arrays(gl::POINTS, first_vertex, vertex_count); } pub fn draw_nonindexed_lines(&mut self, first_vertex: i32, vertex_count: i32) { debug_assert!(self.inside_frame); + #[cfg(debug_assertions)] + debug_assert!(self.shader_is_ready); + self.gl.draw_arrays(gl::LINES, first_vertex, vertex_count); } pub fn draw_indexed_triangles_instanced_u16(&mut self, index_count: i32, instance_count: i32) { debug_assert!(self.inside_frame); + #[cfg(debug_assertions)] + debug_assert!(self.shader_is_ready); + self.gl.draw_elements_instanced( gl::TRIANGLES, index_count, gl::UNSIGNED_SHORT, 0, instance_count, ); } @@ -3300,92 +3347,142 @@ impl Device { pub fn enable_scissor(&self) { self.gl.enable(gl::SCISSOR_TEST); } pub fn disable_scissor(&self) { self.gl.disable(gl::SCISSOR_TEST); } - pub fn set_blend(&self, enable: bool) { + pub fn set_blend(&mut self, enable: bool) { if enable { self.gl.enable(gl::BLEND); } else { self.gl.disable(gl::BLEND); } + #[cfg(debug_assertions)] + { + self.shader_is_ready = false; + } } - pub fn set_blend_mode_alpha(&self) { - self.gl.blend_func_separate(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA, - gl::ONE, gl::ONE); + fn set_blend_factors( + &mut self, + color: (gl::GLenum, gl::GLenum), + alpha: (gl::GLenum, gl::GLenum), + ) { self.gl.blend_equation(gl::FUNC_ADD); + if color == alpha { + self.gl.blend_func(color.0, color.1); + } else { + self.gl.blend_func_separate(color.0, color.1, alpha.0, alpha.1); + } + #[cfg(debug_assertions)] + { + self.shader_is_ready = false; + } } - pub fn set_blend_mode_premultiplied_alpha(&self) { - self.gl.blend_func(gl::ONE, gl::ONE_MINUS_SRC_ALPHA); - self.gl.blend_equation(gl::FUNC_ADD); + pub fn set_blend_mode_alpha(&mut self) { + self.set_blend_factors( + (gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA), + (gl::ONE, gl::ONE), + ); + } + + pub fn set_blend_mode_premultiplied_alpha(&mut self) { + self.set_blend_factors( + (gl::ONE, gl::ONE_MINUS_SRC_ALPHA), + (gl::ONE, gl::ONE_MINUS_SRC_ALPHA), + ); + } + + pub fn set_blend_mode_premultiplied_dest_out(&mut self) { + self.set_blend_factors( + (gl::ZERO, gl::ONE_MINUS_SRC_ALPHA), + (gl::ZERO, gl::ONE_MINUS_SRC_ALPHA), + ); + } + + pub fn set_blend_mode_multiply(&mut self) { + self.set_blend_factors( + (gl::ZERO, gl::SRC_COLOR), + (gl::ZERO, gl::SRC_ALPHA), + ); } - - pub fn set_blend_mode_premultiplied_dest_out(&self) { - self.gl.blend_func(gl::ZERO, gl::ONE_MINUS_SRC_ALPHA); - self.gl.blend_equation(gl::FUNC_ADD); + pub fn set_blend_mode_subpixel_pass0(&mut self) { + self.set_blend_factors( + (gl::ZERO, gl::ONE_MINUS_SRC_COLOR), + (gl::ZERO, gl::ONE_MINUS_SRC_ALPHA), + ); + } + pub fn set_blend_mode_subpixel_pass1(&mut self) { + self.set_blend_factors( + (gl::ONE, gl::ONE), + (gl::ONE, gl::ONE), + ); + } + pub fn set_blend_mode_subpixel_with_bg_color_pass0(&mut self) { + self.set_blend_factors( + (gl::ZERO, gl::ONE_MINUS_SRC_COLOR), + (gl::ZERO, gl::ONE), + ); + } + pub fn set_blend_mode_subpixel_with_bg_color_pass1(&mut self) { + self.set_blend_factors( + (gl::ONE_MINUS_DST_ALPHA, gl::ONE), + (gl::ZERO, gl::ONE), + ); } - - pub fn set_blend_mode_multiply(&self) { - self.gl - .blend_func_separate(gl::ZERO, gl::SRC_COLOR, gl::ZERO, gl::SRC_ALPHA); - self.gl.blend_equation(gl::FUNC_ADD); + pub fn set_blend_mode_subpixel_with_bg_color_pass2(&mut self) { + self.set_blend_factors( + (gl::ONE, gl::ONE), + (gl::ONE, gl::ONE_MINUS_SRC_ALPHA), + ); + } + pub fn set_blend_mode_subpixel_constant_text_color(&mut self, color: ColorF) { + // color is an unpremultiplied color. + self.gl.blend_color(color.r, color.g, color.b, 1.0); + self.set_blend_factors( + (gl::CONSTANT_COLOR, gl::ONE_MINUS_SRC_COLOR), + (gl::CONSTANT_ALPHA, gl::ONE_MINUS_SRC_ALPHA), + ); } - pub fn set_blend_mode_max(&self) { + pub fn set_blend_mode_subpixel_dual_source(&mut self) { + self.set_blend_factors( + (gl::ONE, gl::ONE_MINUS_SRC1_COLOR), + (gl::ONE, gl::ONE_MINUS_SRC1_ALPHA), + ); + } + pub fn set_blend_mode_show_overdraw(&mut self) { + self.set_blend_factors( + (gl::ONE, gl::ONE_MINUS_SRC_ALPHA), + (gl::ONE, gl::ONE_MINUS_SRC_ALPHA), + ); + } + + pub fn set_blend_mode_max(&mut self) { self.gl .blend_func_separate(gl::ONE, gl::ONE, gl::ONE, gl::ONE); self.gl.blend_equation_separate(gl::MAX, gl::FUNC_ADD); + #[cfg(debug_assertions)] + { + self.shader_is_ready = false; + } } - pub fn set_blend_mode_min(&self) { + pub fn set_blend_mode_min(&mut self) { self.gl .blend_func_separate(gl::ONE, gl::ONE, gl::ONE, gl::ONE); self.gl.blend_equation_separate(gl::MIN, gl::FUNC_ADD); - } - pub fn set_blend_mode_subpixel_pass0(&self) { - self.gl.blend_func(gl::ZERO, gl::ONE_MINUS_SRC_COLOR); - self.gl.blend_equation(gl::FUNC_ADD); - } - pub fn set_blend_mode_subpixel_pass1(&self) { - self.gl.blend_func(gl::ONE, gl::ONE); - self.gl.blend_equation(gl::FUNC_ADD); - } - pub fn set_blend_mode_subpixel_with_bg_color_pass0(&self) { - self.gl.blend_func_separate(gl::ZERO, gl::ONE_MINUS_SRC_COLOR, gl::ZERO, gl::ONE); - self.gl.blend_equation(gl::FUNC_ADD); - } - pub fn set_blend_mode_subpixel_with_bg_color_pass1(&self) { - self.gl.blend_func_separate(gl::ONE_MINUS_DST_ALPHA, gl::ONE, gl::ZERO, gl::ONE); - self.gl.blend_equation(gl::FUNC_ADD); + #[cfg(debug_assertions)] + { + self.shader_is_ready = false; + } } - pub fn set_blend_mode_subpixel_with_bg_color_pass2(&self) { - self.gl.blend_func_separate(gl::ONE, gl::ONE, gl::ONE, gl::ONE_MINUS_SRC_ALPHA); - self.gl.blend_equation(gl::FUNC_ADD); - } - pub fn set_blend_mode_subpixel_constant_text_color(&self, color: ColorF) { - // color is an unpremultiplied color. - self.gl.blend_color(color.r, color.g, color.b, 1.0); - self.gl - .blend_func(gl::CONSTANT_COLOR, gl::ONE_MINUS_SRC_COLOR); - self.gl.blend_equation(gl::FUNC_ADD); - } - pub fn set_blend_mode_subpixel_dual_source(&self) { - self.gl.blend_func(gl::ONE, gl::ONE_MINUS_SRC1_COLOR); - self.gl.blend_equation(gl::FUNC_ADD); - } - pub fn set_blend_mode_show_overdraw(&self) { - self.gl.blend_func(gl::ONE, gl::ONE_MINUS_SRC_ALPHA); - self.gl.blend_equation(gl::FUNC_ADD); - } - - pub fn set_blend_mode_advanced(&self, mode: MixBlendMode) { + pub fn set_blend_mode_advanced(&mut self, mode: MixBlendMode) { self.gl.blend_equation(match mode { MixBlendMode::Normal => { // blend factor only make sense for the normal mode self.gl.blend_func_separate(gl::ZERO, gl::SRC_COLOR, gl::ZERO, gl::SRC_ALPHA); gl::FUNC_ADD }, MixBlendMode::Multiply => gl::MULTIPLY_KHR, MixBlendMode::Screen => gl::SCREEN_KHR, @@ -3398,16 +3495,20 @@ impl Device { MixBlendMode::SoftLight => gl::SOFTLIGHT_KHR, MixBlendMode::Difference => gl::DIFFERENCE_KHR, MixBlendMode::Exclusion => gl::EXCLUSION_KHR, MixBlendMode::Hue => gl::HSL_HUE_KHR, MixBlendMode::Saturation => gl::HSL_SATURATION_KHR, MixBlendMode::Color => gl::HSL_COLOR_KHR, MixBlendMode::Luminosity => gl::HSL_LUMINOSITY_KHR, }); + #[cfg(debug_assertions)] + { + self.shader_is_ready = false; + } } pub fn supports_extension(&self, extension: &str) -> bool { supports_extension(&self.extensions, extension) } /// Enable the pixel local storage functionality. Caller must /// have already confirmed the device supports this.
--- a/gfx/wr/webrender/src/renderer.rs +++ b/gfx/wr/webrender/src/renderer.rs @@ -3954,17 +3954,19 @@ impl Renderer { self.gpu_profile.finish_sampler(opaque_sampler); } if !alpha_batch_container.alpha_batches.is_empty() && !self.debug_flags.contains(DebugFlags::DISABLE_ALPHA_PASS) { let _gl = self.gpu_profile.start_marker("alpha batches"); let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT); self.set_blend(true, framebuffer_kind); + let mut prev_blend_mode = BlendMode::None; + let shaders_rc = self.shaders.clone(); // If the device supports pixel local storage, initialize the PLS buffer for // the transparent pass. This involves reading the current framebuffer value // and storing that in PLS. // TODO(gw): This is quite expensive and relies on framebuffer fetch being // available. We can probably switch the opaque pass over to use // PLS too, and remove this pass completely. if self.device.get_capabilities().supports_pixel_local_storage { @@ -3978,22 +3980,22 @@ impl Renderer { ); } for batch in &alpha_batch_container.alpha_batches { if should_skip_batch(&batch.key.kind, &self.debug_flags) { continue; } - self.shaders.borrow_mut() - .get(&batch.key, batch.features | BatchFeatures::ALPHA_PASS, self.debug_flags) - .bind( - &mut self.device, projection, - &mut self.renderer_errors, - ); + let mut shaders = shaders_rc.borrow_mut(); + let shader = shaders.get( + &batch.key, + batch.features | BatchFeatures::ALPHA_PASS, + self.debug_flags, + ); if batch.key.blend_mode != prev_blend_mode { match batch.key.blend_mode { _ if self.debug_flags.contains(DebugFlags::SHOW_OVERDRAW) && framebuffer_kind == FramebufferKind::Main => { self.device.set_blend_mode_show_overdraw(); } BlendMode::None => { @@ -4016,16 +4018,22 @@ impl Renderer { } BlendMode::SubpixelWithBgColor => { // Using the three pass "component alpha with font smoothing // background color" rendering technique: // // /webrender/doc/text-rendering.md // self.device.set_blend_mode_subpixel_with_bg_color_pass0(); + // need to make sure the shader is bound + shader.bind( + &mut self.device, + projection, + &mut self.renderer_errors, + ); self.device.switch_mode(ShaderColorMode::SubpixelWithBgColorPass0 as _); } BlendMode::Advanced(mode) => { if self.enable_advanced_blend_barriers { self.device.gl().blend_barrier_khr(); } self.device.set_blend_mode_advanced(mode); } @@ -4043,36 +4051,53 @@ impl Renderer { uses_scissor, &render_tasks[source_id], &render_tasks[task_id], &render_tasks[backdrop_id], ); } let _timer = self.gpu_profile.start_timer(batch.key.kind.sampler_tag()); + shader.bind( + &mut self.device, + projection, + &mut self.renderer_errors, + ); self.draw_instanced_batch( &batch.instances, VertexArrayKind::Primitive, &batch.key.textures, stats ); if batch.key.blend_mode == BlendMode::SubpixelWithBgColor { self.set_blend_mode_subpixel_with_bg_color_pass1(framebuffer_kind); + // re-binding the shader after the blend mode change + shader.bind( + &mut self.device, + projection, + &mut self.renderer_errors, + ); self.device.switch_mode(ShaderColorMode::SubpixelWithBgColorPass1 as _); // When drawing the 2nd and 3rd passes, we know that the VAO, textures etc // are all set up from the previous draw_instanced_batch call, // so just issue a draw call here to avoid re-uploading the // instances and re-binding textures etc. self.device .draw_indexed_triangles_instanced_u16(6, batch.instances.len() as i32); self.set_blend_mode_subpixel_with_bg_color_pass2(framebuffer_kind); + // re-binding the shader after the blend mode change + shader.bind( + &mut self.device, + projection, + &mut self.renderer_errors, + ); self.device.switch_mode(ShaderColorMode::SubpixelWithBgColorPass2 as _); self.device .draw_indexed_triangles_instanced_u16(6, batch.instances.len() as i32); } if batch.key.blend_mode == BlendMode::SubpixelWithBgColor { prev_blend_mode = BlendMode::None; @@ -4099,19 +4124,26 @@ impl Renderer { self.device.disable_scissor(); } } /// Draw a list of tiles to the framebuffer fn draw_tile_list<'a, I: Iterator<Item = &'a CompositeTile>>( &mut self, tiles_iter: I, + projection: &default::Transform3D<f32>, partial_present_mode: Option<PartialPresentMode>, stats: &mut RendererStats, ) { + self.shaders.borrow_mut().composite.bind( + &mut self.device, + projection, + &mut self.renderer_errors + ); + let mut current_textures = BatchTextures::no_texture(); let mut instances = Vec::new(); for tile in tiles_iter { // Work out the draw params based on the tile surface let (texture, layer, color) = match tile.surface { CompositeTileSurface::Color { color } => { (TextureSource::Dummy, 0.0, color) @@ -4249,63 +4281,60 @@ impl Renderer { // Partial present is disabled, so clear the entire framebuffer self.device.clear_target(clear_color, Some(1.0), None); } } } - self.shaders.borrow_mut().composite.bind( - &mut self.device, - &projection, - &mut self.renderer_errors - ); - // We are only interested in tiles backed with actual cached pixels so we don't // count clear tiles here. let num_tiles = composite_state.opaque_tiles.len() + composite_state.alpha_tiles.len(); self.profile_counters.total_picture_cache_tiles.set(num_tiles); // Draw opaque tiles first, front-to-back to get maxmum // z-reject efficiency. if !composite_state.opaque_tiles.is_empty() { let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE); self.device.enable_depth_write(); self.set_blend(false, FramebufferKind::Main); self.draw_tile_list( composite_state.opaque_tiles.iter().rev(), + projection, partial_present_mode, &mut results.stats, ); self.gpu_profile.finish_sampler(opaque_sampler); } if !composite_state.clear_tiles.is_empty() { let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT); self.device.disable_depth_write(); self.set_blend(true, FramebufferKind::Main); self.device.set_blend_mode_premultiplied_dest_out(); self.draw_tile_list( composite_state.clear_tiles.iter(), + projection, partial_present_mode, &mut results.stats, ); self.gpu_profile.finish_sampler(transparent_sampler); } // Draw alpha tiles if !composite_state.alpha_tiles.is_empty() { let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT); self.device.disable_depth_write(); self.set_blend(true, FramebufferKind::Main); self.set_blend_mode_premultiplied_alpha(FramebufferKind::Main); self.draw_tile_list( composite_state.alpha_tiles.iter(), + projection, partial_present_mode, &mut results.stats, ); self.gpu_profile.finish_sampler(transparent_sampler); } } fn draw_color_target( @@ -5985,52 +6014,52 @@ impl Renderer { // Textures held internally within the device layer. report += self.device.report_memory(); report } // Sets the blend mode. Blend is unconditionally set if the "show overdraw" debugging mode is // enabled. - fn set_blend(&self, mut blend: bool, framebuffer_kind: FramebufferKind) { + fn set_blend(&mut self, mut blend: bool, framebuffer_kind: FramebufferKind) { if framebuffer_kind == FramebufferKind::Main && self.debug_flags.contains(DebugFlags::SHOW_OVERDRAW) { blend = true } self.device.set_blend(blend) } - fn set_blend_mode_multiply(&self, framebuffer_kind: FramebufferKind) { + fn set_blend_mode_multiply(&mut self, framebuffer_kind: FramebufferKind) { if framebuffer_kind == FramebufferKind::Main && self.debug_flags.contains(DebugFlags::SHOW_OVERDRAW) { self.device.set_blend_mode_show_overdraw(); } else { self.device.set_blend_mode_multiply(); } } - fn set_blend_mode_premultiplied_alpha(&self, framebuffer_kind: FramebufferKind) { + fn set_blend_mode_premultiplied_alpha(&mut self, framebuffer_kind: FramebufferKind) { if framebuffer_kind == FramebufferKind::Main && self.debug_flags.contains(DebugFlags::SHOW_OVERDRAW) { self.device.set_blend_mode_show_overdraw(); } else { self.device.set_blend_mode_premultiplied_alpha(); } } - fn set_blend_mode_subpixel_with_bg_color_pass1(&self, framebuffer_kind: FramebufferKind) { + fn set_blend_mode_subpixel_with_bg_color_pass1(&mut self, framebuffer_kind: FramebufferKind) { if framebuffer_kind == FramebufferKind::Main && self.debug_flags.contains(DebugFlags::SHOW_OVERDRAW) { self.device.set_blend_mode_show_overdraw(); } else { self.device.set_blend_mode_subpixel_with_bg_color_pass1(); } } - fn set_blend_mode_subpixel_with_bg_color_pass2(&self, framebuffer_kind: FramebufferKind) { + fn set_blend_mode_subpixel_with_bg_color_pass2(&mut self, framebuffer_kind: FramebufferKind) { if framebuffer_kind == FramebufferKind::Main && self.debug_flags.contains(DebugFlags::SHOW_OVERDRAW) { self.device.set_blend_mode_show_overdraw(); } else { self.device.set_blend_mode_subpixel_with_bg_color_pass2(); } }
--- a/gfx/wr/webrender/src/shade.rs +++ b/gfx/wr/webrender/src/shade.rs @@ -68,31 +68,35 @@ pub(crate) enum ShaderKind { Resolve, Composite, } pub struct LazilyCompiledShader { program: Option<Program>, name: &'static str, kind: ShaderKind, + cached_projection: Transform3D<f32>, features: Vec<&'static str>, } impl LazilyCompiledShader { pub(crate) fn new( kind: ShaderKind, name: &'static str, features: &[&'static str], device: &mut Device, precache_flags: ShaderPrecacheFlags, ) -> Result<Self, ShaderError> { let mut shader = LazilyCompiledShader { program: None, name, kind, + //Note: this isn't really the default state, but there is no chance + // an actual projection passed here would accidentally match. + cached_projection: Transform3D::identity(), features: features.to_vec(), }; if precache_flags.intersects(ShaderPrecacheFlags::ASYNC_COMPILE | ShaderPrecacheFlags::FULL_COMPILE) { let t0 = precise_time_ns(); shader.get_internal(device, precache_flags)?; let t1 = precise_time_ns(); debug!("[C: {:.1} ms ] Precache {} {:?}", @@ -106,25 +110,30 @@ impl LazilyCompiledShader { } pub fn bind( &mut self, device: &mut Device, projection: &Transform3D<f32>, renderer_errors: &mut Vec<RendererError>, ) { - let program = match self.get(device) { + let update_projection = self.cached_projection != *projection; + let program = match self.get_internal(device, ShaderPrecacheFlags::FULL_COMPILE) { Ok(program) => program, Err(e) => { renderer_errors.push(RendererError::from(e)); return; } }; device.bind_program(program); - device.set_uniforms(program, projection); + if update_projection { + device.set_uniforms(program, projection); + // thanks NLL for this (`program` technically borrows `self`) + self.cached_projection = *projection; + } } fn get_internal( &mut self, device: &mut Device, precache_flags: ShaderPrecacheFlags, ) -> Result<&mut Program, ShaderError> { if self.program.is_none() { @@ -240,20 +249,16 @@ impl LazilyCompiledShader { ); } } } Ok(program) } - fn get(&mut self, device: &mut Device) -> Result<&mut Program, ShaderError> { - self.get_internal(device, ShaderPrecacheFlags::FULL_COMPILE) - } - fn deinit(self, device: &mut Device) { if let Some(program) = self.program { device.delete_program(program); } } } // A brush shader supports two modes: