зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1578641 - Improve how tile clipping works in WR picture caching. r=kvark
Previously, picture caching code would use the viewport of the scroll root to find a clipping rect for picture cache tiles. This viewport rect was also used to eliminate fixed position clip rects on primitives that would otherwise cause unwanted invalidations due to them moving relative to the scroll root when scrolls occur. Now, the picture caching code uses a similar technique to Gecko to find shared clips on primitives in a picture cache. These clips are filtered out from being applied on a per-primitive basis, and instead applied once during compositing the tiles into the parent picture. This is a potential performance improvement, since less per-item work is required when building clip chains. More importantly, it means the picture caching code correctly handles cases where the scroll root contains fixed position elements (or other scroll roots). This is a requirement before we can enable picture caching on multiple slices. Differential Revision: https://phabricator.services.mozilla.com/D44618 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
9f3a3e9669
Коммит
d6bac95db7
|
@ -1179,13 +1179,13 @@ impl BatchBuilder {
|
|||
PictureCompositeMode::TileCache { .. } => {
|
||||
let tile_cache = picture.tile_cache.as_ref().unwrap();
|
||||
|
||||
let tile_clip_rect = match tile_cache.local_rect.intersection(&tile_cache.local_clip_rect) {
|
||||
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;
|
||||
}
|
||||
};
|
||||
let local_tile_clip_rect = LayoutRect::from_untyped(&tile_clip_rect.to_untyped());
|
||||
|
||||
for key in &tile_cache.tiles_to_draw {
|
||||
let tile = &tile_cache.tiles[key];
|
||||
|
|
|
@ -15,11 +15,10 @@ use crate::image::{self, Repetition};
|
|||
use crate::intern;
|
||||
use crate::prim_store::{ClipData, ImageMaskData, SpaceMapper, VisibleMaskImageTile};
|
||||
use crate::prim_store::{PointKey, SizeKey, RectangleKey};
|
||||
use crate::render_backend::DataStores;
|
||||
use crate::render_task::to_cache_size;
|
||||
use crate::resource_cache::{ImageRequest, ResourceCache};
|
||||
use std::{cmp, ops, u32};
|
||||
use crate::util::{extract_inner_rect_safe, project_rect, ScaleOffset, MaxRect};
|
||||
use crate::util::{extract_inner_rect_safe, project_rect, ScaleOffset};
|
||||
|
||||
/*
|
||||
|
||||
|
@ -103,7 +102,19 @@ use crate::util::{extract_inner_rect_safe, project_rect, ScaleOffset, MaxRect};
|
|||
// Type definitions for interning clip nodes.
|
||||
|
||||
pub type ClipDataStore = intern::DataStore<ClipIntern>;
|
||||
type ClipDataHandle = intern::Handle<ClipIntern>;
|
||||
pub type ClipDataHandle = intern::Handle<ClipIntern>;
|
||||
|
||||
/// Helper to identify simple clips (normal rects) from other kinds of clips,
|
||||
/// which can often be handled via fast code paths.
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
#[derive(Debug, Copy, Clone, MallocSizeOf)]
|
||||
pub enum ClipNodeKind {
|
||||
/// A normal clip rectangle, with Clip mode.
|
||||
Rectangle,
|
||||
/// A rectangle with ClipOut, or any other kind of clip.
|
||||
Complex,
|
||||
}
|
||||
|
||||
// Result of comparing a clip node instance against a local rect.
|
||||
#[derive(Debug)]
|
||||
|
@ -209,7 +220,6 @@ impl ClipChainId {
|
|||
pub struct ClipChainNode {
|
||||
pub handle: ClipDataHandle,
|
||||
pub parent_clip_chain_id: ClipChainId,
|
||||
pub has_complex_clip: bool,
|
||||
}
|
||||
|
||||
// When a clip node is found to be valid for a
|
||||
|
@ -545,22 +555,25 @@ impl ClipChainInstance {
|
|||
}
|
||||
|
||||
/// Maintains a (flattened) list of clips for a given level in the surface level stack.
|
||||
#[derive(Debug)]
|
||||
pub struct ClipChainLevel {
|
||||
clips: Vec<ClipChainId>,
|
||||
clip_counts: Vec<usize>,
|
||||
viewport: WorldRect,
|
||||
/// These clips will be handled when compositing this surface into the parent,
|
||||
/// and can thus be ignored on the primitives that are drawn as part of this surface.
|
||||
shared_clips: Vec<ClipDataHandle>,
|
||||
}
|
||||
|
||||
impl ClipChainLevel {
|
||||
/// Construct a new level in the active clip chain stack. The viewport
|
||||
/// is used to filter out irrelevant clips.
|
||||
fn new(
|
||||
viewport: WorldRect,
|
||||
shared_clips: Vec<ClipDataHandle>,
|
||||
) -> Self {
|
||||
ClipChainLevel {
|
||||
clips: Vec::new(),
|
||||
clip_counts: Vec::new(),
|
||||
viewport,
|
||||
shared_clips,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -580,7 +593,7 @@ pub struct ClipChainStack {
|
|||
impl ClipChainStack {
|
||||
pub fn new() -> Self {
|
||||
ClipChainStack {
|
||||
stack: vec![ClipChainLevel::new(WorldRect::max_rect())],
|
||||
stack: vec![ClipChainLevel::new(Vec::new())],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -589,72 +602,37 @@ impl ClipChainStack {
|
|||
&mut self,
|
||||
clip_chain_id: ClipChainId,
|
||||
clip_store: &ClipStore,
|
||||
data_stores: &DataStores,
|
||||
clip_scroll_tree: &ClipScrollTree,
|
||||
global_screen_world_rect: WorldRect,
|
||||
) {
|
||||
let level = self.stack.last_mut().unwrap();
|
||||
let mut clip_count = 0;
|
||||
|
||||
let mut current_clip_chain_id = clip_chain_id;
|
||||
while current_clip_chain_id != ClipChainId::NONE {
|
||||
let clip_chain_node = &clip_store.clip_chain_nodes[current_clip_chain_id.0 as usize];
|
||||
let clip_uid = clip_chain_node.handle.uid();
|
||||
|
||||
let clip_node = &data_stores.clip[clip_chain_node.handle];
|
||||
|
||||
// Filter out irrelevant rectangle clips. If a clip rect is in world space
|
||||
// and completely contains the viewport to be rendered, then the clip itself
|
||||
// is redundant. This is particularly important for picture caching, to avoid
|
||||
// extra invalidations. By skipping these global clip rects (such as the iframe rect)
|
||||
// we can (a) avoid invalidations due to introducing clip dependencies where the
|
||||
// relative transform changes during scrolling, and (b) allow correct rendering
|
||||
// of tiles that exceed these clip rects, so that we can draw them once and
|
||||
// cache them, to be used during scrolling.
|
||||
let valid_clip = match clip_node.item.kind {
|
||||
ClipItemKind::Rectangle { rect, mode: ClipMode::Clip } => {
|
||||
let scroll_root = clip_scroll_tree.find_scroll_root(
|
||||
clip_node.item.spatial_node_index,
|
||||
);
|
||||
|
||||
let mut is_required = true;
|
||||
|
||||
if scroll_root == ROOT_SPATIAL_NODE_INDEX {
|
||||
let map_local_to_world = SpaceMapper::new_with_target(
|
||||
ROOT_SPATIAL_NODE_INDEX,
|
||||
clip_node.item.spatial_node_index,
|
||||
global_screen_world_rect,
|
||||
clip_scroll_tree,
|
||||
);
|
||||
|
||||
// TODO(gw): This map method can produce a conservative bounding rect
|
||||
// which is not what we require here. In this case, we know
|
||||
// that the conversion is exect, due to checking that the
|
||||
// scroll root is the root spatial node. However, we should
|
||||
// change this to directly use the content_transform, to make
|
||||
// the intent clearer here.
|
||||
if let Some(clip_world_rect) = map_local_to_world.map(&rect) {
|
||||
if clip_world_rect.contains_rect(&level.viewport) {
|
||||
is_required = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is_required
|
||||
// The clip is required, so long as it doesn't exist in any of the shared_clips
|
||||
// array from this or any parent surfaces.
|
||||
// TODO(gw): We could consider making this a HashSet if it ever shows up in
|
||||
// profiles, but the typical array length is 2-3 elements.
|
||||
let mut valid_clip = true;
|
||||
for level in &self.stack {
|
||||
if level.shared_clips.iter().any(|handle| {
|
||||
handle.uid() == clip_uid
|
||||
}) {
|
||||
valid_clip = false;
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if valid_clip {
|
||||
level.clips.push(current_clip_chain_id);
|
||||
self.stack.last_mut().unwrap().clips.push(current_clip_chain_id);
|
||||
clip_count += 1;
|
||||
}
|
||||
|
||||
current_clip_chain_id = clip_chain_node.parent_clip_chain_id;
|
||||
}
|
||||
|
||||
level.clip_counts.push(clip_count);
|
||||
self.stack.last_mut().unwrap().clip_counts.push(clip_count);
|
||||
}
|
||||
|
||||
/// Pop a clip chain root from the currently active list.
|
||||
|
@ -670,24 +648,9 @@ impl ClipChainStack {
|
|||
/// stack of clips to be propagated.
|
||||
pub fn push_surface(
|
||||
&mut self,
|
||||
viewport: WorldRect,
|
||||
shared_clips: &[ClipDataHandle],
|
||||
) {
|
||||
// Ensure that sub-surfaces (e.g. filters) of a tile
|
||||
// cache intersect with any parent viewport. This ensures
|
||||
// that we correctly filter out redundant clips on these
|
||||
// child surfaces.
|
||||
let viewport = match self.stack.last() {
|
||||
Some(parent_level) => {
|
||||
parent_level.viewport
|
||||
.intersection(&viewport)
|
||||
.unwrap_or(WorldRect::zero())
|
||||
}
|
||||
None => {
|
||||
viewport
|
||||
}
|
||||
};
|
||||
|
||||
let level = ClipChainLevel::new(viewport);
|
||||
let level = ClipChainLevel::new(shared_clips.to_vec());
|
||||
self.stack.push(level);
|
||||
}
|
||||
|
||||
|
@ -722,13 +685,11 @@ impl ClipStore {
|
|||
&mut self,
|
||||
handle: ClipDataHandle,
|
||||
parent_clip_chain_id: ClipChainId,
|
||||
has_complex_clip: bool,
|
||||
) -> ClipChainId {
|
||||
let id = ClipChainId(self.clip_chain_nodes.len() as u32);
|
||||
self.clip_chain_nodes.push(ClipChainNode {
|
||||
handle,
|
||||
parent_clip_chain_id,
|
||||
has_complex_clip,
|
||||
});
|
||||
id
|
||||
}
|
||||
|
@ -1072,14 +1033,14 @@ impl ClipItemKeyKind {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn has_complex_clip(&self) -> bool {
|
||||
pub fn node_kind(&self) -> ClipNodeKind {
|
||||
match *self {
|
||||
ClipItemKeyKind::Rectangle(_, ClipMode::Clip) => false,
|
||||
ClipItemKeyKind::Rectangle(_, ClipMode::Clip) => ClipNodeKind::Rectangle,
|
||||
|
||||
ClipItemKeyKind::Rectangle(_, ClipMode::ClipOut) |
|
||||
ClipItemKeyKind::RoundedRectangle(..) |
|
||||
ClipItemKeyKind::ImageMask(..) |
|
||||
ClipItemKeyKind::BoxShadow(..) => true,
|
||||
ClipItemKeyKind::BoxShadow(..) => ClipNodeKind::Complex,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1097,7 +1058,7 @@ impl intern::InternDebug for ClipItemKey {}
|
|||
impl intern::Internable for ClipIntern {
|
||||
type Key = ClipItemKey;
|
||||
type StoreData = ClipNode;
|
||||
type InternData = ();
|
||||
type InternData = ClipNodeKind;
|
||||
}
|
||||
|
||||
#[derive(Debug, MallocSizeOf)]
|
||||
|
|
|
@ -12,7 +12,7 @@ use api::{PropertyBinding, ReferenceFrame, ReferenceFrameKind, ScrollFrameDispla
|
|||
use api::{Shadow, SpaceAndClipInfo, SpatialId, StackingContext, StickyFrameDisplayItem};
|
||||
use api::{ClipMode, PrimitiveKeyKind, TransformStyle, YuvColorSpace, ColorRange, YuvData, TempFilterData};
|
||||
use api::units::*;
|
||||
use crate::clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore, ClipItemKeyKind};
|
||||
use crate::clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore, ClipItemKeyKind, ClipDataHandle, ClipNodeKind};
|
||||
use crate::clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex};
|
||||
use crate::frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig};
|
||||
use crate::glyph_rasterizer::FontInstance;
|
||||
|
@ -466,6 +466,31 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
let mut clip_chain_instances = Vec::new();
|
||||
let mut clip_chain_instance_stack = Vec::new();
|
||||
|
||||
// Maintain a list of clip node handles that are shared by every primitive
|
||||
// in this list. These are often root / fixed position clips. We collect these
|
||||
// and handle them when compositing the picture cache tiles. This saves per-item
|
||||
// clip work, but more importantly, it avoids lots of invalidations due to
|
||||
// content being clipped by fixed iframe/scrollframe rects.
|
||||
|
||||
// TODO(gw): The logic to build the shared clips list is quite complicated, because:
|
||||
// (a) We want to cache result and not do a heap of extra work per primitive,
|
||||
// since stress tests like dl_mutate have 50000+ primitives. Since each
|
||||
// primitive often shares the same clip chain as the previous primitive,
|
||||
// caching the last set of prim clips is generally sufficient.
|
||||
// (b) The general logic to set up picture caching is still complicated due to
|
||||
// not caching multiple slices (e.g. scroll bars). Once we enable
|
||||
// multiple picture cache slices, this logic becomes much simpler.
|
||||
|
||||
// The clips we have found that exist on every primitive we are going to cache.
|
||||
let mut shared_clips = Vec::new();
|
||||
// The clips found the last time we traversed a set of clip chains. Stored and cleared
|
||||
// here to avoid constant allocations.
|
||||
let mut prim_clips = Vec::new();
|
||||
// If true, the cache is out of date and needs to be rebuilt.
|
||||
let mut update_shared_clips = true;
|
||||
// The last prim clip chain we build prim_clips for.
|
||||
let mut last_prim_clip_chain_id = ClipChainId::NONE;
|
||||
|
||||
/// Records the indices in the list of a push/pop clip chain instance pair.
|
||||
#[derive(Debug)]
|
||||
struct ClipChainPairInfo {
|
||||
|
@ -475,11 +500,25 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
clip_chain_id: ClipChainId,
|
||||
}
|
||||
|
||||
for (i, instance) in primitives.iter().enumerate() {
|
||||
let scroll_root = self.clip_scroll_tree.find_scroll_root(
|
||||
instance.spatial_node_index,
|
||||
);
|
||||
// Helper fn to collect clip handles from a given clip chain.
|
||||
fn add_clips(
|
||||
clip_chain_id: ClipChainId,
|
||||
prim_clips: &mut Vec<ClipDataHandle>,
|
||||
clip_store: &ClipStore,
|
||||
) {
|
||||
let mut current_clip_chain_id = clip_chain_id;
|
||||
|
||||
while current_clip_chain_id != ClipChainId::NONE {
|
||||
let clip_chain_node = &clip_store
|
||||
.clip_chain_nodes[current_clip_chain_id.0 as usize];
|
||||
|
||||
prim_clips.push(clip_chain_node.handle);
|
||||
|
||||
current_clip_chain_id = clip_chain_node.parent_clip_chain_id;
|
||||
}
|
||||
}
|
||||
|
||||
for (i, instance) in primitives.iter().enumerate() {
|
||||
// If we encounter a push/pop clip, record where they occurred in the
|
||||
// primitive list for later processing.
|
||||
match instance.kind {
|
||||
|
@ -491,6 +530,9 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
spatial_node_index: instance.spatial_node_index,
|
||||
clip_chain_id: instance.clip_chain_id,
|
||||
});
|
||||
// Invalidate the prim_clips cache - there is a new clip chain.
|
||||
update_shared_clips = true;
|
||||
continue;
|
||||
}
|
||||
PrimitiveInstanceKind::PopClipChain => {
|
||||
let index = clip_chain_instance_stack.pop().unwrap();
|
||||
|
@ -505,10 +547,54 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
instance.spatial_node_index,
|
||||
);
|
||||
clip_chain_instance.pop_index = i;
|
||||
// Invalidate the prim_clips cache - a clip chain was removed.
|
||||
update_shared_clips = true;
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// If the primitive clip chain is different, then we need to rebuild prim_clips.
|
||||
update_shared_clips |= last_prim_clip_chain_id != instance.clip_chain_id;
|
||||
last_prim_clip_chain_id = instance.clip_chain_id;
|
||||
|
||||
if update_shared_clips {
|
||||
prim_clips.clear();
|
||||
// Collect any clips from the clip chain stack that will affect this prim.
|
||||
for clip_instance_index in &clip_chain_instance_stack {
|
||||
let clip_instance = &clip_chain_instances[*clip_instance_index];
|
||||
add_clips(
|
||||
clip_instance.clip_chain_id,
|
||||
&mut prim_clips,
|
||||
&self.clip_store,
|
||||
);
|
||||
}
|
||||
// Collect any clips from the primitive's specific clip chain.
|
||||
add_clips(
|
||||
instance.clip_chain_id,
|
||||
&mut prim_clips,
|
||||
&self.clip_store,
|
||||
);
|
||||
|
||||
// We want to only retain clips that are shared across all primitives.
|
||||
// TODO(gw): We could consider using a HashSet here, but:
|
||||
// (a) The sizes of these arrays are typically very small (<< 10).
|
||||
// (b) We would have to impl Ord/Eq on interner handles, which we
|
||||
// otherwise don't need / want.
|
||||
shared_clips.retain(|h1: &ClipDataHandle| {
|
||||
let uid = h1.uid();
|
||||
prim_clips.iter().any(|h2| {
|
||||
uid == h2.uid()
|
||||
})
|
||||
});
|
||||
|
||||
update_shared_clips = false;
|
||||
}
|
||||
|
||||
let scroll_root = self.clip_scroll_tree.find_scroll_root(
|
||||
instance.spatial_node_index,
|
||||
);
|
||||
|
||||
if scroll_root != ROOT_SPATIAL_NODE_INDEX {
|
||||
// If we find multiple scroll roots in this page, then skip
|
||||
// picture caching for now. In future, we can handle picture
|
||||
|
@ -527,6 +613,9 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
}
|
||||
|
||||
if first_index.is_none() {
|
||||
// The first time we identify a prim that will be cached, set the prim_clips
|
||||
// array to this, such that the retain() logic above works.
|
||||
shared_clips = prim_clips.clone();
|
||||
first_index = Some(i);
|
||||
}
|
||||
}
|
||||
|
@ -547,34 +636,20 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
|
||||
let mut preceding_prims;
|
||||
let mut remaining_prims;
|
||||
let mut trailing_prims;
|
||||
|
||||
match first_index {
|
||||
Some(first_index) => {
|
||||
// Split off the preceding primtives.
|
||||
remaining_prims = old_prim_list.split_off(first_index);
|
||||
|
||||
// Find the first primitive in reverse order that is not the root scroll node.
|
||||
let last_index = remaining_prims.iter().rposition(|instance| {
|
||||
let scroll_root = self.clip_scroll_tree.find_scroll_root(
|
||||
instance.spatial_node_index,
|
||||
);
|
||||
|
||||
scroll_root != ROOT_SPATIAL_NODE_INDEX
|
||||
}).unwrap_or(remaining_prims.len() - 1);
|
||||
|
||||
preceding_prims = old_prim_list;
|
||||
trailing_prims = remaining_prims.split_off(last_index + 1);
|
||||
}
|
||||
None => {
|
||||
preceding_prims = Vec::new();
|
||||
remaining_prims = old_prim_list;
|
||||
trailing_prims = Vec::new();
|
||||
}
|
||||
}
|
||||
|
||||
let mid_index = preceding_prims.len();
|
||||
let post_index = mid_index + remaining_prims.len();
|
||||
|
||||
// Step through each clip chain pair, and see if it crosses a slice boundary.
|
||||
for clip_chain_instance in clip_chain_instances {
|
||||
|
@ -596,25 +671,6 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
if clip_chain_instance.push_index < post_index && clip_chain_instance.pop_index >= post_index {
|
||||
remaining_prims.push(
|
||||
create_clip_prim_instance(
|
||||
clip_chain_instance.spatial_node_index,
|
||||
clip_chain_instance.clip_chain_id,
|
||||
PrimitiveInstanceKind::PopClipChain,
|
||||
)
|
||||
);
|
||||
|
||||
trailing_prims.insert(
|
||||
0,
|
||||
create_clip_prim_instance(
|
||||
clip_chain_instance.spatial_node_index,
|
||||
clip_chain_instance.clip_chain_id,
|
||||
PrimitiveInstanceKind::PushClipChain,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let prim_list = PrimitiveList::new(
|
||||
|
@ -642,10 +698,25 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
}
|
||||
);
|
||||
|
||||
// Build a clip-chain for the tile cache, that contains any of the shared clips
|
||||
// we will apply when drawing the tiles. In all cases provided by Gecko, these
|
||||
// are rectangle clips with a scale/offset transform only, and get handled as
|
||||
// a simple local clip rect in the vertex shader. However, this should in theory
|
||||
// also work with any complex clips, such as rounded rects and image masks, by
|
||||
// producing a clip mask that is applied to the picture cache tiles.
|
||||
let mut parent_clip_chain_id = ClipChainId::NONE;
|
||||
for clip_handle in &shared_clips {
|
||||
parent_clip_chain_id = self.clip_store.add_clip_chain_node(
|
||||
*clip_handle,
|
||||
parent_clip_chain_id,
|
||||
);
|
||||
}
|
||||
|
||||
let tile_cache = Box::new(TileCacheInstance::new(
|
||||
0,
|
||||
main_scroll_root,
|
||||
self.config.background_color,
|
||||
shared_clips,
|
||||
));
|
||||
|
||||
let pic_index = self.prim_store.pictures.alloc().init(PicturePrimitive::new_image(
|
||||
|
@ -669,16 +740,15 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
pic_index: PictureIndex(pic_index),
|
||||
segment_instance_index: SegmentInstanceIndex::INVALID,
|
||||
},
|
||||
ClipChainId::NONE,
|
||||
parent_clip_chain_id,
|
||||
main_scroll_root,
|
||||
);
|
||||
|
||||
// This contains the tile caching picture, with preceding and
|
||||
// trailing primitives outside the main scroll root.
|
||||
primitives.reserve(preceding_prims.len() + trailing_prims.len() + 1);
|
||||
primitives.reserve(preceding_prims.len() + 1);
|
||||
primitives.extend(preceding_prims);
|
||||
primitives.push(instance);
|
||||
primitives.extend(trailing_prims);
|
||||
}
|
||||
|
||||
fn flatten_items(
|
||||
|
@ -1297,17 +1367,14 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
|
||||
for _ in 0 .. item_clip_node.count {
|
||||
// Get the id of the clip sources entry for that clip chain node.
|
||||
let (handle, has_complex_clip) = {
|
||||
let handle = {
|
||||
let clip_chain = self
|
||||
.clip_store
|
||||
.get_clip_chain(clip_node_clip_chain_id);
|
||||
|
||||
clip_node_clip_chain_id = clip_chain.parent_clip_chain_id;
|
||||
|
||||
(
|
||||
clip_chain.handle,
|
||||
clip_chain.has_complex_clip,
|
||||
)
|
||||
clip_chain.handle
|
||||
};
|
||||
|
||||
// Add a new clip chain node, which references the same clip sources, and
|
||||
|
@ -1317,7 +1384,6 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
.add_clip_chain_node(
|
||||
handle,
|
||||
clip_chain_id,
|
||||
has_complex_clip,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1405,15 +1471,13 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
for item in clip_items {
|
||||
// Intern this clip item, and store the handle
|
||||
// in the clip chain node.
|
||||
let has_complex_clip = item.kind.has_complex_clip();
|
||||
let handle = self.interners
|
||||
.clip
|
||||
.intern(&item, || ());
|
||||
.intern(&item, || item.kind.node_kind());
|
||||
|
||||
clip_chain_id = self.clip_store.add_clip_chain_node(
|
||||
handle,
|
||||
clip_chain_id,
|
||||
has_complex_clip,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1709,7 +1773,9 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
.clip_store
|
||||
.clip_chain_nodes[current_clip_chain_id.0 as usize];
|
||||
|
||||
if clip_chain_node.has_complex_clip {
|
||||
let clip_kind = self.interners.clip[clip_chain_node.handle];
|
||||
|
||||
if let ClipNodeKind::Complex = clip_kind {
|
||||
blit_reason = BlitReason::CLIP;
|
||||
break;
|
||||
}
|
||||
|
@ -1853,10 +1919,17 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
}
|
||||
);
|
||||
|
||||
// TODO(gw): For now, we don't bother trying to share any clips if we create an
|
||||
// implicit picture cache. This cache always caches with a fixed position
|
||||
// root scroll node, so it won't save any invalidations. It might save
|
||||
// some per-item clipping work though. We should handle this by unifying
|
||||
// the implicit cache creation code here to work as part of the normal
|
||||
// setup_picture_caching method.
|
||||
let tile_cache = TileCacheInstance::new(
|
||||
0,
|
||||
ROOT_SPATIAL_NODE_INDEX,
|
||||
self.config.background_color,
|
||||
Vec::new(),
|
||||
);
|
||||
|
||||
let pic_index = self.prim_store.pictures.alloc().init(PicturePrimitive::new_image(
|
||||
|
@ -2163,14 +2236,13 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
let handle = self
|
||||
.interners
|
||||
.clip
|
||||
.intern(&item, || ());
|
||||
.intern(&item, || ClipNodeKind::Rectangle);
|
||||
|
||||
parent_clip_chain_index = self
|
||||
.clip_store
|
||||
.add_clip_chain_node(
|
||||
handle,
|
||||
parent_clip_chain_index,
|
||||
false,
|
||||
);
|
||||
clip_count += 1;
|
||||
|
||||
|
@ -2183,14 +2255,13 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
let handle = self
|
||||
.interners
|
||||
.clip
|
||||
.intern(&item, || ());
|
||||
.intern(&item, || ClipNodeKind::Complex);
|
||||
|
||||
parent_clip_chain_index = self
|
||||
.clip_store
|
||||
.add_clip_chain_node(
|
||||
handle,
|
||||
parent_clip_chain_index,
|
||||
true,
|
||||
);
|
||||
clip_count += 1;
|
||||
}
|
||||
|
@ -2208,14 +2279,13 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
let handle = self
|
||||
.interners
|
||||
.clip
|
||||
.intern(&item, || ());
|
||||
.intern(&item, || ClipNodeKind::Complex);
|
||||
|
||||
parent_clip_chain_index = self
|
||||
.clip_store
|
||||
.add_clip_chain_node(
|
||||
handle,
|
||||
parent_clip_chain_index,
|
||||
true,
|
||||
);
|
||||
clip_count += 1;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use api::{PropertyBinding, PropertyBindingId, FilterPrimitive, FontRenderMode};
|
|||
use api::{DebugFlags, RasterSpace, ImageKey, ColorF};
|
||||
use api::units::*;
|
||||
use crate::box_shadow::{BLUR_SAMPLE_SCALE};
|
||||
use crate::clip::{ClipStore, ClipDataStore, ClipChainInstance};
|
||||
use crate::clip::{ClipStore, ClipDataStore, ClipChainInstance, ClipDataHandle};
|
||||
use crate::clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX,
|
||||
ClipScrollTree, CoordinateSpaceMapping, SpatialNodeIndex, VisibleFace, CoordinateSystemId
|
||||
};
|
||||
|
@ -34,7 +34,6 @@ use crate::render_task::{RenderTask, RenderTaskLocation, BlurTaskCache, ClearMod
|
|||
use crate::resource_cache::ResourceCache;
|
||||
use crate::scene::SceneProperties;
|
||||
use crate::scene_builder::Interners;
|
||||
use crate::spatial_node::SpatialNodeType;
|
||||
use smallvec::SmallVec;
|
||||
use std::{mem, u16};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
@ -1082,14 +1081,8 @@ pub struct TileCacheInstance {
|
|||
tile_bounds_p1: TileOffset,
|
||||
/// Local rect (unclipped) of the picture this cache covers.
|
||||
pub local_rect: PictureRect,
|
||||
/// Local clip rect for this tile cache.
|
||||
pub local_clip_rect: PictureRect,
|
||||
/// A list of tiles that are valid and visible, which should be drawn to the main scene.
|
||||
pub tiles_to_draw: Vec<TileOffset>,
|
||||
/// The world space viewport that this tile cache draws into.
|
||||
/// Any clips outside this viewport can be ignored (and must be removed so that
|
||||
/// we can draw outside the bounds of the viewport).
|
||||
pub world_viewport_rect: WorldRect,
|
||||
/// 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
|
||||
|
@ -1104,6 +1097,11 @@ pub struct TileCacheInstance {
|
|||
/// all tiles need to be invalidated and redrawn, since snapping differences are
|
||||
/// likely to occur.
|
||||
fract_offset: PictureVector2D,
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
impl TileCacheInstance {
|
||||
|
@ -1111,6 +1109,7 @@ impl TileCacheInstance {
|
|||
slice: usize,
|
||||
spatial_node_index: SpatialNodeIndex,
|
||||
background_color: Option<ColorF>,
|
||||
shared_clips: Vec<ClipDataHandle>,
|
||||
) -> Self {
|
||||
TileCacheInstance {
|
||||
slice,
|
||||
|
@ -1129,14 +1128,13 @@ impl TileCacheInstance {
|
|||
tile_bounds_p0: TileOffset::zero(),
|
||||
tile_bounds_p1: TileOffset::zero(),
|
||||
local_rect: PictureRect::zero(),
|
||||
local_clip_rect: PictureRect::zero(),
|
||||
tiles_to_draw: Vec::new(),
|
||||
world_viewport_rect: WorldRect::zero(),
|
||||
surface_index: SurfaceIndex(0),
|
||||
background_color,
|
||||
backdrop: BackdropInfo::empty(),
|
||||
subpixel_mode: SubpixelMode::Allow,
|
||||
fract_offset: PictureVector2D::zero(),
|
||||
shared_clips,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1253,49 +1251,7 @@ impl TileCacheInstance {
|
|||
self.fract_offset = fract_offset;
|
||||
}
|
||||
|
||||
let spatial_node = &frame_context
|
||||
.clip_scroll_tree
|
||||
.spatial_nodes[self.spatial_node_index.0 as usize];
|
||||
let (viewport_rect, viewport_spatial_node_index) = match spatial_node.node_type {
|
||||
SpatialNodeType::ScrollFrame(ref info) => {
|
||||
(info.viewport_rect, spatial_node.parent.unwrap())
|
||||
}
|
||||
SpatialNodeType::StickyFrame(..) => {
|
||||
unreachable!();
|
||||
}
|
||||
SpatialNodeType::ReferenceFrame(..) => {
|
||||
assert_eq!(self.spatial_node_index, ROOT_SPATIAL_NODE_INDEX);
|
||||
(LayoutRect::max_rect(), ROOT_SPATIAL_NODE_INDEX)
|
||||
}
|
||||
};
|
||||
|
||||
let viewport_to_world_mapper = SpaceMapper::new_with_target(
|
||||
ROOT_SPATIAL_NODE_INDEX,
|
||||
viewport_spatial_node_index,
|
||||
frame_context.global_screen_world_rect,
|
||||
frame_context.clip_scroll_tree,
|
||||
);
|
||||
self.world_viewport_rect = viewport_to_world_mapper
|
||||
.map(&viewport_rect)
|
||||
.expect("bug: unable to map viewport to world space");
|
||||
|
||||
// TODO(gw): This is a reverse mapping. It should always work since we know
|
||||
// that this path only runs for slices in the root coordinate system.
|
||||
// But perhaps we should assert that?
|
||||
// TODO(gw): We could change to directly use the ScaleOffset in content_transform
|
||||
// which would make this clearer that we know the coordinate systems are the
|
||||
// same and that it's a safe / exact conversion.
|
||||
self.map_local_to_surface.set_target_spatial_node(
|
||||
viewport_spatial_node_index,
|
||||
frame_context.clip_scroll_tree,
|
||||
);
|
||||
let local_viewport_rect = self
|
||||
.map_local_to_surface
|
||||
.map(&viewport_rect)
|
||||
.expect("bug: unable to map to local viewport rect");
|
||||
|
||||
self.local_rect = pic_rect;
|
||||
self.local_clip_rect = local_viewport_rect;
|
||||
|
||||
// Do a hacky diff of opacity binding values from the last frame. This is
|
||||
// used later on during tile invalidation tests.
|
||||
|
@ -1330,15 +1286,11 @@ impl TileCacheInstance {
|
|||
.unmap(&frame_context.global_screen_world_rect)
|
||||
.expect("unable to unmap screen rect");
|
||||
|
||||
let visible_rect_in_pic_space = screen_rect_in_pic_space
|
||||
.intersection(&self.local_clip_rect)
|
||||
.unwrap_or(PictureRect::zero());
|
||||
|
||||
// Inflate the needed rect a bit, so that we retain tiles that we have drawn
|
||||
// but have just recently gone off-screen. This means that we avoid re-drawing
|
||||
// tiles if the user is scrolling up and down small amounts, at the cost of
|
||||
// a bit of extra texture memory.
|
||||
let desired_rect_in_pic_space = visible_rect_in_pic_space
|
||||
let desired_rect_in_pic_space = screen_rect_in_pic_space
|
||||
.inflate(0.0, 3.0 * self.tile_size.height);
|
||||
|
||||
let needed_rect_in_pic_space = desired_rect_in_pic_space
|
||||
|
|
|
@ -1804,7 +1804,7 @@ impl PrimitiveStore {
|
|||
None => (parent_surface_index, false)
|
||||
};
|
||||
|
||||
let viewport = match pic.raster_config {
|
||||
match pic.raster_config {
|
||||
Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { .. }, .. }) => {
|
||||
let mut tile_cache = pic.tile_cache.take().unwrap();
|
||||
debug_assert!(frame_state.tile_cache.is_none());
|
||||
|
@ -1819,20 +1819,17 @@ impl PrimitiveStore {
|
|||
frame_state,
|
||||
);
|
||||
|
||||
let viewport = tile_cache.world_viewport_rect;
|
||||
|
||||
// Push a new surface, supplying the list of clips that should be
|
||||
// ignored, since they are handled by clipping when drawing this surface.
|
||||
frame_state.clip_chain_stack.push_surface(&tile_cache.shared_clips);
|
||||
frame_state.tile_cache = Some(tile_cache);
|
||||
|
||||
viewport
|
||||
}
|
||||
_ => {
|
||||
WorldRect::max_rect()
|
||||
if is_composite {
|
||||
frame_state.clip_chain_stack.push_surface(&[]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if is_composite {
|
||||
frame_state.clip_chain_stack.push_surface(viewport);
|
||||
};
|
||||
}
|
||||
|
||||
(prim_list, surface_index, pic.apply_local_clip_rect, world_culling_rect, is_composite)
|
||||
};
|
||||
|
@ -1888,9 +1885,6 @@ impl PrimitiveStore {
|
|||
frame_state.clip_chain_stack.push_clip(
|
||||
prim_instance.clip_chain_id,
|
||||
frame_state.clip_store,
|
||||
frame_state.data_stores,
|
||||
frame_context.clip_scroll_tree,
|
||||
frame_context.global_screen_world_rect,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -1906,9 +1900,6 @@ impl PrimitiveStore {
|
|||
frame_state.clip_chain_stack.push_clip(
|
||||
prim_instance.clip_chain_id,
|
||||
frame_state.clip_store,
|
||||
frame_state.data_stores,
|
||||
frame_context.clip_scroll_tree,
|
||||
frame_context.global_screen_world_rect,
|
||||
);
|
||||
|
||||
let pic_surface_rect = self.update_visibility(
|
||||
|
@ -2061,9 +2052,6 @@ impl PrimitiveStore {
|
|||
frame_state.clip_chain_stack.push_clip(
|
||||
prim_instance.clip_chain_id,
|
||||
frame_state.clip_store,
|
||||
frame_state.data_stores,
|
||||
frame_context.clip_scroll_tree,
|
||||
frame_context.global_screen_world_rect,
|
||||
);
|
||||
|
||||
frame_state.clip_store.set_active_clips(
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
expected:
|
||||
if (os == "android") and not e10s: FAIL
|
||||
if (os == "android") and e10s: PASS
|
||||
if webrender and (os == "win"): FAIL
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
expected:
|
||||
if (os == "android") and not e10s: FAIL
|
||||
if (os == "android") and e10s: PASS
|
||||
if webrender and (os == "win"): FAIL
|
||||
|
|
Загрузка…
Ссылка в новой задаче