Bug 1518899 - Update webrender to commit d3edc30cf95d3c96fd8308969b22062698a0f6ce (WR PR #3493). r=kats
authorWR Updater Bot <graphics-team@mozilla.staktrace.com>
Wed, 09 Jan 2019 22:16:02 +0000
changeset 510258 267a295b50cb577e739284dd48622a6d2025d7a5
parent 510257 6d3266df4764fd0ae813afff1f1a997ab3074224
child 510259 1212d77a97b13451edec9af53e45be3e6aad0caa
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1518899
milestone66.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
Bug 1518899 - Update webrender to commit d3edc30cf95d3c96fd8308969b22062698a0f6ce (WR PR #3493). r=kats https://github.com/servo/webrender/pull/3493 Differential Revision: https://phabricator.services.mozilla.com/D16102
gfx/webrender_bindings/revision.txt
gfx/wr/examples/common/boilerplate.rs
gfx/wr/webrender/src/debug_render.rs
gfx/wr/webrender/src/frame_builder.rs
gfx/wr/webrender/src/picture.rs
gfx/wr/webrender/src/prim_store/mod.rs
gfx/wr/webrender/src/render_backend.rs
gfx/wr/webrender/src/renderer.rs
gfx/wr/webrender/src/tiling.rs
gfx/wr/webrender_api/src/api.rs
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-a25552ac640600e5bd456e048c0df592ab9fde93
+d3edc30cf95d3c96fd8308969b22062698a0f6ce
--- a/gfx/wr/examples/common/boilerplate.rs
+++ b/gfx/wr/examples/common/boilerplate.rs
@@ -228,16 +228,17 @@ pub fn main_wrapper<E: Example>(
                 },
                 ..
             } => match key {
                 winit::VirtualKeyCode::Escape => return winit::ControlFlow::Break,
                 winit::VirtualKeyCode::P => debug_flags.toggle(DebugFlags::PROFILER_DBG),
                 winit::VirtualKeyCode::O => debug_flags.toggle(DebugFlags::RENDER_TARGET_DBG),
                 winit::VirtualKeyCode::I => debug_flags.toggle(DebugFlags::TEXTURE_CACHE_DBG),
                 winit::VirtualKeyCode::S => debug_flags.toggle(DebugFlags::COMPACT_PROFILER),
+                winit::VirtualKeyCode::T => debug_flags.toggle(DebugFlags::PICTURE_CACHING_DBG),
                 winit::VirtualKeyCode::Q => debug_flags.toggle(
                     DebugFlags::GPU_TIME_QUERIES | DebugFlags::GPU_SAMPLE_QUERIES
                 ),
                 winit::VirtualKeyCode::F => debug_flags.toggle(
                     DebugFlags::NEW_FRAME_INDICATOR | DebugFlags::NEW_SCENE_INDICATOR
                 ),
                 winit::VirtualKeyCode::G => debug_flags.toggle(DebugFlags::GPU_CACHE_DBG),
                 winit::VirtualKeyCode::Key1 => txn.set_window_parameters(
--- a/gfx/wr/webrender/src/debug_render.rs
+++ b/gfx/wr/webrender/src/debug_render.rs
@@ -1,21 +1,35 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use api::{ColorU, ImageFormat, TextureTarget};
+use api::{ColorU, ColorF, ImageFormat, TextureTarget};
 use api::{DeviceIntRect, DeviceRect, DevicePoint, DeviceSize, DeviceIntSize};
 use debug_font_data;
 use device::{Device, Program, Texture, TextureSlot, VertexDescriptor, ShaderError, VAO};
 use device::{TextureFilter, VertexAttribute, VertexAttributeKind, VertexUsageHint};
 use euclid::{Point2D, Rect, Size2D, Transform3D};
 use internal_types::{ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE};
 use std::f32;
 
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub enum DebugItem {
+    Text {
+        msg: String,
+        color: ColorF,
+        position: DevicePoint,
+    },
+    Rect {
+        color: ColorF,
+        rect: DeviceRect,
+    },
+}
+
 #[derive(Debug, Copy, Clone)]
 enum DebugSampler {
     Font,
 }
 
 impl Into<TextureSlot> for DebugSampler {
     fn into(self) -> TextureSlot {
         match self {
--- a/gfx/wr/webrender/src/frame_builder.rs
+++ b/gfx/wr/webrender/src/frame_builder.rs
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{ColorF, DeviceIntPoint, DevicePixelScale, LayoutPixel, PicturePixel, RasterPixel};
-use api::{DeviceIntRect, DeviceIntSize, DocumentLayer, FontRenderMode};
+use api::{DeviceIntRect, DeviceIntSize, DocumentLayer, FontRenderMode, DebugFlags};
 use api::{LayoutPoint, LayoutRect, LayoutSize, PipelineId, RasterSpace, WorldPoint, WorldRect, WorldPixel};
 use clip::{ClipDataStore, ClipStore};
 use clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex};
 use display_list_flattener::{DisplayListFlattener};
 use gpu_cache::GpuCache;
 use gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind, ZBufferIdGenerator};
 use hit_test::{HitTester, HitTestingRun};
 use internal_types::{FastHashMap, PlaneSplitter};
@@ -72,16 +72,17 @@ pub struct FrameBuilder {
 
 pub struct FrameBuildingContext<'a> {
     pub device_pixel_scale: DevicePixelScale,
     pub scene_properties: &'a SceneProperties,
     pub pipelines: &'a FastHashMap<PipelineId, Arc<ScenePipeline>>,
     pub screen_world_rect: WorldRect,
     pub clip_scroll_tree: &'a ClipScrollTree,
     pub max_local_clip: LayoutRect,
+    pub debug_flags: DebugFlags,
 }
 
 pub struct FrameBuildingState<'a> {
     pub render_tasks: &'a mut RenderTaskTree,
     pub profile_counters: &'a mut FrameProfileCounters,
     pub clip_store: &'a mut ClipStore,
     pub resource_cache: &'a mut ResourceCache,
     pub gpu_cache: &'a mut GpuCache,
@@ -210,16 +211,17 @@ impl FrameBuilder {
         render_tasks: &mut RenderTaskTree,
         profile_counters: &mut FrameProfileCounters,
         device_pixel_scale: DevicePixelScale,
         scene_properties: &SceneProperties,
         transform_palette: &mut TransformPalette,
         resources: &mut FrameResources,
         surfaces: &mut Vec<SurfaceInfo>,
         scratch: &mut PrimitiveScratchBuffer,
+        debug_flags: DebugFlags,
     ) -> Option<RenderTaskId> {
         profile_scope!("cull");
 
         if self.prim_store.pictures.is_empty() {
             return None
         }
 
         scratch.begin_frame();
@@ -235,16 +237,17 @@ impl FrameBuilder {
             scene_properties,
             pipelines,
             screen_world_rect,
             clip_scroll_tree,
             max_local_clip: LayoutRect::new(
                 LayoutPoint::new(-MAX_CLIP_COORD, -MAX_CLIP_COORD),
                 LayoutSize::new(2.0 * MAX_CLIP_COORD, 2.0 * MAX_CLIP_COORD),
             ),
+            debug_flags,
         };
 
         // Construct a dummy root surface, that represents the
         // main framebuffer surface.
         let root_surface = SurfaceInfo::new(
             ROOT_SPATIAL_NODE_INDEX,
             ROOT_SPATIAL_NODE_INDEX,
             0.0,
@@ -284,16 +287,17 @@ impl FrameBuilder {
             &mut tile_cache_state,
             &frame_context,
             resource_cache,
             resources,
             &self.clip_store,
             &pic_update_state.surfaces,
             gpu_cache,
             &mut retained_tiles,
+            scratch,
         );
 
         let mut frame_state = FrameBuildingState {
             render_tasks,
             profile_counters,
             clip_store: &mut self.clip_store,
             resource_cache,
             gpu_cache,
@@ -369,16 +373,17 @@ impl FrameBuilder {
         device_pixel_scale: DevicePixelScale,
         layer: DocumentLayer,
         pan: WorldPoint,
         texture_cache_profile: &mut TextureCacheProfileCounters,
         gpu_cache_profile: &mut GpuCacheProfileCounters,
         scene_properties: &SceneProperties,
         resources: &mut FrameResources,
         scratch: &mut PrimitiveScratchBuffer,
+        debug_flags: DebugFlags,
     ) -> Frame {
         profile_scope!("build");
         debug_assert!(
             DeviceIntRect::new(DeviceIntPoint::zero(), self.window_size)
                 .contains_rect(&self.screen_rect)
         );
 
         let mut profile_counters = FrameProfileCounters::new();
@@ -410,16 +415,17 @@ impl FrameBuilder {
             &mut render_tasks,
             &mut profile_counters,
             device_pixel_scale,
             scene_properties,
             &mut transform_palette,
             resources,
             &mut surfaces,
             scratch,
+            debug_flags,
         );
 
         resource_cache.block_until_all_resources_added(gpu_cache,
                                                        &mut render_tasks,
                                                        texture_cache_profile);
 
         let mut passes = vec![];
 
@@ -508,25 +514,26 @@ impl FrameBuilder {
             passes,
             transform_palette: transform_palette.transforms,
             render_tasks,
             deferred_resolves,
             gpu_cache_frame_id,
             has_been_rendered: false,
             has_texture_cache_tasks,
             prim_headers,
+            #[cfg(feature = "debug_renderer")]
+            debug_items: mem::replace(&mut scratch.debug_items, Vec::new()),
         }
     }
 
     pub fn create_hit_tester(
         &mut self,
         clip_scroll_tree: &ClipScrollTree,
         clip_data_store: &ClipDataStore,
     ) -> HitTester {
         HitTester::new(
             &self.hit_testing_runs,
             clip_scroll_tree,
             &self.clip_store,
             clip_data_store,
         )
     }
 }
-
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -1,30 +1,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{DeviceRect, FilterOp, MixBlendMode, PipelineId, PremultipliedColorF, PictureRect, PicturePoint, WorldPoint};
 use api::{DeviceIntRect, DevicePoint, LayoutRect, PictureToRasterTransform, LayoutPixel, PropertyBinding, PropertyBindingId};
 use api::{DevicePixelScale, RasterRect, RasterSpace, ColorF, ImageKey, DirtyRect, WorldSize, ClipMode};
 use api::{PicturePixel, RasterPixel, WorldPixel, WorldRect, ImageFormat, ImageDescriptor, WorldVector2D, LayoutPoint};
+#[cfg(feature = "debug_renderer")]
+use api::{DebugFlags, DeviceVector2D};
 use box_shadow::{BLUR_SAMPLE_SCALE};
 use clip::{ClipNodeCollector, ClipStore, ClipChainId, ClipChainNode, ClipItem};
 use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex, CoordinateSystemId};
+#[cfg(feature = "debug_renderer")]
+use debug_colors;
 use device::TextureFilter;
 use euclid::{TypedScale, vec3, TypedRect, TypedPoint2D, TypedSize2D};
 use euclid::approxeq::ApproxEq;
 use intern::ItemUid;
 use internal_types::{FastHashMap, FastHashSet, PlaneSplitter};
 use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PictureContext};
 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
 use gpu_types::{TransformPalette, TransformPaletteId, UvRectKind};
 use plane_split::{Clipper, Polygon, Splitter};
 use prim_store::{PictureIndex, PrimitiveInstance, SpaceMapper, VisibleFace, PrimitiveInstanceKind};
-use prim_store::{get_raster_rects, CoordinateSpaceMapping};
+use prim_store::{get_raster_rects, CoordinateSpaceMapping, PrimitiveScratchBuffer};
 use prim_store::{OpacityBindingStorage, ImageInstanceStorage, OpacityBindingIndex};
 use print_tree::PrintTreePrinter;
 use render_backend::FrameResources;
 use render_task::{ClearMode, RenderTask, RenderTaskCacheEntryHandle, TileBlit};
 use render_task::{RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation};
 use resource_cache::ResourceCache;
 use scene::{FilterOpHelpers, SceneProperties};
 use scene_builder::DocumentResources;
@@ -976,16 +980,17 @@ impl TileCache {
     /// Apply any updates after prim dependency updates. This applies
     /// any late tile invalidations, and sets up the dirty rect and
     /// set of tile blits.
     pub fn post_update(
         &mut self,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         frame_context: &FrameBuildingContext,
+        _scratch: &mut PrimitiveScratchBuffer,
     ) -> LayoutRect {
         let mut dirty_world_rect = WorldRect::zero();
 
         self.dirty_region = None;
         self.pending_blits.clear();
 
         let descriptor = ImageDescriptor::new(
             TILE_SIZE_WIDTH,
@@ -1056,16 +1061,39 @@ impl TileCache {
             // If there are no primitives there is no need to draw or cache it.
             if tile.descriptor.prims.is_empty() {
                 continue;
             }
 
             // Decide how to handle this tile when drawing this frame.
             if tile.is_valid {
                 self.tiles_to_draw.push(TileIndex(i));
+
+                #[cfg(feature = "debug_renderer")]
+                {
+                    if frame_context.debug_flags.contains(DebugFlags::PICTURE_CACHING_DBG) {
+                        let tile_device_rect = tile.world_rect * frame_context.device_pixel_scale;
+                        let mut label_pos = tile_device_rect.origin + DeviceVector2D::new(20.0, 30.0);
+                        _scratch.push_debug_rect(
+                            tile_device_rect,
+                            debug_colors::GREEN,
+                        );
+                        _scratch.push_debug_string(
+                            label_pos,
+                            debug_colors::WHITE,
+                            format!("{:?}", tile.id),
+                        );
+                        label_pos.y += 20.0;
+                        _scratch.push_debug_string(
+                            label_pos,
+                            debug_colors::WHITE,
+                            format!("same: {} frames", tile.same_frames),
+                        );
+                    }
+                }
             } else {
                 // Add the tile rect to the dirty rect.
                 dirty_world_rect = dirty_world_rect.union(&visible_rect);
 
                 // Only cache tiles that have had the same content for at least two
                 // frames. This skips caching on pages / benchmarks that are changing
                 // every frame, which is wasteful.
                 if tile.same_frames > FRAMES_BEFORE_CACHING {
@@ -1108,20 +1136,31 @@ impl TileCache {
                 }
             }
         }
 
         // Store the dirty region for drawing the main scene.
         self.dirty_region = if dirty_world_rect.is_empty() {
             None
         } else {
-            let dirty_device_rect = (dirty_world_rect * frame_context.device_pixel_scale).round().to_i32();
+            let dirty_device_rect = dirty_world_rect * frame_context.device_pixel_scale;
+
+            #[cfg(feature = "debug_renderer")]
+            {
+                if frame_context.debug_flags.contains(DebugFlags::PICTURE_CACHING_DBG) {
+                    _scratch.push_debug_rect(
+                        dirty_device_rect,
+                        debug_colors::RED,
+                    );
+                }
+            }
+
             Some(DirtyRegion {
                 dirty_world_rect,
-                dirty_device_rect,
+                dirty_device_rect: dirty_device_rect.round().to_i32(),
             })
         };
 
         local_clip_rect
     }
 }
 
 /// State structure that is used during the tile cache update picture traversal.
--- a/gfx/wr/webrender/src/prim_store/mod.rs
+++ b/gfx/wr/webrender/src/prim_store/mod.rs
@@ -5,21 +5,25 @@
 use api::{BorderRadius, ClipMode, ColorF, PictureRect, ColorU, LayoutVector2D};
 use api::{DeviceIntRect, DevicePixelScale, DeviceRect};
 use api::{FilterOp, ImageRendering, TileOffset, RepeatMode};
 use api::{LayoutPoint, LayoutRect, LayoutSideOffsets, LayoutSize};
 use api::{PremultipliedColorF, PropertyBinding, Shadow};
 use api::{WorldPixel, BoxShadowClipMode, WorldRect, LayoutToWorldScale};
 use api::{PicturePixel, RasterPixel, LineStyle, LineOrientation, AuHelpers};
 use api::LayoutPrimitiveInfo;
+#[cfg(feature = "debug_renderer")]
+use api::DevicePoint;
 use border::{get_max_scale_for_border, build_border_instances};
 use border::BorderSegmentCacheKey;
 use clip::{ClipStore};
 use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex};
 use clip::{ClipDataStore, ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItem, ClipNodeCollector};
+#[cfg(feature = "debug_renderer")]
+use debug_render::DebugItem;
 use display_list_flattener::{AsInstanceKind, CreateShadow, IsVisible};
 use euclid::{SideOffsets2D, TypedTransform3D, TypedRect, TypedScale, TypedSize2D};
 use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
 use frame_builder::PrimitiveContext;
 use glyph_rasterizer::GlyphKey;
 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks};
 use gpu_types::BrushFlags;
 use image::{Repetition};
@@ -1487,53 +1491,91 @@ pub struct PrimitiveScratchBuffer {
     /// A list of segment ranges and GPU cache handles for prim instances
     /// that have opted into segment building. In future, this should be
     /// removed in favor of segment building during primitive interning.
     pub segment_instances: SegmentInstanceStorage,
 
     /// A list of visible tiles that tiled gradients use to store
     /// per-tile information.
     pub gradient_tiles: GradientTileStorage,
+
+    #[cfg(feature = "debug_renderer")]
+    pub debug_items: Vec<DebugItem>,
 }
 
 impl PrimitiveScratchBuffer {
     pub fn new() -> Self {
         PrimitiveScratchBuffer {
             clip_mask_instances: Vec::new(),
             glyph_keys: GlyphKeyStorage::new(0),
             border_cache_handles: BorderHandleStorage::new(0),
             segments: SegmentStorage::new(0),
             segment_instances: SegmentInstanceStorage::new(0),
             gradient_tiles: GradientTileStorage::new(0),
+            #[cfg(feature = "debug_renderer")]
+            debug_items: Vec::new(),
         }
     }
 
     pub fn recycle(&mut self) {
         recycle_vec(&mut self.clip_mask_instances);
         self.glyph_keys.recycle();
         self.border_cache_handles.recycle();
         self.segments.recycle();
         self.segment_instances.recycle();
         self.gradient_tiles.recycle();
+        #[cfg(feature = "debug_renderer")]
+        recycle_vec(&mut self.debug_items);
     }
 
     pub fn begin_frame(&mut self) {
         // Clear the clip mask tasks for the beginning of the frame. Append
         // a single kind representing no clip mask, at the ClipTaskIndex::INVALID
         // location.
         self.clip_mask_instances.clear();
         self.clip_mask_instances.push(ClipMaskKind::None);
 
         self.border_cache_handles.clear();
 
         // TODO(gw): As in the previous code, the gradient tiles store GPU cache
         //           handles that are cleared (and thus invalidated + re-uploaded)
         //           every frame. This maintains the existing behavior, but we
         //           should fix this in the future to retain handles.
         self.gradient_tiles.clear();
+
+        #[cfg(feature = "debug_renderer")]
+        self.debug_items.clear();
+    }
+
+    #[allow(dead_code)]
+    #[cfg(feature = "debug_renderer")]
+    pub fn push_debug_rect(
+        &mut self,
+        rect: DeviceRect,
+        color: ColorF,
+    ) {
+        self.debug_items.push(DebugItem::Rect {
+            rect,
+            color: color.into(),
+        });
+    }
+
+    #[allow(dead_code)]
+    #[cfg(feature = "debug_renderer")]
+    pub fn push_debug_string(
+        &mut self,
+        position: DevicePoint,
+        color: ColorF,
+        msg: String,
+    ) {
+        self.debug_items.push(DebugItem::Text {
+            position,
+            color: color.into(),
+            msg,
+        });
     }
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Clone, Debug)]
 pub struct PrimitiveStoreStats {
     picture_count: usize,
@@ -1658,16 +1700,17 @@ impl PrimitiveStore {
         state: &mut TileCacheUpdateState,
         frame_context: &FrameBuildingContext,
         resource_cache: &mut ResourceCache,
         resources: &FrameResources,
         clip_store: &ClipStore,
         surfaces: &[SurfaceInfo],
         gpu_cache: &mut GpuCache,
         retained_tiles: &mut RetainedTiles,
+        scratch: &mut PrimitiveScratchBuffer,
     ) {
         let children = {
             let pic = &mut self.pictures[pic_index.0];
             // Only update the tile cache if we ended up selecting tile caching for the
             // composite mode of this picture. In some cases, even if the requested
             // composite mode was tile caching, WR may choose not to draw this picture
             // with tile cache enabled. For now, this is only in the case of very large
             // picture rects, but in future we may do it for performance reasons too.
@@ -1709,28 +1752,30 @@ impl PrimitiveStore {
                 state,
                 frame_context,
                 resource_cache,
                 resources,
                 clip_store,
                 surfaces,
                 gpu_cache,
                 retained_tiles,
+                scratch,
             );
         }
 
         let pic = &mut self.pictures[pic_index.0];
         if let Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { .. }, .. }) = pic.raster_config {
             let mut tile_cache = state.tile_cache.take().unwrap();
 
             // Build the dirty region(s) for this tile cache.
             pic.local_clip_rect = tile_cache.post_update(
                 resource_cache,
                 gpu_cache,
                 frame_context,
+                scratch,
             );
 
             pic.tile_cache = Some(tile_cache);
         }
 
         pic.prim_list.pictures = children;
     }
 
--- a/gfx/wr/webrender/src/render_backend.rs
+++ b/gfx/wr/webrender/src/render_backend.rs
@@ -470,16 +470,17 @@ impl Document {
         DocumentOps::nop()
     }
 
     fn build_frame(
         &mut self,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         resource_profile: &mut ResourceProfileCounters,
+        debug_flags: DebugFlags,
     ) -> RenderedDocument {
         let accumulated_scale_factor = self.view.accumulated_scale_factor();
         let pan = self.view.pan.to_f32() / accumulated_scale_factor;
 
         // Advance to the next frame.
         self.stamp.advance();
 
         assert!(self.stamp.frame_id() != FrameId::INVALID,
@@ -496,16 +497,17 @@ impl Document {
                 accumulated_scale_factor,
                 self.view.layer,
                 pan,
                 &mut resource_profile.texture_cache,
                 &mut resource_profile.gpu_cache,
                 &self.dynamic_properties,
                 &mut self.resources,
                 &mut self.scratch,
+                debug_flags,
             );
             self.hit_tester = Some(frame_builder.create_hit_tester(
                 &self.clip_scroll_tree,
                 &self.resources.clip_data_store,
             ));
             frame
         };
 
@@ -1375,16 +1377,17 @@ impl RenderBackend {
             let (pending_update, rendered_document) = {
                 let _timer = profile_counters.total_time.timer();
                 let frame_build_start_time = precise_time_ns();
 
                 let rendered_document = doc.build_frame(
                     &mut self.resource_cache,
                     &mut self.gpu_cache,
                     &mut profile_counters.resources,
+                    self.debug_flags,
                 );
 
                 debug!("generated frame for document {:?} with {} passes",
                     document_id, rendered_document.frame.passes.len());
 
                 let msg = ResultMsg::UpdateGpuCache(self.gpu_cache.extract_updates());
                 self.result_tx.send(msg).unwrap();
 
@@ -1632,16 +1635,17 @@ impl RenderBackend {
                 let file_name = format!("scene-{}-{}", (id.0).0, id.1);
                 config.serialize(&doc.scene, file_name);
             }
             if config.bits.contains(CaptureBits::FRAME) {
                 let rendered_document = doc.build_frame(
                     &mut self.resource_cache,
                     &mut self.gpu_cache,
                     &mut profile_counters.resources,
+                    self.debug_flags,
                 );
                 //TODO: write down doc's pipeline info?
                 // it has `pipeline_epoch_map`,
                 // which may capture necessary details for some cases.
                 let file_name = format!("frame-{}-{}", (id.0).0, id.1);
                 config.serialize(&rendered_document.frame, file_name);
             }
 
--- a/gfx/wr/webrender/src/renderer.rs
+++ b/gfx/wr/webrender/src/renderer.rs
@@ -31,16 +31,18 @@ use api::{RenderApiSender, RenderNotifie
 use api::{channel};
 use api::DebugCommand;
 pub use api::DebugFlags;
 use api::channel::PayloadReceiverHelperMethods;
 use batch::{BatchKind, BatchTextures, BrushBatchKind};
 #[cfg(any(feature = "capture", feature = "replay"))]
 use capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage};
 use debug_colors;
+#[cfg(feature = "debug_renderer")]
+use debug_render::DebugItem;
 use device::{DepthFunction, Device, GpuFrameId, Program, UploadMethod, Texture, PBO};
 use device::{DrawTarget, ExternalTexture, FBOId, ReadTarget, TextureSlot};
 use device::{ShaderError, TextureFilter, TextureFlags,
              VertexUsageHint, VAO, VBO, CustomVAO};
 use device::{ProgramCache, ReadPixelsFormat};
 #[cfg(feature = "debug_renderer")]
 use euclid::rect;
 use euclid::Transform3D;
@@ -4189,16 +4191,17 @@ impl Renderer {
             );
         }
 
         self.texture_resolver.end_frame(&mut self.device, frame_id);
 
         #[cfg(feature = "debug_renderer")]
         {
             if let Some(framebuffer_size) = framebuffer_size {
+                self.draw_frame_debug_items(&frame.debug_items);
                 self.draw_render_target_debug(framebuffer_size);
                 self.draw_texture_cache_debug(framebuffer_size);
                 self.draw_gpu_cache_debug(framebuffer_size);
             }
             self.draw_epoch_debug();
         }
 
         // Garbage collect any frame outputs that weren't used this frame.
@@ -4242,16 +4245,56 @@ impl Renderer {
         self.debug_flags = flags;
     }
 
     pub fn save_cpu_profile(&self, filename: &str) {
         write_profile(filename);
     }
 
     #[cfg(feature = "debug_renderer")]
+    fn draw_frame_debug_items(&mut self, items: &[DebugItem]) {
+        let debug_renderer = match self.debug.get_mut(&mut self.device) {
+            Some(render) => render,
+            None => return,
+        };
+
+        for item in items {
+            match item {
+                DebugItem::Rect { rect, color } => {
+                    let inner_color = color.scale_alpha(0.2).into();
+                    let outer_color = (*color).into();
+
+                    debug_renderer.add_quad(
+                        rect.origin.x,
+                        rect.origin.y,
+                        rect.origin.x + rect.size.width,
+                        rect.origin.y + rect.size.height,
+                        inner_color,
+                        inner_color,
+                    );
+
+                    debug_renderer.add_rect(
+                        &rect.to_i32(),
+                        outer_color,
+                    );
+                }
+                DebugItem::Text { ref msg, position, color } => {
+                    debug_renderer.add_text(
+                        position.x,
+                        position.y,
+                        msg,
+                        (*color).into(),
+                        None,
+                    );
+                }
+            }
+        }
+    }
+
+    #[cfg(feature = "debug_renderer")]
     fn draw_render_target_debug(&mut self, framebuffer_size: DeviceIntSize) {
         if !self.debug_flags.contains(DebugFlags::RENDER_TARGET_DBG) {
             return;
         }
 
         let debug_renderer = match self.debug.get_mut(&mut self.device) {
             Some(render) => render,
             None => return,
--- a/gfx/wr/webrender/src/tiling.rs
+++ b/gfx/wr/webrender/src/tiling.rs
@@ -3,16 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{ColorF, BorderStyle, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixelScale};
 use api::{DocumentLayer, FilterOp, ImageFormat};
 use api::{MixBlendMode, PipelineId, DeviceRect, LayoutSize};
 use batch::{AlphaBatchBuilder, AlphaBatchContainer, ClipBatcher, resolve_image};
 use clip::ClipStore;
 use clip_scroll_tree::{ClipScrollTree};
+#[cfg(feature = "debug_renderer")]
+use debug_render::DebugItem;
 use device::{Texture};
 #[cfg(feature = "pathfinder")]
 use euclid::{TypedPoint2D, TypedVector2D};
 use gpu_cache::{GpuCache};
 use gpu_types::{BorderInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance};
 use gpu_types::{TransformData, TransformPalette, ZBufferIdGenerator};
 use internal_types::{CacheTextureId, FastHashMap, SavedTargetIndex, TextureSource};
 #[cfg(feature = "pathfinder")]
@@ -1109,16 +1111,20 @@ pub struct Frame {
 
     /// True if this frame contains any render tasks
     /// that write to the texture cache.
     pub has_texture_cache_tasks: bool,
 
     /// True if this frame has been drawn by the
     /// renderer.
     pub has_been_rendered: bool,
+
+    /// Debugging information to overlay for this frame.
+    #[cfg(feature = "debug_renderer")]
+    pub debug_items: Vec<DebugItem>,
 }
 
 impl Frame {
     // This frame must be flushed if it writes to the
     // texture cache, and hasn't been drawn yet.
     pub fn must_be_drawn(&self) -> bool {
         self.has_texture_cache_tasks && !self.has_been_rendered
     }
--- a/gfx/wr/webrender_api/src/api.rs
+++ b/gfx/wr/webrender_api/src/api.rs
@@ -979,16 +979,18 @@ bitflags! {
         /// Show an indicator that moves every time a scene is built.
         const NEW_SCENE_INDICATOR   = 1 << 10;
         /// Show an overlay displaying overdraw amount.
         const SHOW_OVERDRAW         = 1 << 11;
         /// Display the contents of GPU cache.
         const GPU_CACHE_DBG         = 1 << 12;
         const SLOW_FRAME_INDICATOR  = 1 << 13;
         const TEXTURE_CACHE_DBG_CLEAR_EVICTED = 1 << 14;
+        /// Show picture caching debug overlay
+        const PICTURE_CACHING_DBG   = 1 << 15;
     }
 }
 
 pub struct RenderApi {
     api_sender: MsgSender<ApiMsg>,
     payload_sender: PayloadSender,
     namespace_id: IdNamespace,
     next_id: Cell<ResourceId>,