| author | Glenn Watson <gw@intuitionlibrary.com> |
| Mon, 03 Feb 2020 23:16:54 +0000 | |
| changeset 512426 | 42095e4b17091e46bd63d72f17145505ab1f8faf |
| parent 512425 | 494913315ee37f08e9a2a5fb7b1eb7af20f516ab |
| child 512427 | 58e428bf7b1439155cb055e06ef9e67b0128ef74 |
| push id | 37087 |
| push user | nbeleuzu@mozilla.com |
| push date | Tue, 04 Feb 2020 04:04:16 +0000 |
| treeherder | mozilla-central@c32017c40f19 [default view] [failures only] |
| perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
| reviewers | mstange |
| bugs | 1608280 |
| milestone | 74.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/batch.rs +++ b/gfx/wr/webrender/src/batch.rs @@ -1207,25 +1207,22 @@ impl BatchBuilder { // present modes during render, such as partial present etc. let tile_cache = picture.tile_cache.as_ref().unwrap(); let map_local_to_world = SpaceMapper::new_with_target( ROOT_SPATIAL_NODE_INDEX, tile_cache.spatial_node_index, ctx.screen_world_rect, ctx.spatial_tree, ); - let local_tile_clip_rect = LayoutRect::from_untyped(&tile_cache.local_rect.to_untyped()); - let local_tile_clip_rect = match local_tile_clip_rect.intersection(&prim_info.combined_local_clip_rect) { - Some(rect) => rect, - None => { - return; - } - }; + // TODO(gw): As a follow up to the valid_rect work, see why we use + // prim_info.combined_local_clip_rect here instead of the + // local_clip_rect built in the TileCacheInstance. Perhaps + // these can be unified or are different for a good reason? let world_clip_rect = map_local_to_world - .map(&local_tile_clip_rect) + .map(&prim_info.combined_local_clip_rect) .expect("bug: unable to map clip rect"); let device_clip_rect = (world_clip_rect * ctx.global_device_pixel_scale).round(); let z_id = composite_state.z_generator.next(); composite_state.push_surface( tile_cache, device_clip_rect, z_id,
--- a/gfx/wr/webrender/src/composite.rs +++ b/gfx/wr/webrender/src/composite.rs @@ -61,16 +61,17 @@ pub enum CompositeTileSurface { /// Describes the geometry and surface of a tile to be composited #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct CompositeTile { pub surface: CompositeTileSurface, pub rect: DeviceRect, pub clip_rect: DeviceRect, pub dirty_rect: DeviceRect, + pub valid_rect: DeviceRect, pub z_id: ZBufferId, pub tile_id: TileId, } /// Public interface specified in `RendererOptions` that configures /// how WR compositing will operate. pub enum CompositorConfig { /// Let WR draw tiles via normal batching. This requires no special OS support. @@ -298,27 +299,44 @@ impl CompositeState { tile_cache: &TileCacheInstance, device_clip_rect: DeviceRect, z_id: ZBufferId, global_device_pixel_scale: DevicePixelScale, resource_cache: &ResourceCache, ) { let mut visible_tile_count = 0; + // TODO(gw): For now, we apply the valid rect as part of the clip rect + // during native compositing. This works for the initial + // implementation, since the valid rect is determined only + // by the bounding rect of the picture cache slice. When + // we implement proper per-tile valid rects, we will need to + // supply the valid rect directly to the compositor interface. + let mut combined_valid_rect = DeviceRect::zero(); + for key in &tile_cache.tiles_to_draw { let tile = &tile_cache.tiles[key]; if !tile.is_visible { // This can occur when a tile is found to be occluded during frame building. continue; } visible_tile_count += 1; let device_rect = (tile.world_tile_rect * global_device_pixel_scale).round(); let dirty_rect = (tile.world_dirty_rect * global_device_pixel_scale).round(); + // The device rect is guaranteed to be aligned on a device pixel - the round + // above is just to deal with float accuracy. However, the valid rect is not + // always aligned to a device pixel. To handle this, round out to get all + // required pixels, and intersect with the tile device rect. + let valid_rect = (tile.world_valid_rect * global_device_pixel_scale) + .round_out() + .intersection(&device_rect) + .unwrap_or_else(DeviceRect::zero); + combined_valid_rect = combined_valid_rect.union(&valid_rect); let surface = tile.surface.as_ref().expect("no tile surface set!"); let (surface, is_opaque) = match surface { TileSurface::Color { color } => { (CompositeTileSurface::Color { color: *color }, true) } TileSurface::Clear => { (CompositeTileSurface::Clear, false) @@ -330,34 +348,37 @@ impl CompositeState { tile.is_opaque || tile_cache.is_opaque(), ) } }; let tile = CompositeTile { surface, rect: device_rect, + valid_rect, dirty_rect, clip_rect: device_clip_rect, z_id, tile_id: tile.id, }; self.push_tile(tile, is_opaque); } if visible_tile_count > 0 { - self.descriptor.surfaces.push( - CompositeSurfaceDescriptor { - slice: tile_cache.slice, - surface_id: tile_cache.native_surface_id, - offset: tile_cache.device_position, - clip_rect: device_clip_rect, - } - ); + if let Some(clip_rect) = device_clip_rect.intersection(&combined_valid_rect) { + self.descriptor.surfaces.push( + CompositeSurfaceDescriptor { + slice: tile_cache.slice, + surface_id: tile_cache.native_surface_id, + offset: tile_cache.device_position, + clip_rect, + } + ); + } } } /// Add a tile to the appropriate array, depending on tile properties and compositor mode. fn push_tile( &mut self, tile: CompositeTile, is_opaque: bool,
--- a/gfx/wr/webrender/src/picture.rs +++ b/gfx/wr/webrender/src/picture.rs @@ -401,23 +401,23 @@ struct TilePreUpdateContext { /// require invalidation of all tiles. fract_offset: PictureVector2D, /// The optional background color of the picture cache instance background_color: Option<ColorF>, /// The visible part of the screen in world coords. global_screen_world_rect: WorldRect, + + /// The local rect of the overall picture cache + local_rect: PictureRect, } // Immutable context passed to picture cache tiles during post_update struct TilePostUpdateContext<'a> { - /// The local rect of the overall picture cache - local_rect: PictureRect, - /// The local clip rect (in picture space) of the entire picture cache local_clip_rect: PictureRect, /// The calculated backdrop information for this cache instance. backdrop: BackdropInfo, /// Information about transform node differences from last frame. spatial_nodes: &'a FastHashMap<SpatialNodeIndex, SpatialNodeDependency>, @@ -662,16 +662,20 @@ pub struct Tile { /// The current local rect of this tile. pub local_tile_rect: PictureRect, /// The picture space dirty rect for this tile. local_dirty_rect: PictureRect, /// The world space dirty rect for this tile. /// TODO(gw): We have multiple dirty rects available due to the quadtree above. In future, /// expose these as multiple dirty rects, which will help in some cases. pub world_dirty_rect: WorldRect, + /// Picture space rect that contains valid pixels region of this tile. + local_valid_rect: PictureRect, + /// World space rect that contains valid pixels region of this tile. + pub world_valid_rect: WorldRect, /// Uniquely describes the content of this tile, in a way that can be /// (reasonably) efficiently hashed and compared. pub current_descriptor: TileDescriptor, /// The content descriptor for this tile from the previous frame. pub prev_descriptor: TileDescriptor, /// Handle to the backing surface for this tile. pub surface: Option<TileSurface>, /// If true, this tile is marked valid, and the existing texture @@ -702,16 +706,18 @@ pub struct Tile { impl Tile { /// Construct a new, invalid tile. fn new( id: TileId, ) -> Self { Tile { local_tile_rect: PictureRect::zero(), world_tile_rect: WorldRect::zero(), + local_valid_rect: PictureRect::zero(), + world_valid_rect: WorldRect::zero(), local_dirty_rect: PictureRect::zero(), world_dirty_rect: WorldRect::zero(), surface: None, current_descriptor: TileDescriptor::new(), prev_descriptor: TileDescriptor::new(), is_valid: false, is_visible: false, fract_offset: PictureVector2D::zero(), @@ -814,22 +820,27 @@ impl Tile { /// Called during pre_update of a tile cache instance. Allows the /// tile to setup state before primitive dependency calculations. fn pre_update( &mut self, local_tile_rect: PictureRect, ctx: &TilePreUpdateContext, ) { self.local_tile_rect = local_tile_rect; + self.local_valid_rect = local_tile_rect.intersection(&ctx.local_rect).unwrap(); self.invalidation_reason = None; self.world_tile_rect = ctx.pic_to_world_mapper .map(&self.local_tile_rect) .expect("bug: map local tile rect"); + self.world_valid_rect = ctx.pic_to_world_mapper + .map(&self.local_valid_rect) + .expect("bug: map local valid rect"); + // Check if this tile is currently on screen. self.is_visible = self.world_tile_rect.intersects(&ctx.global_screen_world_rect); // If the tile isn't visible, early exit, skipping the normal set up to // validate dependencies. Instead, we will only compare the current tile // dependencies the next time it comes into view. if !self.is_visible { return; @@ -970,19 +981,18 @@ impl Tile { } return false; } // 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.local_tile_rect - .intersection(&ctx.local_rect) - .and_then(|r| r.intersection(&ctx.local_clip_rect)) + let clipped_rect = self.local_valid_rect + .intersection(&ctx.local_clip_rect) .unwrap_or_else(PictureRect::zero); self.is_opaque = ctx.backdrop.rect.contains_rect(&clipped_rect); // Check if the selected composite mode supports dirty rect updates. For Draw composite // mode, we can always update the content with smaller dirty rects. For native composite // mode, we can only use dirty rects if the compositor supports partial surface updates. let (supports_dirty_rects, supports_simple_prims) = match state.composite_state.compositor_kind { CompositorKind::Draw { .. } => { @@ -2191,16 +2201,17 @@ impl TileCacheInstance { self.tile_bounds_p0 = TileOffset::new(x0, y0); self.tile_bounds_p1 = TileOffset::new(x1, y1); let mut world_culling_rect = WorldRect::zero(); mem::swap(&mut self.tiles, &mut self.old_tiles); let ctx = TilePreUpdateContext { + local_rect: self.local_rect, pic_to_world_mapper, fract_offset: self.fract_offset, background_color: self.background_color, global_screen_world_rect: frame_context.global_screen_world_rect, }; self.tiles.clear(); for y in y0 .. y1 { @@ -2714,17 +2725,16 @@ impl TileCacheInstance { self.spatial_nodes.insert(spatial_node_index, SpatialNodeDependency { changed, value, }); } let ctx = TilePostUpdateContext { - local_rect: self.local_rect, local_clip_rect: self.local_clip_rect, backdrop: self.backdrop, spatial_nodes: &self.spatial_nodes, opacity_bindings: &self.opacity_bindings, current_tile_size: self.current_tile_size, }; let mut state = TilePostUpdateState { @@ -4019,41 +4029,37 @@ impl PicturePrimitive { Some((render_task_id, render_task_id)) } PictureCompositeMode::TileCache { .. } => { let tile_cache = self.tile_cache.as_mut().unwrap(); let mut first = true; // Get the overall world space rect of the picture cache. Used to clip // the tile rects below for occlusion testing to the relevant area. - let local_clip_rect = tile_cache.local_rect - .intersection(&tile_cache.local_clip_rect) - .unwrap_or_else(PictureRect::zero); - let world_clip_rect = map_pic_to_world - .map(&local_clip_rect) + .map(&tile_cache.local_clip_rect) .expect("bug: unable to map clip rect"); for key in &tile_cache.tiles_to_draw { let tile = tile_cache.tiles.get_mut(key).expect("bug: no tile found!"); // Get the world space rect that this tile will actually occupy on screem - let tile_draw_rect = match world_clip_rect.intersection(&tile.world_tile_rect) { + let world_draw_rect = match world_clip_rect.intersection(&tile.world_valid_rect) { Some(rect) => rect, None => { tile.is_visible = false; continue; } }; // If that draw rect is occluded by some set of tiles in front of it, // then mark it as not visible and skip drawing. When it's not occluded // it will fail this test, and get rasterized by the render task setup // code below. - if frame_state.composite_state.is_tile_occluded(tile_cache.slice, tile_draw_rect) { + if frame_state.composite_state.is_tile_occluded(tile_cache.slice, world_draw_rect) { // If this tile has an allocated native surface, free it, since it's completely // occluded. We will need to re-allocate this surface if it becomes visible, // but that's likely to be rare (e.g. when there is no content display list // for a frame or two during a tab switch). let surface = tile.surface.as_mut().expect("no tile surface set!"); if let TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { id, .. }, .. } = surface { if let Some(id) = id.take() {
--- a/gfx/wr/webrender/src/renderer.rs +++ b/gfx/wr/webrender/src/renderer.rs @@ -4216,16 +4216,22 @@ impl Renderer { None => tile.rect, }; let clip_rect = match partial_clip_rect.intersection(&tile.clip_rect) { Some(rect) => rect, None => continue, }; + // Only composite the part of the tile that contains valid pixels + let clip_rect = match clip_rect.intersection(&tile.valid_rect) { + Some(rect) => rect, + None => continue, + }; + // Flush this batch if the textures aren't compatible if !current_textures.is_compatible_with(&textures) { self.draw_instanced_batch( &instances, VertexArrayKind::Composite, ¤t_textures, stats, );