| author | Glenn Watson <gw@intuitionlibrary.com> |
| Mon, 16 Mar 2020 03:47:53 +0000 | |
| changeset 518887 | 6199f7b91e8bde7b7965585842d7a72e2e406e3e |
| parent 518886 | 0fdeafcdcb41a6e7def0650ef1d93e3e5bef9299 |
| child 518888 | 2786da95404d56478d501b0e15aefaa68ce9e02d |
| push id | 37218 |
| push user | rmaries@mozilla.com |
| push date | Mon, 16 Mar 2020 09:28:04 +0000 |
| treeherder | mozilla-central@6199f7b91e8b [default view] [failures only] |
| perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
| reviewers | lsalzman, Bert |
| bugs | 1621390 |
| milestone | 76.0a1 |
| first release with | nightly linux32
6199f7b91e8b
/
76.0a1
/
20200316092804
/
files
nightly linux64
6199f7b91e8b
/
76.0a1
/
20200316092804
/
files
nightly mac
6199f7b91e8b
/
76.0a1
/
20200316092804
/
files
nightly win32
6199f7b91e8b
/
76.0a1
/
20200316092804
/
files
nightly win64
6199f7b91e8b
/
76.0a1
/
20200316092804
/
files
|
| last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
| releases | nightly linux32
76.0a1
/
20200316092804
/
pushlog to previous
nightly linux64
76.0a1
/
20200316092804
/
pushlog to previous
nightly mac
76.0a1
/
20200316092804
/
pushlog to previous
nightly win32
76.0a1
/
20200316092804
/
pushlog to previous
nightly win64
76.0a1
/
20200316092804
/
pushlog to previous
|
--- a/gfx/wr/webrender/src/batch.rs +++ b/gfx/wr/webrender/src/batch.rs @@ -14,17 +14,18 @@ use crate::gpu_types::{BrushFlags, Brush use crate::gpu_types::{ClipMaskInstance, SplitCompositeInstance, BrushShaderKind}; use crate::gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance}; use crate::gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette}; use crate::gpu_types::{ImageBrushData, get_shader_opacity}; use crate::internal_types::{FastHashMap, SavedTargetIndex, Swizzle, TextureSource, Filter}; use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive}; use crate::prim_store::{DeferredResolve, EdgeAaSegmentMask, PrimitiveInstanceKind, PrimitiveVisibilityIndex, PrimitiveVisibilityMask}; use crate::prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex}; -use crate::prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex, VECS_PER_SEGMENT, SpaceMapper}; +use crate::prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex, PrimitiveVisibilityFlags}; +use crate::prim_store::{VECS_PER_SEGMENT, SpaceMapper}; use crate::prim_store::image::ImageSource; use crate::render_target::RenderTargetContext; use crate::render_task_graph::{RenderTaskId, RenderTaskGraph}; use crate::render_task::RenderTaskAddress; use crate::renderer::{BlendMode, ImageBufferKind, ShaderColorMode}; use crate::renderer::{BLOCKS_PER_UV_RECT, MAX_VERTEX_TEXTURE_WIDTH}; use crate::resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache}; use smallvec::SmallVec; @@ -188,16 +189,26 @@ impl AlphaBatchList { item_rects: Vec::new(), current_z_id: ZBufferId::invalid(), current_batch_index: usize::MAX, break_advanced_blend_batches, lookback_count, } } + /// Clear all current batches in this list. This is typically used + /// when a primitive is encountered that occludes all previous + /// content in this batch list. + fn clear(&mut self) { + self.current_batch_index = usize::MAX; + self.current_z_id = ZBufferId::invalid(); + self.batches.clear(); + self.item_rects.clear(); + } + pub fn set_params_and_get_batch( &mut self, key: BatchKey, features: BatchFeatures, // The bounding box of everything at this Z plane. We expect potentially // multiple primitive segments coming with the same `z_id`. z_bounding_rect: &PictureRect, z_id: ZBufferId, @@ -285,16 +296,24 @@ impl OpaqueBatchList { OpaqueBatchList { batches: Vec::new(), pixel_area_threshold_for_new_batch, current_batch_index: usize::MAX, lookback_count, } } + /// Clear all current batches in this list. This is typically used + /// when a primitive is encountered that occludes all previous + /// content in this batch list. + fn clear(&mut self) { + self.current_batch_index = usize::MAX; + self.batches.clear(); + } + pub fn set_params_and_get_batch( &mut self, key: BatchKey, features: BatchFeatures, // The bounding box of everything at the current Z, whatever it is. We expect potentially // multiple primitive segments produced by a primitive, which we allow to check // `current_batch_index` instead of iterating the batches. z_bounding_rect: &PictureRect, @@ -497,16 +516,24 @@ impl AlphaBatchBuilder { alpha_batch_list: AlphaBatchList::new(break_advanced_blend_batches, lookback_count), opaque_batch_list: OpaqueBatchList::new(batch_area_threshold, lookback_count), render_task_id, render_task_address, vis_mask, } } + /// Clear all current batches in this builder. This is typically used + /// when a primitive is encountered that occludes all previous + /// content in this batch list. + fn clear(&mut self) { + self.alpha_batch_list.clear(); + self.opaque_batch_list.clear(); + } + pub fn build( mut self, batch_containers: &mut Vec<AlphaBatchContainer>, merged_batches: &mut AlphaBatchContainer, task_rect: DeviceIntRect, task_scissor_rect: Option<DeviceIntRect>, ) { self.opaque_batch_list.finalize(); @@ -648,16 +675,24 @@ impl BatchBuilder { polygons_address, z: z_id, }), ); } } } + /// Clear all current batchers. This is typically used when a primitive + /// is encountered that occludes all previous content in this batch list. + fn clear_batches(&mut self) { + for batcher in &mut self.batchers { + batcher.clear(); + } + } + /// Add a picture to a given batch builder. pub fn add_pic_to_batch( &mut self, pic: &PicturePrimitive, ctx: &RenderTargetContext, gpu_cache: &mut GpuCache, render_tasks: &RenderTaskGraph, deferred_resolves: &mut Vec<DeferredResolve>, @@ -726,16 +761,26 @@ impl BatchBuilder { // TODO(gw): Calculating this for every primitive is a bit // wasteful. We should probably cache this in // the scroll node... let transform_kind = transform_id.transform_kind(); let prim_info = &ctx.scratch.prim_info[prim_instance.visibility_info.0 as usize]; let bounding_rect = &prim_info.clip_chain.pic_clip_rect; + // If this primitive is a backdrop, that means that it is known to cover + // the entire picture cache background. In that case, the renderer will + // use the backdrop color as a clear color, and so we can drop this + // primitive and any prior primitives from the batch lists for this + // picture cache slice. + if prim_info.flags.contains(PrimitiveVisibilityFlags::IS_BACKDROP) { + self.clear_batches(); + return; + } + let z_id = z_generator.next(); let prim_common_data = &ctx.data_stores.as_common_data(&prim_instance); let prim_rect = LayoutRect::new( prim_instance.prim_origin, prim_common_data.prim_size );
--- a/gfx/wr/webrender/src/frame_builder.rs +++ b/gfx/wr/webrender/src/frame_builder.rs @@ -10,17 +10,17 @@ use crate::spatial_tree::{SpatialTree, R use crate::composite::{CompositorKind, CompositeState}; use crate::debug_render::DebugItem; use crate::gpu_cache::{GpuCache, GpuCacheHandle}; use crate::gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind, ZBufferIdGenerator}; use crate::gpu_types::TransformData; use crate::internal_types::{FastHashMap, PlaneSplitter, SavedTargetIndex}; use crate::picture::{PictureUpdateState, SurfaceInfo, ROOT_SURFACE_INDEX, SurfaceIndex, RecordedDirtyRegion}; use crate::picture::{RetainedTiles, TileCacheInstance, DirtyRegion, SurfaceRenderTasks, SubpixelMode}; -use crate::picture::{TileCacheLogger}; +use crate::picture::{BackdropKind, TileCacheLogger}; use crate::prim_store::{SpaceMapper, PictureIndex, PrimitiveDebugId, PrimitiveScratchBuffer}; use crate::prim_store::{DeferredResolve, PrimitiveVisibilityMask}; use crate::profiler::{FrameProfileCounters, TextureCacheProfileCounters, ResourceProfileCounters}; use crate::render_backend::{DataStores, FrameStamp, FrameId}; use crate::render_target::{RenderTarget, PictureCacheTarget, TextureCacheRenderTarget}; use crate::render_target::{RenderTargetContext, RenderTargetKind}; use crate::render_task_graph::{RenderTaskId, RenderTaskGraph, RenderTaskGraphCounters}; use crate::render_task_graph::{RenderPassKind, RenderPass}; @@ -878,24 +878,29 @@ pub fn build_render_pass( // first layer even if it's technically transparent. // Otherwise, clear to transparent and composite with alpha. // TODO(gw): We can detect per-tile opacity for the clear color here // which might be a significant win on some pages? let forced_opaque = match tile_cache.background_color { Some(color) => color.a >= 1.0, None => false, }; - // TODO(gw): Once we have multiple slices enabled, take advantage of - // option to skip clears if the slice is opaque. - let clear_color = if forced_opaque { + let mut clear_color = if forced_opaque { Some(ColorF::WHITE) } else { Some(ColorF::TRANSPARENT) }; + // If this picture cache has a valid color backdrop, we will use + // that as the clear color, skipping the draw of the backdrop + // primitive (and anything prior to it) during batching. + if let Some(BackdropKind::Color { color }) = tile_cache.backdrop.kind { + clear_color = Some(color); + } + // Create an alpha batcher for each of the tasks of this picture. let mut batchers = Vec::new(); for task_id in &task_ids { let task_id = *task_id; let vis_mask = match render_tasks[task_id].kind { RenderTaskKind::Picture(ref info) => info.vis_mask, _ => unreachable!(), };
--- a/gfx/wr/webrender/src/picture.rs +++ b/gfx/wr/webrender/src/picture.rs @@ -115,17 +115,17 @@ use crate::internal_types::{FastHashMap, use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PictureContext}; use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle}; use crate::gpu_types::{UvRectKind, ZBufferId}; use plane_split::{Clipper, Polygon, Splitter}; use crate::prim_store::{SpaceMapper, PrimitiveVisibilityMask, PointKey, PrimitiveTemplateKind}; use crate::prim_store::{SpaceSnapper, PictureIndex, PrimitiveInstance, PrimitiveInstanceKind}; use crate::prim_store::{get_raster_rects, PrimitiveScratchBuffer, RectangleKey}; use crate::prim_store::{OpacityBindingStorage, ImageInstanceStorage, OpacityBindingIndex}; -use crate::prim_store::{ColorBindingStorage, ColorBindingIndex}; +use crate::prim_store::{ColorBindingStorage, ColorBindingIndex, PrimitiveVisibilityFlags}; use crate::print_tree::{PrintTree, PrintTreePrinter}; use crate::render_backend::DataStores; use crate::render_task_graph::RenderTaskId; use crate::render_target::RenderTargetKind; use crate::render_task::{RenderTask, RenderTaskLocation, BlurTaskCache, ClearMode}; use crate::resource_cache::{ResourceCache, ImageGeneration}; use crate::scene::SceneProperties; use crate::spatial_tree::CoordinateSystemId; @@ -1247,17 +1247,17 @@ impl Tile { .unwrap_or_else(DeviceRect::zero); // Check if this tile can be considered opaque. Opacity state must be updated only // after all early out checks have been performed. Otherwise, we might miss updating // the native surface next time this tile becomes visible. let clipped_rect = self.current_descriptor.local_valid_rect .intersection(&ctx.local_clip_rect) .unwrap_or_else(PictureRect::zero); - let mut is_opaque = ctx.backdrop.rect.contains_rect(&clipped_rect); + let mut is_opaque = ctx.backdrop.opaque_rect.contains_rect(&clipped_rect); if self.has_compositor_surface { // If we found primitive(s) that are ordered _after_ the first compositor // surface, _and_ intersect with any compositor surface, then we will need // to draw this tile with alpha blending, as an overlay to the compositor surface. let fg_world_valid_rect = ctx.pic_to_world_mapper .map(&self.fg_local_valid_rect) .expect("bug: map fg local valid rect"); @@ -1333,36 +1333,36 @@ impl Tile { } // See if this tile is a simple color, in which case we can just draw // it as a rect, and avoid allocating a texture surface and drawing it. // TODO(gw): Initial native compositor interface doesn't support simple // color tiles. We can definitely support this in DC, so this // should be added as a follow up. let is_simple_prim = - ctx.backdrop.kind.can_be_promoted_to_compositor_surface() && + ctx.backdrop.kind.is_some() && self.current_descriptor.prims.len() == 1 && self.is_opaque && supports_simple_prims; // Set up the backing surface for this tile. let surface = if is_simple_prim { // If we determine the tile can be represented by a color, set the // surface unconditionally (this will drop any previously used // texture cache backing surface). match ctx.backdrop.kind { - BackdropKind::Color { color } => { + Some(BackdropKind::Color { color }) => { TileSurface::Color { color, } } - BackdropKind::Clear => { + Some(BackdropKind::Clear) => { TileSurface::Clear } - BackdropKind::Image => { + None => { // This should be prevented by the is_simple_prim check above. unreachable!(); } } } else { // If this tile will be backed by a surface, we want to retain // the texture handle from the previous frame, if possible. If // the tile was previously a color, or not set, then just set @@ -1802,52 +1802,39 @@ impl ::std::fmt::Display for RecordedDir impl ::std::fmt::Debug for RecordedDirtyRegion { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { ::std::fmt::Display::fmt(self, f) } } #[derive(Debug, Copy, Clone)] -enum BackdropKind { +pub enum BackdropKind { Color { color: ColorF, }, Clear, - Image, -} - -impl BackdropKind { - /// Returns true if the compositor can directly draw this backdrop. - fn can_be_promoted_to_compositor_surface(&self) -> bool { - match self { - BackdropKind::Color { .. } | BackdropKind::Clear => true, - BackdropKind::Image => false, - } - } } /// Stores information about the calculated opaque backdrop of this slice. #[derive(Debug, Copy, Clone)] -struct BackdropInfo { +pub struct BackdropInfo { /// The picture space rectangle that is known to be opaque. This is used /// to determine where subpixel AA can be used, and where alpha blending /// can be disabled. - rect: PictureRect, + pub opaque_rect: PictureRect, /// Kind of the backdrop - kind: BackdropKind, + pub kind: Option<BackdropKind>, } impl BackdropInfo { fn empty() -> Self { BackdropInfo { - rect: PictureRect::zero(), - kind: BackdropKind::Color { - color: ColorF::BLACK, - }, + opaque_rect: PictureRect::zero(), + kind: None, } } } #[derive(Clone)] pub struct TileCacheLoggerSlice { pub serialized_slice: String, pub local_to_world_transform: Transform3D<f32, PicturePixel, WorldPixel>, @@ -2210,17 +2197,17 @@ pub struct TileCacheInstance { /// The local clip rect, from the shared clips of this picture. local_clip_rect: PictureRect, /// The surface index that this tile cache will be drawn into. surface_index: SurfaceIndex, /// The background color from the renderer. If this is set opaque, we know it's /// fine to clear the tiles to this and allow subpixel text on the first slice. pub background_color: Option<ColorF>, /// Information about the calculated backdrop content of this cache. - backdrop: BackdropInfo, + pub backdrop: BackdropInfo, /// The allowed subpixel mode for this surface, which depends on the detected /// opacity of the background. pub subpixel_mode: SubpixelMode, /// A list of clip handles that exist on every (top-level) primitive in this picture. /// It's often the case that these are root / fixed position clips. By handling them /// here, we can avoid applying them to the items, which reduces work, but more importantly /// reduces invalidations. pub shared_clips: Vec<ClipDataHandle>, @@ -2825,41 +2812,41 @@ impl TileCacheInstance { clip_store: &ClipStore, pictures: &[PicturePrimitive], resource_cache: &mut ResourceCache, opacity_binding_store: &OpacityBindingStorage, color_bindings: &ColorBindingStorage, image_instances: &ImageInstanceStorage, surface_stack: &[SurfaceIndex], composite_state: &mut CompositeState, - ) -> bool { + ) -> Option<PrimitiveVisibilityFlags> { // This primitive exists on the last element on the current surface stack. let prim_surface_index = *surface_stack.last().unwrap(); // If the primitive is completely clipped out by the clip chain, there // is no need to add it to any primitive dependencies. let prim_clip_chain = match prim_clip_chain { Some(prim_clip_chain) => prim_clip_chain, - None => return false, + None => return None, }; self.map_local_to_surface.set_target_spatial_node( prim_spatial_node_index, frame_context.spatial_tree, ); // Map the primitive local rect into picture space. let prim_rect = match self.map_local_to_surface.map(&local_prim_rect) { Some(rect) => rect, - None => return false, + None => return None, }; // If the rect is invalid, no need to create dependencies. if prim_rect.size.is_empty_or_negative() { - return false; + return None; } // If the primitive is directly drawn onto this picture cache surface, then // the pic_clip_rect is in the same space. If not, we need to map it from // the surface space into the picture cache space. let on_picture_surface = prim_surface_index == self.surface_index; let pic_clip_rect = if on_picture_surface { prim_clip_chain.pic_clip_rect @@ -2889,33 +2876,33 @@ impl TileCacheInstance { // Map the rect into the parent surface, and inflate if this surface requires // it. If the rect can't be mapping (e.g. due to an invalid transform) then // just bail out from the dependencies and cull this primitive. current_pic_clip_rect = match map_local_to_surface.map(¤t_pic_clip_rect) { Some(rect) => { rect.inflate(surface.inflation_factor, surface.inflation_factor) } None => { - return false; + return None; } }; current_spatial_node_index = surface.surface_spatial_node_index; } current_pic_clip_rect }; // Get the tile coordinates in the picture space. let (p0, p1) = self.get_tile_coords_for_rect(&pic_clip_rect); // If the primitive is outside the tiling rects, it's known to not // be visible. if p0.x == p1.x || p0.y == p1.y { - return false; + return None; } // Build the list of resources that this primitive has dependencies on. let mut prim_info = PrimitiveDependencyInfo::new( prim_instance.uid(), prim_rect.origin, pic_clip_rect, ); @@ -2970,17 +2957,20 @@ impl TileCacheInstance { // case for background rects is that they don't have animated opacity. let color = match data_stores.prim[data_handle].kind { PrimitiveTemplateKind::Rectangle { color, .. } => { frame_context.scene_properties.resolve_color(&color) } _ => unreachable!(), }; if color.a >= 1.0 { - backdrop_candidate = Some(BackdropKind::Color { color }); + backdrop_candidate = Some(BackdropInfo { + opaque_rect: pic_clip_rect, + kind: Some(BackdropKind::Color { color }), + }); } } else { let opacity_binding = &opacity_binding_store[opacity_binding_index]; for binding in &opacity_binding.bindings { prim_info.opacity_bindings.push(OpacityBinding::from(*binding)); } } @@ -2992,19 +2982,27 @@ impl TileCacheInstance { } PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => { let image_data = &data_stores.image[data_handle].kind; let image_instance = &image_instances[image_instance_index]; let opacity_binding_index = image_instance.opacity_binding_index; if opacity_binding_index == OpacityBindingIndex::INVALID { if let Some(image_properties) = resource_cache.get_image_properties(image_data.key) { - // If this image is opaque, it can be considered as a possible opaque backdrop - if image_properties.descriptor.is_opaque() { - backdrop_candidate = Some(BackdropKind::Image); + // For an image to be a possible opaque backdrop, it must: + // - Have a valid, opaque image descriptor + // - Not use tiling (since they can fail to draw) + // - Not having any spacing / padding + if image_properties.descriptor.is_opaque() && + image_properties.tiling.is_none() && + image_data.tile_spacing == LayoutSize::zero() { + backdrop_candidate = Some(BackdropInfo { + opaque_rect: pic_clip_rect, + kind: None, + }); } } } else { let opacity_binding = &opacity_binding_store[opacity_binding_index]; for binding in &opacity_binding.bindings { prim_info.opacity_bindings.push(OpacityBinding::from(*binding)); } } @@ -3204,17 +3202,17 @@ impl TileCacheInstance { prim_info.images.push(ImageDependency { key: border_data.request.key, generation: resource_cache.get_image_generation(border_data.request.key), }); } PrimitiveInstanceKind::PushClipChain | PrimitiveInstanceKind::PopClipChain => { // Early exit to ensure this doesn't get added as a dependency on the tile. - return false; + return None; } PrimitiveInstanceKind::TextRun { data_handle, .. } => { // Only do these checks if we haven't already disabled subpx // text rendering for this slice. if self.subpixel_mode == SubpixelMode::Allow && !self.is_opaque() { let run_data = &data_stores.text_run[data_handle]; // Only care about text runs that have requested subpixel rendering. @@ -3224,46 +3222,51 @@ impl TileCacheInstance { FontRenderMode::Subpixel => true, FontRenderMode::Alpha | FontRenderMode::Mono => false, }; // If a text run is on a child surface, the subpx mode will be // correctly determined as we recurse through pictures in take_context. if on_picture_surface && subpx_requested - && !self.backdrop.rect.contains_rect(&pic_clip_rect) { + && !self.backdrop.opaque_rect.contains_rect(&pic_clip_rect) { self.subpixel_mode = SubpixelMode::Deny; } } } PrimitiveInstanceKind::Clear { .. } => { - backdrop_candidate = Some(BackdropKind::Clear); + backdrop_candidate = Some(BackdropInfo { + opaque_rect: pic_clip_rect, + kind: Some(BackdropKind::Clear), + }); } PrimitiveInstanceKind::LineDecoration { .. } | PrimitiveInstanceKind::NormalBorder { .. } | PrimitiveInstanceKind::LinearGradient { .. } | PrimitiveInstanceKind::RadialGradient { .. } | PrimitiveInstanceKind::ConicGradient { .. } | PrimitiveInstanceKind::Backdrop { .. } => { // These don't contribute dependencies } }; // If this primitive considers itself a backdrop candidate, apply further // checks to see if it matches all conditions to be a backdrop. + let mut vis_flags = PrimitiveVisibilityFlags::empty(); + if let Some(backdrop_candidate) = backdrop_candidate { - let is_suitable_backdrop = match backdrop_candidate { - BackdropKind::Clear => { + let is_suitable_backdrop = match backdrop_candidate.kind { + Some(BackdropKind::Clear) => { // Clear prims are special - they always end up in their own slice, // and always set the backdrop. In future, we hope to completely // remove clear prims, since they don't integrate with the compositing // system cleanly. true } - BackdropKind::Image | BackdropKind::Color { .. } => { + Some(BackdropKind::Color { .. }) | None => { // Check a number of conditions to see if we can consider this // primitive as an opaque backdrop rect. Several of these are conservative // checks and could be relaxed in future. However, these checks // are quick and capture the common cases of background rects and images. // Specifically, we currently require: // - The primitive is on the main picture cache surface. // - Same coord system as picture cache (ensures rects are axis-aligned). // - No clip masks exist. @@ -3276,22 +3279,35 @@ impl TileCacheInstance { prim_spatial_node.coordinate_system_id == surface_spatial_node.coordinate_system_id }; same_coord_system && on_picture_surface } }; if is_suitable_backdrop - && !prim_clip_chain.needs_mask - && pic_clip_rect.contains_rect(&self.backdrop.rect) { - self.backdrop = BackdropInfo { - rect: pic_clip_rect, - kind: backdrop_candidate, - }; + && self.external_surfaces.is_empty() + && !prim_clip_chain.needs_mask { + + if backdrop_candidate.opaque_rect.contains_rect(&self.backdrop.opaque_rect) { + self.backdrop.opaque_rect = backdrop_candidate.opaque_rect; + } + + if let Some(kind) = backdrop_candidate.kind { + if backdrop_candidate.opaque_rect.contains_rect(&self.local_rect) { + // If we have a color backdrop, mark the visibility flags + // of the primitive so it is skipped during batching (and + // also clears any previous primitives). + if let BackdropKind::Color { .. } = kind { + vis_flags |= PrimitiveVisibilityFlags::IS_BACKDROP; + } + + self.backdrop.kind = Some(kind); + } + } } } // Record any new spatial nodes in the used list. self.used_spatial_nodes.extend(&prim_info.spatial_nodes); // Truncate the lengths of dependency arrays to the max size we can handle. // Any arrays this size or longer will invalidate every frame. @@ -3307,17 +3323,17 @@ impl TileCacheInstance { // TODO(gw): Convert to 2d array temporarily to avoid hash lookups per-tile? let key = TileOffset::new(x, y); let tile = self.tiles.get_mut(&key).expect("bug: no tile"); tile.add_prim_dependency(&prim_info); } } - true + Some(vis_flags) } /// Print debug information about this picture cache to a tree printer. fn print(&self) { // TODO(gw): This initial implementation is very basic - just printing // the picture cache state to stdout. In future, we can // make this dump each frame to a file, and produce a report // stating which frames had invalidations. This will allow @@ -3375,18 +3391,18 @@ impl TileCacheInstance { ROOT_SPATIAL_NODE_INDEX, self.spatial_node_index, frame_context.global_screen_world_rect, frame_context.spatial_tree, ); // Register the opaque region of this tile cache as an occluder, which // is used later in the frame to occlude other tiles. - if self.backdrop.rect.is_well_formed_and_nonempty() { - let backdrop_rect = self.backdrop.rect + if self.backdrop.opaque_rect.is_well_formed_and_nonempty() { + let backdrop_rect = self.backdrop.opaque_rect .intersection(&self.local_rect) .and_then(|r| { r.intersection(&self.local_clip_rect) }); if let Some(backdrop_rect) = backdrop_rect { let world_backdrop_rect = map_pic_to_world .map(&backdrop_rect)
--- a/gfx/wr/webrender/src/prim_store/mod.rs +++ b/gfx/wr/webrender/src/prim_store/mod.rs @@ -1514,16 +1514,30 @@ impl PrimitiveVisibilityMask { pub fn is_empty(&self) -> bool { self.bits == 0 } /// The maximum number of supported dirty regions. pub const MAX_DIRTY_REGIONS: usize = 8 * mem::size_of::<PrimitiveVisibilityMask>(); } +bitflags! { + /// A set of bitflags that can be set in the visibility information + /// for a primitive instance. This can be used to control how primitives + /// are treated during batching. + // TODO(gw): We should also move `is_compositor_surface` to be part of + // this flags struct. + #[cfg_attr(feature = "capture", derive(Serialize))] + pub struct PrimitiveVisibilityFlags: u16 { + /// Implies that this primitive covers the entire picture cache slice, + /// and can thus be dropped during batching and drawn with clear color. + const IS_BACKDROP = 1; + } +} + /// Information stored for a visible primitive about the visible /// rect and associated clip information. #[cfg_attr(feature = "capture", derive(Serialize))] pub struct PrimitiveVisibility { /// The clip chain instance that was built for this primitive. pub clip_chain: ClipChainInstance, /// The current world rect, clipped to screen / dirty rect boundaries. @@ -1534,16 +1548,20 @@ pub struct PrimitiveVisibility { /// An index into the clip task instances array in the primitive /// store. If this is ClipTaskIndex::INVALID, then the primitive /// has no clip mask. Otherwise, it may store the offset of the /// global clip mask task for this primitive, or the first of /// a list of clip task ids (one per segment). pub clip_task_index: ClipTaskIndex, + /// A set of flags that define how this primitive should be handled + /// during batching of visibile primitives. + pub flags: PrimitiveVisibilityFlags, + /// A mask defining which of the dirty regions this primitive is visible in. pub visibility_mask: PrimitiveVisibilityMask, /// The current combined local clip for this primitive, from /// the primitive local clip above and the current clip chain. pub combined_local_clip_rect: LayoutRect, } @@ -2090,16 +2108,17 @@ impl PrimitiveStore { frame_state.scratch.prim_info.push( PrimitiveVisibility { clipped_world_rect: WorldRect::max_rect(), clip_chain: ClipChainInstance::empty(), clip_task_index: ClipTaskIndex::INVALID, combined_local_clip_rect: LayoutRect::zero(), visibility_mask: PrimitiveVisibilityMask::empty(), + flags: PrimitiveVisibilityFlags::empty(), } ); prim_instance.visibility_info = vis_index; } else { if prim_local_rect.size.width <= 0.0 || prim_local_rect.size.height <= 0.0 { if prim_instance.is_chased() { println!("\tculled for zero local rectangle"); @@ -2151,41 +2170,51 @@ impl PrimitiveStore { frame_state.resource_cache, surface.device_pixel_scale, &world_culling_rect, &mut frame_state.data_stores.clip, true, prim_instance.is_chased(), ); + // Primitive visibility flags default to empty, but may be supplied + // by the `update_prim_dependencies` method below when picture caching + // is active. + let mut vis_flags = PrimitiveVisibilityFlags::empty(); + if let Some(ref mut tile_cache) = frame_state.tile_cache { // TODO(gw): Refactor how tile_cache is stored in frame_state // so that we can pass frame_state directly to // update_prim_dependencies, rather than splitting borrows. - if !tile_cache.update_prim_dependencies( + match tile_cache.update_prim_dependencies( prim_instance, cluster.spatial_node_index, clip_chain.as_ref(), prim_local_rect, frame_context, frame_state.data_stores, frame_state.clip_store, &self.pictures, frame_state.resource_cache, &self.opacity_bindings, &self.color_bindings, &self.images, &frame_state.surface_stack, &mut frame_state.composite_state, ) { - prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; - // Ensure the primitive clip is popped - perhaps we can use - // some kind of scope to do this automatically in future. - frame_state.clip_chain_stack.pop_clip(); - continue; + Some(flags) => { + vis_flags = flags; + } + None => { + prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; + // Ensure the primitive clip is popped - perhaps we can use + // some kind of scope to do this automatically in future. + frame_state.clip_chain_stack.pop_clip(); + continue; + } } } // Ensure the primitive clip is popped frame_state.clip_chain_stack.pop_clip(); let clip_chain = match clip_chain { Some(clip_chain) => clip_chain, @@ -2303,16 +2332,17 @@ impl PrimitiveStore { frame_state.scratch.prim_info.push( PrimitiveVisibility { clipped_world_rect, clip_chain, clip_task_index: ClipTaskIndex::INVALID, combined_local_clip_rect, visibility_mask: PrimitiveVisibilityMask::empty(), + flags: vis_flags, } ); prim_instance.visibility_info = vis_index; self.request_resources_for_prim( prim_instance, cluster.spatial_node_index,
--- a/layout/reftests/transform-3d/reftest.list +++ b/layout/reftests/transform-3d/reftest.list @@ -89,10 +89,10 @@ fuzzy-if(webrender,0-16,0-132) == mask-l fuzzy(0-255,0-150) == split-intersect2.html split-intersect2-ref.html fuzzy(0-255,0-100) == split-non-ortho1.html split-non-ortho1-ref.html fuzzy-if(winWidget,0-150,0-120) == component-alpha-1.html component-alpha-1-ref.html == nested-transform-1.html nested-transform-1-ref.html == transform-geometry-1.html transform-geometry-1-ref.html == intermediate-1.html intermediate-1-ref.html == preserves3d-nested-filter-1.html preserves3d-nested-filter-1-ref.html != preserve3d-scale.html about:blank -fuzzy-if(webrender,0-1,0-3) == perspective-overflow-1.html perspective-overflow-1-ref.html +fuzzy-if(webrender,0-1,0-5) == perspective-overflow-1.html perspective-overflow-1-ref.html == 1544995-1.html 1544995-1-ref.html