Bug 1540200 - Part 1. Move picture local rect calculation to update visibility. r=kvark

We currently calculate a picture's local rect when we are doing the
first picture traversal. It was composed of the union of the clipped
local rects of its children. However the true local rect of a picture is
the union of the snapped clipped local rects of its children. The
snapping is done in device space, but we won't know the exact transform
until we establish the raster roots, which is based on the picture's
local rect.

As such, we create an estimated local rect which is how we currently
calculate the local rect. Then once the raster roots have been selected,
we recalculate the local rect of the picture based on its children
during update visibility.

This patch should have not contain any functional changes.

Differential Revision: https://phabricator.services.mozilla.com/D28881
This commit is contained in:
Andrew Osmond 2019-04-10 12:35:20 -04:00
parent af98466fc0
commit 1a022002f5
3 changed files with 139 additions and 59 deletions

View File

@ -978,7 +978,7 @@ impl AlphaBatchBuilder {
let prim_cache_address = gpu_cache.get_address(&ctx.globals.default_image_handle);
let prim_header = PrimitiveHeader {
local_rect: picture.local_rect,
local_rect: picture.snapped_local_rect,
local_clip_rect: prim_info.combined_local_clip_rect,
task_address,
specific_prim_address: prim_cache_address,
@ -1016,7 +1016,7 @@ impl AlphaBatchBuilder {
).unwrap_or(OPAQUE_TASK_ADDRESS);
let prim_header = PrimitiveHeader {
local_rect: pic.local_rect,
local_rect: pic.snapped_local_rect,
local_clip_rect: prim_info.combined_local_clip_rect,
task_address,
specific_prim_address: GpuCacheAddress::invalid(),
@ -1125,7 +1125,7 @@ impl AlphaBatchBuilder {
// the local bounds of the picture extend to within the edge tiles.
let local_clip_rect = prim_info
.combined_local_clip_rect
.intersection(&picture.local_rect)
.intersection(&picture.snapped_local_rect)
.and_then(|rect| {
rect.intersection(&tile_cache.local_clip_rect)
});
@ -1619,7 +1619,7 @@ impl AlphaBatchBuilder {
};
let prim_header = PrimitiveHeader {
local_rect: picture.local_rect,
local_rect: picture.snapped_local_rect,
local_clip_rect: prim_info.combined_local_clip_rect,
task_address,
specific_prim_address: prim_cache_address,

View File

@ -858,11 +858,18 @@ impl TileCache {
});
}
// Map the picture rect to world space and work out the tiles that we need
// in order to ensure the screen is covered.
let pic_world_rect = world_mapper
.map(&pic_rect)
.expect("bug: unable to map picture rect to world");
// Map the picture rect to world and device space and work out the tiles
// that we need in order to ensure the screen is covered. We haven't done
// any snapping yet, so we need to round out in device space to ensure we
// cover all pixels the picture may touch.
let pic_device_rect = {
let unsnapped_world_rect = world_mapper
.map(&pic_rect)
.expect("bug: unable to map picture rect to world");
(unsnapped_world_rect * frame_context.global_device_pixel_scale)
.round_out()
};
let pic_world_rect = pic_device_rect / frame_context.global_device_pixel_scale;
// If the bounding rect of the picture to cache doesn't intersect with
// the visible world rect at all, just take the screen world rect as
@ -892,7 +899,6 @@ impl TileCache {
// given the world reference point constraint.
let device_ref_point = world_ref_point * frame_context.global_device_pixel_scale;
let device_world_rect = frame_context.screen_world_rect * frame_context.global_device_pixel_scale;
let pic_device_rect = pic_world_rect * frame_context.global_device_pixel_scale;
let needed_device_rect = pic_device_rect
.intersection(&device_world_rect)
.unwrap_or(device_world_rect);
@ -1733,7 +1739,6 @@ impl<'a> PictureUpdateState<'a> {
prim_list,
self,
frame_context,
gpu_cache,
);
}
}
@ -2190,8 +2195,16 @@ pub struct PicturePrimitive {
pub spatial_node_index: SpatialNodeIndex,
/// The local rect of this picture. It is built
/// dynamically during the first picture traversal.
pub local_rect: LayoutRect,
/// dynamically when updating visibility. It takes
/// into account snapping in device space for its
/// children.
pub snapped_local_rect: LayoutRect,
/// The local rect of this picture. It is built
/// dynamically during the first picture traversal. It
/// does not take into account snapping in device for
/// its children.
pub unsnapped_local_rect: LayoutRect,
/// If false, this picture needs to (re)build segments
/// if it supports segment rendering. This can occur
@ -2216,7 +2229,8 @@ impl PicturePrimitive {
) {
pt.new_level(format!("{:?}", self_index));
pt.add_item(format!("prim_count: {:?}", self.prim_list.prim_instances.len()));
pt.add_item(format!("local_rect: {:?}", self.local_rect));
pt.add_item(format!("snapped_local_rect: {:?}", self.snapped_local_rect));
pt.add_item(format!("unsnapped_local_rect: {:?}", self.unsnapped_local_rect));
pt.add_item(format!("spatial_node_index: {:?}", self.spatial_node_index));
pt.add_item(format!("raster_config: {:?}", self.raster_config));
pt.add_item(format!("requested_composite_mode: {:?}", self.requested_composite_mode));
@ -2325,7 +2339,8 @@ impl PicturePrimitive {
pipeline_id,
requested_raster_space,
spatial_node_index,
local_rect: LayoutRect::zero(),
snapped_local_rect: LayoutRect::zero(),
unsnapped_local_rect: LayoutRect::zero(),
tile_cache,
options,
segments_are_valid: false,
@ -2749,7 +2764,6 @@ impl PicturePrimitive {
prim_list: PrimitiveList,
state: &mut PictureUpdateState,
frame_context: &FrameBuildingContext,
gpu_cache: &mut GpuCache,
) {
// Restore the pictures list used during recursion.
self.prim_list = prim_list;
@ -2822,22 +2836,10 @@ impl PicturePrimitive {
let surface_index = state.pop_surface();
debug_assert_eq!(surface_index, raster_config.surface_index);
// If the local rect changed (due to transforms in child primitives) then
// invalidate the GPU cache location to re-upload the new local rect
// and stretch size. Drop shadow filters also depend on the local rect
// size for the extra GPU cache data handle.
// TODO(gw): In future, if we support specifying a flag which gets the
// stretch size from the segment rect in the shaders, we can
// remove this invalidation here completely.
if self.local_rect != surface_rect {
if let PictureCompositeMode::Filter(FilterOp::DropShadow(..)) = raster_config.composite_mode {
gpu_cache.invalidate(&self.extra_gpu_data_handle);
}
// Invalidate any segments built for this picture, since the local
// rect has changed.
self.segments_are_valid = false;
self.local_rect = surface_rect;
}
// Snapping may change the local rect slightly, and as such should just be
// considered an estimated size for determining if we need raster roots and
// preparing the tile cache.
self.unsnapped_local_rect = surface_rect;
// Check if any of the surfaces can't be rasterized in local space but want to.
if raster_config.establishes_raster_root {
@ -2910,7 +2912,7 @@ impl PicturePrimitive {
frame_context.clip_scroll_tree,
);
let pic_rect = PictureRect::from_untyped(&self.local_rect.to_untyped());
let pic_rect = PictureRect::from_untyped(&self.snapped_local_rect.to_untyped());
let (clipped, unclipped) = match get_raster_rects(
pic_rect,
@ -3072,14 +3074,14 @@ impl PicturePrimitive {
// Basic brush primitive header is (see end of prepare_prim_for_render_inner in prim_store.rs)
// [brush specific data]
// [segment_rect, segment data]
let shadow_rect = self.local_rect.translate(&offset);
let shadow_rect = self.snapped_local_rect.translate(&offset);
// ImageBrush colors
request.push(color.premultiplied());
request.push(PremultipliedColorF::WHITE);
request.push([
self.local_rect.size.width,
self.local_rect.size.height,
self.snapped_local_rect.size.width,
self.snapped_local_rect.size.height,
0.0,
0.0,
]);

View File

@ -10,6 +10,7 @@ use api::{PrimitiveKeyKind, RasterSpace};
use api::units::*;
use border::{get_max_scale_for_border, build_border_instances};
use border::BorderSegmentCacheKey;
use box_shadow::{BLUR_SAMPLE_SCALE};
use clip::{ClipStore};
use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex, VisibleFace};
use clip::{ClipDataStore, ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItem};
@ -27,7 +28,7 @@ use image::{Repetition};
use intern;
use malloc_size_of::MallocSizeOf;
use picture::{PictureCompositeMode, PicturePrimitive, SurfaceInfo};
use picture::{ClusterIndex, PrimitiveList, RecordedDirtyRegion, SurfaceIndex, RetainedTiles, RasterConfig};
use picture::{ClusterIndex, PrimitiveList, RecordedDirtyRegion, SurfaceIndex, RetainedTiles};
use prim_store::borders::{ImageBorderDataHandle, NormalBorderDataHandle};
use prim_store::gradient::{GRADIENT_FP_STOPS, GradientCacheKey, GradientStopKey};
use prim_store::gradient::{LinearGradientPrimitive, LinearGradientDataHandle, RadialGradientDataHandle};
@ -1722,7 +1723,7 @@ impl PrimitiveStore {
parent_surface_index: SurfaceIndex,
frame_context: &FrameVisibilityContext,
frame_state: &mut FrameVisibilityState,
) {
) -> Option<PictureRect> {
let (mut prim_list, surface_index, apply_local_clip_rect, raster_space) = {
let pic = &mut self.pictures[pic_index.0];
@ -1739,7 +1740,7 @@ impl PrimitiveStore {
// relative transforms have changed, which means we need to
// re-map the dependencies of any child primitives.
tile_cache.pre_update(
pic.local_rect,
pic.unsnapped_local_rect,
frame_context,
frame_state,
surface_index,
@ -1764,6 +1765,8 @@ impl PrimitiveStore {
frame_context.clip_scroll_tree,
);
let mut surface_rect = PictureRect::zero();
for prim_instance in &mut prim_list.prim_instances {
prim_instance.reset();
@ -1811,7 +1814,7 @@ impl PrimitiveStore {
frame_state.clip_chain_stack.push_clip(prim_instance.clip_chain_id);
}
self.update_visibility(
let pic_surface_rect = self.update_visibility(
pic_index,
surface_index,
frame_context,
@ -1829,7 +1832,7 @@ impl PrimitiveStore {
// this way. In future, we could perhaps just store the
// size in the picture primitive, to that there isn't
// any duplicated data.
prim_instance.prim_origin = pic.local_rect.origin;
prim_instance.prim_origin = pic.snapped_local_rect.origin;
// Similar to above, pop either the clip chain or root entry off the current clip stack.
if is_composite {
@ -1838,7 +1841,33 @@ impl PrimitiveStore {
frame_state.clip_chain_stack.pop_clip();
}
(pic.raster_config.is_none(), pic.local_rect)
let pic_visible_rect = match pic.raster_config {
Some(ref rc) => {
let visible_rect = match rc.composite_mode {
// If we have a drop shadow filter, we also need to include the shadow in
// our local rect for the purpose of calculating the size of the picture.
PictureCompositeMode::Filter(FilterOp::DropShadow(offset, ..)) => {
pic.snapped_local_rect.translate(&offset).union(&pic.snapped_local_rect)
}
_ => pic.snapped_local_rect,
};
map_local_to_surface.map(&visible_rect)
}
None => pic_surface_rect,
};
if let Some(ref rect) = pic_visible_rect {
surface_rect = surface_rect.union(rect);
}
if prim_instance.is_chased() {
if pic.unsnapped_local_rect != pic.snapped_local_rect {
println!("\tsnapped from {:?} to {:?}", pic.unsnapped_local_rect, pic.snapped_local_rect);
}
}
(pic.raster_config.is_none(), pic.snapped_local_rect)
}
_ => {
let prim_data = &frame_state.data_stores.as_common_data(&prim_instance);
@ -1848,6 +1877,13 @@ impl PrimitiveStore {
prim_data.prim_size,
);
let visible_rect = prim_instance.local_clip_rect
.intersection(&prim_rect)
.unwrap_or(LayoutRect::zero());
if let Some(rect) = map_local_to_surface.map(&visible_rect) {
surface_rect = surface_rect.union(&rect);
}
(false, prim_rect)
}
};
@ -2024,22 +2060,64 @@ impl PrimitiveStore {
}
let pic = &mut self.pictures[pic_index.0];
if let Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { .. }, .. }) = pic.raster_config {
let mut tile_cache = frame_state.tile_cache.take().unwrap();
// Build the dirty region(s) for this tile cache.
tile_cache.post_update(
frame_state.resource_cache,
frame_state.gpu_cache,
frame_context,
frame_state.scratch,
);
pic.tile_cache = Some(tile_cache);
}
pic.prim_list = prim_list;
// If the local rect changed (due to transforms in child primitives) then
// invalidate the GPU cache location to re-upload the new local rect
// and stretch size. Drop shadow filters also depend on the local rect
// size for the extra GPU cache data handle.
// TODO(gw): In future, if we support specifying a flag which gets the
// stretch size from the segment rect in the shaders, we can
// remove this invalidation here completely.
if let Some(ref raster_config) = pic.raster_config {
// Inflate the local bounding rect if required by the filter effect.
// This inflaction factor is to be applied to the surface itself.
let inflation_size = match raster_config.composite_mode {
PictureCompositeMode::Filter(FilterOp::Blur(_)) => surface.inflation_factor,
PictureCompositeMode::Filter(FilterOp::DropShadow(_, blur_radius, _)) =>
(blur_radius * BLUR_SAMPLE_SCALE).ceil(),
_ => 0.0,
};
surface_rect = surface_rect.inflate(inflation_size, inflation_size);
// Layout space for the picture is picture space from the
// perspective of its child primitives.
let pic_local_rect = surface_rect * TypedScale::new(1.0);
if pic.snapped_local_rect != pic_local_rect {
if let PictureCompositeMode::Filter(FilterOp::DropShadow(..)) = raster_config.composite_mode {
frame_state.gpu_cache.invalidate(&pic.extra_gpu_data_handle);
}
// Invalidate any segments built for this picture, since the local
// rect has changed.
pic.segments_are_valid = false;
pic.snapped_local_rect = pic_local_rect;
}
if let PictureCompositeMode::TileCache { .. } = raster_config.composite_mode {
let mut tile_cache = frame_state.tile_cache.take().unwrap();
// Build the dirty region(s) for this tile cache.
tile_cache.post_update(
frame_state.resource_cache,
frame_state.gpu_cache,
frame_context,
frame_state.scratch,
);
pic.tile_cache = Some(tile_cache);
}
None
} else {
let parent_surface = &frame_context.surfaces[parent_surface_index.0 as usize];
let map_surface_to_parent_surface = SpaceMapper::new_with_target(
parent_surface.surface_spatial_node_index,
surface.surface_spatial_node_index,
PictureRect::max_rect(),
frame_context.clip_scroll_tree,
);
map_surface_to_parent_surface.map(&surface_rect)
}
}
fn request_resources_for_prim(
@ -2948,7 +3026,7 @@ impl PrimitiveStore {
splitter,
frame_state.transforms,
prim_instance.spatial_node_index,
pic.local_rect,
pic.snapped_local_rect,
&prim_info.combined_local_clip_rect,
frame_context.screen_world_rect,
plane_split_anchor,
@ -3286,7 +3364,7 @@ impl PrimitiveInstance {
// Override the prim local rect with the dynamically calculated
// local rect for the picture.
prim_local_rect = pic.local_rect;
prim_local_rect = pic.snapped_local_rect;
segment_instance_index
} else {