From 2e96378015b15ff45b4d32b0cb2043a1eb74bbfa Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 10 Aug 2018 13:07:36 -0700 Subject: [PATCH 1/2] Add a debugging feature to visualize overdraw. Lighter shades of orange indicate more overdraw. This has already helped to diagnose a few issues (e.g. #2958). --- webrender/res/brush.glsl | 4 ++ webrender/res/ps_text_run.glsl | 4 +- webrender/res/shared.glsl | 5 +- webrender/src/device/gl.rs | 4 ++ webrender/src/renderer.rs | 126 +++++++++++++++++++++++++-------- webrender/src/shade.rs | 44 ++++++++++-- webrender_api/src/api.rs | 2 + wrench/src/main.rs | 4 ++ wrench/src/wrench.rs | 1 + 9 files changed, 156 insertions(+), 38 deletions(-) diff --git a/webrender/res/brush.glsl b/webrender/res/brush.glsl index 0d04a1a4d5..baeebd45dc 100644 --- a/webrender/res/brush.glsl +++ b/webrender/res/brush.glsl @@ -122,6 +122,9 @@ struct Fragment { Fragment brush_fs(); void main(void) { +#ifdef WR_FEATURE_DEBUG_OVERDRAW + oFragColor = WR_DEBUG_OVERDRAW_COLOR; +#else // Run the specific brush FS code to output the color. Fragment frag = brush_fs(); @@ -138,5 +141,6 @@ void main(void) { // TODO(gw): Handle pre-multiply common code here as required. oFragColor = frag.color; +#endif } #endif diff --git a/webrender/res/ps_text_run.glsl b/webrender/res/ps_text_run.glsl index 49730e84a9..fe53d711cc 100644 --- a/webrender/res/ps_text_run.glsl +++ b/webrender/res/ps_text_run.glsl @@ -271,7 +271,9 @@ void main(void) { alpha *= float(all(greaterThanEqual(vUvClip, vec4(0.0)))); #endif -#ifdef WR_FEATURE_DUAL_SOURCE_BLENDING +#if defined(WR_FEATURE_DEBUG_OVERDRAW) + oFragColor = WR_DEBUG_OVERDRAW_COLOR; +#elif defined(WR_FEATURE_DUAL_SOURCE_BLENDING) vec4 alpha_mask = mask * alpha; oFragColor = vColor * alpha_mask; oFragBlend = alpha_mask * vColor.a; diff --git a/webrender/res/shared.glsl b/webrender/res/shared.glsl index fe60dd08b2..03a24e64d6 100644 --- a/webrender/res/shared.glsl +++ b/webrender/res/shared.glsl @@ -57,7 +57,10 @@ out vec4 oFragColor; #endif - #define EPSILON 0.0001 + #define EPSILON 0.0001 + + // "Show Overdraw" color. Premultiplied. + #define WR_DEBUG_OVERDRAW_COLOR vec4(0.110, 0.077, 0.027, 0.125) float distance_to_line(vec2 p0, vec2 perp_dir, vec2 p) { vec2 dir_to_p0 = p0 - p; diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs index 6e5d4731f6..3a115ae91c 100644 --- a/webrender/src/device/gl.rs +++ b/webrender/src/device/gl.rs @@ -2181,6 +2181,10 @@ impl Device { 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 supports_extension(&self, extension: &str) -> bool { supports_extension(&self.extensions, extension) diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 923bc32784..7453e9fbe0 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -212,17 +212,18 @@ impl BatchKind { bitflags! { #[derive(Default)] pub struct DebugFlags: u32 { - const PROFILER_DBG = 1 << 0; - const RENDER_TARGET_DBG = 1 << 1; - const TEXTURE_CACHE_DBG = 1 << 2; - const GPU_TIME_QUERIES = 1 << 3; - const GPU_SAMPLE_QUERIES= 1 << 4; - const DISABLE_BATCHING = 1 << 5; - const EPOCHS = 1 << 6; - const COMPACT_PROFILER = 1 << 7; - const ECHO_DRIVER_MESSAGES = 1 << 8; - const NEW_FRAME_INDICATOR = 1 << 9; - const NEW_SCENE_INDICATOR = 1 << 10; + const PROFILER_DBG = 1 << 0; + const RENDER_TARGET_DBG = 1 << 1; + const TEXTURE_CACHE_DBG = 1 << 2; + const GPU_TIME_QUERIES = 1 << 3; + const GPU_SAMPLE_QUERIES = 1 << 4; + const DISABLE_BATCHING = 1 << 5; + const EPOCHS = 1 << 6; + const COMPACT_PROFILER = 1 << 7; + const ECHO_DRIVER_MESSAGES = 1 << 8; + const NEW_FRAME_INDICATOR = 1 << 9; + const NEW_SCENE_INDICATOR = 1 << 10; + const SHOW_OVERDRAW = 1 << 11; } } @@ -2168,6 +2169,9 @@ impl Renderer { DebugCommand::EnableNewSceneIndicator(enable) => { self.set_debug_flag(DebugFlags::NEW_SCENE_INDICATOR, enable); } + DebugCommand::EnableShowOverdraw(enable) => { + self.set_debug_flag(DebugFlags::SHOW_OVERDRAW, enable); + } DebugCommand::EnableDualSourceBlending(_) => { panic!("Should be handled by render backend"); } @@ -2304,7 +2308,7 @@ impl Renderer { self.device.disable_scissor(); self.device.disable_depth(); - self.device.set_blend(false); + self.set_blend(false, FramebufferKind::Main); //self.update_shaders(); self.update_texture_cache(); @@ -2847,12 +2851,18 @@ impl Renderer { assert!(texture.has_depth() >= target.needs_depth()); } + let framebuffer_kind = if render_target.is_none() { + FramebufferKind::Main + } else { + FramebufferKind::Other + }; + { let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_TARGET); self.device .bind_draw_target(render_target, Some(target_size)); self.device.disable_depth(); - self.device.set_blend(false); + self.set_blend(false, framebuffer_kind); let depth_clear = if !depth_is_ready && target.needs_depth() { self.device.enable_depth_write(); @@ -2903,7 +2913,7 @@ impl Renderer { if !target.vertical_blurs.is_empty() || !target.horizontal_blurs.is_empty() { let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR); - self.device.set_blend(false); + self.set_blend(false, framebuffer_kind); self.shaders.cs_blur_rgba8 .bind(&mut self.device, projection, &mut self.renderer_errors); @@ -2933,7 +2943,7 @@ impl Renderer { if target.needs_depth() { let _gl = self.gpu_profile.start_marker("opaque batches"); let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE); - self.device.set_blend(false); + self.set_blend(false, framebuffer_kind); //Note: depth equality is needed for split planes self.device.set_depth_func(DepthFunction::LessEqual); self.device.enable_depth(); @@ -2963,7 +2973,7 @@ impl Renderer { .rev() { self.shaders - .get(&batch.key) + .get(&batch.key, self.debug_flags) .bind( &mut self.device, projection, &mut self.renderer_errors, @@ -2989,7 +2999,7 @@ impl Renderer { let _gl = self.gpu_profile.start_marker("alpha batches"); let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT); - self.device.set_blend(true); + self.set_blend(true, framebuffer_kind); let mut prev_blend_mode = BlendMode::None; for alpha_batch_container in &target.alpha_batch_containers { @@ -3010,7 +3020,7 @@ impl Renderer { for batch in &alpha_batch_container.alpha_batches { self.shaders - .get(&batch.key) + .get(&batch.key, self.debug_flags) .bind( &mut self.device, projection, &mut self.renderer_errors, @@ -3018,6 +3028,10 @@ impl Renderer { 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 => { unreachable!("bug: opaque blend in alpha pass"); } @@ -3073,7 +3087,7 @@ impl Renderer { ); if batch.key.blend_mode == BlendMode::SubpixelWithBgColor { - self.device.set_blend_mode_subpixel_with_bg_color_pass1(); + self.set_blend_mode_subpixel_with_bg_color_pass1(framebuffer_kind); self.device.switch_mode(ShaderColorMode::SubpixelWithBgColorPass1 as _); // When drawing the 2nd and 3rd passes, we know that the VAO, textures etc @@ -3083,7 +3097,7 @@ impl Renderer { self.device .draw_indexed_triangles_instanced_u16(6, batch.instances.len() as i32); - self.device.set_blend_mode_subpixel_with_bg_color_pass2(); + self.set_blend_mode_subpixel_with_bg_color_pass2(framebuffer_kind); self.device.switch_mode(ShaderColorMode::SubpixelWithBgColorPass2 as _); self.device @@ -3099,7 +3113,7 @@ impl Renderer { } self.device.disable_depth(); - self.device.set_blend(false); + self.set_blend(false, framebuffer_kind); self.gpu_profile.finish_sampler(transparent_sampler); // For any registered image outputs on this render target, @@ -3191,7 +3205,7 @@ impl Renderer { if !target.vertical_blurs.is_empty() || !target.horizontal_blurs.is_empty() { let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR); - self.device.set_blend(false); + self.set_blend(false, FramebufferKind::Other); self.shaders.cs_blur_a8 .bind(&mut self.device, projection, &mut self.renderer_errors); @@ -3221,8 +3235,8 @@ impl Renderer { let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_CLIP); // switch to multiplicative blending - self.device.set_blend(true); - self.device.set_blend_mode_multiply(); + self.set_blend(true, FramebufferKind::Other); + self.set_blend_mode_multiply(FramebufferKind::Other); // draw rounded cornered rectangles if !target.clip_batcher.rectangles.is_empty() { @@ -3326,7 +3340,7 @@ impl Renderer { self.device.disable_depth(); self.device.disable_depth_write(); - self.device.set_blend(false); + self.set_blend(false, FramebufferKind::Other); // Handle any Pathfinder glyphs. let stencil_page = self.stencil_glyphs(&target.glyphs, &projection, &target_size, stats); @@ -3341,7 +3355,7 @@ impl Renderer { self.device.disable_depth(); self.device.disable_depth_write(); - self.device.set_blend(false); + self.set_blend(false, FramebufferKind::Other); for rect in &target.clears { self.device.clear_target(Some([0.0, 0.0, 0.0, 0.0]), None, Some(*rect)); @@ -3354,8 +3368,8 @@ impl Renderer { if !target.border_segments.is_empty() { let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_BORDER); - self.device.set_blend(true); - self.device.set_blend_mode_premultiplied_alpha(); + self.set_blend(true, FramebufferKind::Other); + self.set_blend_mode_premultiplied_alpha(FramebufferKind::Other); self.shaders.cs_border_segment.bind( &mut self.device, @@ -3370,7 +3384,7 @@ impl Renderer { stats, ); - self.device.set_blend(false); + self.set_blend(false, FramebufferKind::Other); } // Draw any blurs for this target. @@ -3624,8 +3638,8 @@ impl Renderer { } self.device.disable_depth_write(); + self.set_blend(false, FramebufferKind::Other); self.device.disable_stencil(); - self.device.set_blend(false); self.bind_frame_data(frame); self.texture_resolver.begin_frame(); @@ -4013,6 +4027,52 @@ impl Renderer { } self.device.end_frame(); } + + // 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) { + 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) { + 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) { + 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) { + 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) { + 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(); + } + } } pub enum ExternalImageSource<'a> { @@ -4612,3 +4672,9 @@ fn get_vao<'a>(vertex_array_kind: VertexArrayKind, VertexArrayKind::Border => &vaos.border_vao, } } + +#[derive(Clone, Copy, PartialEq)] +enum FramebufferKind { + Main, + Other, +} diff --git a/webrender/src/shade.rs b/webrender/src/shade.rs index 7549f92ff2..b60e57c634 100644 --- a/webrender/src/shade.rs +++ b/webrender/src/shade.rs @@ -13,7 +13,7 @@ use glyph_rasterizer::GlyphFormat; use renderer::{ desc, MAX_VERTEX_TEXTURE_WIDTH, - BlendMode, ImageBufferKind, RendererError, RendererOptions, + BlendMode, DebugFlags, ImageBufferKind, RendererError, RendererOptions, TextureSampler, VertexArrayKind, }; @@ -50,6 +50,7 @@ pub const IMAGE_BUFFER_KINDS: [ImageBufferKind; 4] = [ ]; const ALPHA_FEATURE: &str = "ALPHA_PASS"; +const DEBUG_OVERDRAW_FEATURE: &str = "DEBUG_OVERDRAW"; const DITHERING_FEATURE: &str = "DITHERING"; const DUAL_SOURCE_FEATURE: &str = "DUAL_SOURCE_BLENDING"; @@ -181,6 +182,7 @@ struct BrushShader { opaque: LazilyCompiledShader, alpha: LazilyCompiledShader, dual_source: Option, + debug_overdraw: LazilyCompiledShader, } impl BrushShader { @@ -227,15 +229,29 @@ impl BrushShader { None }; + let mut debug_overdraw_features = features.to_vec(); + debug_overdraw_features.push(DEBUG_OVERDRAW_FEATURE); + + let debug_overdraw = LazilyCompiledShader::new( + ShaderKind::Brush, + name, + &debug_overdraw_features, + device, + precache, + )?; + Ok(BrushShader { opaque, alpha, dual_source, + debug_overdraw, }) } - fn get(&mut self, blend_mode: BlendMode) -> &mut LazilyCompiledShader { + fn get(&mut self, blend_mode: BlendMode, debug_flags: DebugFlags) + -> &mut LazilyCompiledShader { match blend_mode { + _ if debug_flags.contains(DebugFlags::SHOW_OVERDRAW) => &mut self.debug_overdraw, BlendMode::None => &mut self.opaque, BlendMode::Alpha | BlendMode::PremultipliedAlpha | @@ -256,12 +272,14 @@ impl BrushShader { if let Some(dual_source) = self.dual_source { dual_source.deinit(device); } + self.debug_overdraw.deinit(device); } } pub struct TextShader { simple: LazilyCompiledShader, glyph_transform: LazilyCompiledShader, + debug_overdraw: LazilyCompiledShader, } impl TextShader { @@ -290,14 +308,27 @@ impl TextShader { precache, )?; - Ok(TextShader { simple, glyph_transform }) + let mut debug_overdraw_features = features.to_vec(); + debug_overdraw_features.push("DEBUG_OVERDRAW"); + + let debug_overdraw = LazilyCompiledShader::new( + ShaderKind::Text, + name, + &debug_overdraw_features, + device, + precache, + )?; + + Ok(TextShader { simple, glyph_transform, debug_overdraw }) } pub fn get( &mut self, glyph_format: GlyphFormat, + debug_flags: DebugFlags, ) -> &mut LazilyCompiledShader { match glyph_format { + _ if debug_flags.contains(DebugFlags::SHOW_OVERDRAW) => &mut self.debug_overdraw, GlyphFormat::Alpha | GlyphFormat::Subpixel | GlyphFormat::Bitmap | @@ -310,6 +341,7 @@ impl TextShader { fn deinit(self, device: &mut Device) { self.simple.deinit(device); self.glyph_transform.deinit(device); + self.debug_overdraw.deinit(device); } } @@ -673,7 +705,7 @@ impl Shaders { (color_space as usize) } - pub fn get(&mut self, key: &BatchKey) -> &mut LazilyCompiledShader { + pub fn get(&mut self, key: &BatchKey, debug_flags: DebugFlags) -> &mut LazilyCompiledShader { match key.kind { BatchKind::SplitComposite => { &mut self.ps_split_composite @@ -708,14 +740,14 @@ impl Shaders { .expect("Unsupported YUV shader kind") } }; - brush_shader.get(key.blend_mode) + brush_shader.get(key.blend_mode, debug_flags) } BatchKind::TextRun(glyph_format) => { let text_shader = match key.blend_mode { BlendMode::SubpixelDualSource => &mut self.ps_text_run_dual_source, _ => &mut self.ps_text_run, }; - text_shader.get(glyph_format) + text_shader.get(glyph_format, debug_flags) } } } diff --git a/webrender_api/src/api.rs b/webrender_api/src/api.rs index 48a5fde95a..8debdd3548 100644 --- a/webrender_api/src/api.rs +++ b/webrender_api/src/api.rs @@ -571,6 +571,8 @@ pub enum DebugCommand { EnableNewFrameIndicator(bool), /// Show an indicator that moves every time a scene is built. EnableNewSceneIndicator(bool), + /// Show an overlay displaying overdraw amount. + EnableShowOverdraw(bool), /// Fetch current documents and display lists. FetchDocuments, /// Fetch current passes and batches. diff --git a/wrench/src/main.rs b/wrench/src/main.rs index 6719363c9d..c15d7c697a 100644 --- a/wrench/src/main.rs +++ b/wrench/src/main.rs @@ -620,6 +620,10 @@ fn render<'a>( ); do_render = true; } + VirtualKeyCode::V => { + wrench.renderer.toggle_debug_flags(DebugFlags::SHOW_OVERDRAW); + do_render = true; + } VirtualKeyCode::R => { wrench.set_page_zoom(ZoomFactor::new(1.0)); do_frame = true; diff --git a/wrench/src/wrench.rs b/wrench/src/wrench.rs index 052560a263..078f0c84ed 100644 --- a/wrench/src/wrench.rs +++ b/wrench/src/wrench.rs @@ -552,6 +552,7 @@ impl Wrench { "O - Toggle showing intermediate targets", "I - Toggle showing texture caches", "B - Toggle showing alpha primitive rects", + "V - Toggle showing overdraw", "S - Toggle compact profiler", "Q - Toggle GPU queries for time and samples", "M - Trigger memory pressure event", From a80d7e1a24c235720083acbd8992bbf8524041e0 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 9 Aug 2018 21:09:53 -0700 Subject: [PATCH 2/2] Add a "total pixels drawn" line to the debug overlay, and express sample counts as percentages to make them clearer --- webrender/src/profiler.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/webrender/src/profiler.rs b/webrender/src/profiler.rs index 24112da7ab..5ef5b796df 100644 --- a/webrender/src/profiler.rs +++ b/webrender/src/profiler.rs @@ -98,19 +98,19 @@ impl ProfileCounter for IntProfileCounter { } #[cfg(feature = "debug_renderer")] -pub struct FloatProfileCounter { +pub struct PercentageProfileCounter { description: &'static str, value: f32, } #[cfg(feature = "debug_renderer")] -impl ProfileCounter for FloatProfileCounter { +impl ProfileCounter for PercentageProfileCounter { fn description(&self) -> &'static str { self.description } fn value(&self) -> String { - format!("{:.2}", self.value) + format!("{:.2}%", self.value * 100.0) } } @@ -1119,21 +1119,27 @@ impl Profiler { ); if !gpu_samplers.is_empty() { - let mut samplers = Vec::::new(); + let mut samplers = Vec::::new(); // Gathering unique GPU samplers. This has O(N^2) complexity, // but we only have a few samplers per target. + let mut total = 0.0; for sampler in gpu_samplers { let value = sampler.count as f32 * screen_fraction; + total += value; match samplers.iter().position(|s| { s.description as *const _ == sampler.tag.label as *const _ }) { Some(pos) => samplers[pos].value += value, - None => samplers.push(FloatProfileCounter { + None => samplers.push(PercentageProfileCounter { description: sampler.tag.label, value, }), } } + samplers.push(PercentageProfileCounter { + description: "Total", + value: total, + }); let samplers: Vec<&ProfileCounter> = samplers.iter().map(|sampler| { sampler as &ProfileCounter }).collect();