Bug 1724846 - Split DL spatial tree to separate payload r=gfx-reviewers,nical

This will allow experimenting with different representations of
the spatial tree (such as interning and/or providing stable
indices during display list building). It may also simplify
future changes to the public API to expose the spatial tree
directly.

As part of these changes, refactor how the debug representation
for the capture format is (de)serialized, to make it simpler to
add different payload vector types in future.

Differential Revision: https://phabricator.services.mozilla.com/D122183
This commit is contained in:
Glenn Watson 2021-08-24 02:18:51 +00:00
parent 84d67f5d03
commit 3999ffe70d
15 changed files with 541 additions and 381 deletions

View File

@ -465,7 +465,7 @@ void AsyncImagePipelineManager::ApplyAsyncImageForPipeline(
aSceneBuilderTxn.SetDisplayList(gfx::DeviceColor(0.f, 0.f, 0.f, 0.f), aEpoch,
wr::ToLayoutSize(aPipeline->mScBounds.Size()),
aPipelineId, dl.dl_desc, dl.dl_items,
dl.dl_cache);
dl.dl_cache, dl.dl_spatial_tree);
}
void AsyncImagePipelineManager::ApplyAsyncImageForPipeline(
@ -520,7 +520,7 @@ void AsyncImagePipelineManager::SetEmptyDisplayList(
builder.Finalize(dl);
txn.SetDisplayList(gfx::DeviceColor(0.f, 0.f, 0.f, 0.f), epoch,
wr::ToLayoutSize(pipeline->mScBounds.Size()), aPipelineId,
dl.dl_desc, dl.dl_items, dl.dl_cache);
dl.dl_desc, dl.dl_items, dl.dl_cache, dl.dl_spatial_tree);
}
void AsyncImagePipelineManager::HoldExternalImage(

View File

@ -18,6 +18,7 @@ void IPDLParamTraits<mozilla::layers::DisplayListData>::Write(
WriteIPDLParam(aMsg, aActor, aParam.mCommands);
WriteIPDLParam(aMsg, aActor, std::move(aParam.mDLItems));
WriteIPDLParam(aMsg, aActor, std::move(aParam.mDLCache));
WriteIPDLParam(aMsg, aActor, std::move(aParam.mDLSpatialTree));
WriteIPDLParam(aMsg, aActor, aParam.mDLDesc);
WriteIPDLParam(aMsg, aActor, aParam.mRemotePipelineIds);
WriteIPDLParam(aMsg, aActor, aParam.mResourceUpdates);
@ -34,6 +35,7 @@ bool IPDLParamTraits<mozilla::layers::DisplayListData>::Read(
ReadIPDLParam(aMsg, aIter, aActor, &aResult->mCommands) &&
ReadIPDLParam(aMsg, aIter, aActor, &aResult->mDLItems) &&
ReadIPDLParam(aMsg, aIter, aActor, &aResult->mDLCache) &&
ReadIPDLParam(aMsg, aIter, aActor, &aResult->mDLSpatialTree) &&
ReadIPDLParam(aMsg, aIter, aActor, &aResult->mDLDesc) &&
ReadIPDLParam(aMsg, aIter, aActor, &aResult->mRemotePipelineIds) &&
ReadIPDLParam(aMsg, aIter, aActor, &aResult->mResourceUpdates) &&

View File

@ -22,6 +22,7 @@ struct DisplayListData {
nsTArray<WebRenderParentCommand> mCommands;
Maybe<mozilla::ipc::ByteBuf> mDLItems;
Maybe<mozilla::ipc::ByteBuf> mDLCache;
Maybe<mozilla::ipc::ByteBuf> mDLSpatialTree;
wr::BuiltDisplayListDescriptor mDLDesc;
nsTArray<wr::PipelineId> mRemotePipelineIds;
nsTArray<OpUpdateResource> mResourceUpdates;

View File

@ -1101,7 +1101,8 @@ void WebRenderBridgeParent::SetAPZSampleTime() {
bool WebRenderBridgeParent::SetDisplayList(
const LayoutDeviceRect& aRect, ipc::ByteBuf&& aDLItems,
ipc::ByteBuf&& aDLCache, const wr::BuiltDisplayListDescriptor& aDLDesc,
ipc::ByteBuf&& aDLCache, ipc::ByteBuf&& aSpatialTreeDL,
const wr::BuiltDisplayListDescriptor& aDLDesc,
const nsTArray<OpUpdateResource>& aResourceUpdates,
const nsTArray<RefCountedShmem>& aSmallShmems,
const nsTArray<ipc::Shmem>& aLargeShmems, const TimeStamp& aTxnStartTime,
@ -1114,6 +1115,7 @@ bool WebRenderBridgeParent::SetDisplayList(
wr::Vec<uint8_t> dlItems(std::move(aDLItems));
wr::Vec<uint8_t> dlCache(std::move(aDLCache));
wr::Vec<uint8_t> dlSpatialTreeData(std::move(aSpatialTreeDL));
if (IsRootWebRenderBridgeParent()) {
#ifdef MOZ_WIDGET_GTK
@ -1129,7 +1131,7 @@ bool WebRenderBridgeParent::SetDisplayList(
gfx::DeviceColor clearColor(0.f, 0.f, 0.f, 0.f);
aTxn.SetDisplayList(clearColor, aWrEpoch,
wr::ToLayoutSize(RoundedToInt(aRect).Size()), mPipelineId,
aDLDesc, dlItems, dlCache);
aDLDesc, dlItems, dlCache, dlSpatialTreeData);
if (aObserveLayersUpdate) {
aTxn.Notify(
@ -1175,10 +1177,12 @@ bool WebRenderBridgeParent::ProcessDisplayListData(
return false;
}
if (aDisplayList.mDLItems && aDisplayList.mDLCache && aValidTransaction &&
if (aDisplayList.mDLItems && aDisplayList.mDLCache &&
aDisplayList.mDLSpatialTree && aValidTransaction &&
!SetDisplayList(aDisplayList.mRect,
std::move(aDisplayList.mDLItems.ref()),
std::move(aDisplayList.mDLCache.ref()),
std::move(aDisplayList.mDLSpatialTree.ref()),
aDisplayList.mDLDesc, aDisplayList.mResourceUpdates,
aDisplayList.mSmallShmems, aDisplayList.mLargeShmems,
aTxnStartTime, txn, aWrEpoch, aObserveLayersUpdate)) {

View File

@ -332,7 +332,7 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent,
bool aObserveLayersUpdate);
bool SetDisplayList(const LayoutDeviceRect& aRect, ipc::ByteBuf&& aDLItems,
ipc::ByteBuf&& aDLCache,
ipc::ByteBuf&& aDLCache, ipc::ByteBuf&& aSpatialTreeDL,
const wr::BuiltDisplayListDescriptor& aDLDesc,
const nsTArray<OpUpdateResource>& aResourceUpdates,
const nsTArray<RefCountedShmem>& aSmallShmems,

View File

@ -455,6 +455,7 @@ void WebRenderLayerManager::EndTransactionWithoutLayer(
builder.Finalize(dlData);
mLastDisplayListSize.items_size = dlData.mDLItems->mCapacity;
mLastDisplayListSize.cache_size = dlData.mDLCache->mCapacity;
mLastDisplayListSize.spatial_tree_size = dlData.mDLSpatialTree->mCapacity;
resourceUpdates.Flush(dlData.mResourceUpdates, dlData.mSmallShmems,
dlData.mLargeShmems);
dlData.mRect =

View File

@ -263,10 +263,12 @@ void TransactionBuilder::SetDisplayList(
const gfx::DeviceColor& aBgColor, Epoch aEpoch,
const wr::LayoutSize& aViewportSize, wr::WrPipelineId pipeline_id,
wr::BuiltDisplayListDescriptor dl_descriptor,
wr::Vec<uint8_t>& dl_items_data, wr::Vec<uint8_t>& dl_cache_data) {
wr::Vec<uint8_t>& dl_items_data, wr::Vec<uint8_t>& dl_cache_data,
wr::Vec<uint8_t>& dl_spatial_tree) {
wr_transaction_set_display_list(mTxn, aEpoch, ToColorF(aBgColor),
aViewportSize, pipeline_id, dl_descriptor,
&dl_items_data.inner, &dl_cache_data.inner);
&dl_items_data.inner, &dl_cache_data.inner,
&dl_spatial_tree.inner);
}
void TransactionBuilder::ClearDisplayList(Epoch aEpoch,
@ -996,9 +998,9 @@ void DisplayListBuilder::DumpSerializedDisplayList() {
}
void DisplayListBuilder::Finalize(BuiltDisplayList& aOutDisplayList) {
wr_api_finalize_builder(mWrState, &aOutDisplayList.dl_desc,
&aOutDisplayList.dl_items.inner,
&aOutDisplayList.dl_cache.inner);
wr_api_finalize_builder(
mWrState, &aOutDisplayList.dl_desc, &aOutDisplayList.dl_items.inner,
&aOutDisplayList.dl_cache.inner, &aOutDisplayList.dl_spatial_tree.inner);
}
void DisplayListBuilder::Finalize(layers::DisplayListData& aOutTransaction) {
@ -1006,18 +1008,23 @@ void DisplayListBuilder::Finalize(layers::DisplayListData& aOutTransaction) {
wr_dp_set_cache_size(mWrState, mDisplayItemCache->CurrentSize());
}
wr::VecU8 dlItems, dlCache;
wr::VecU8 dlItems, dlCache, dlSpatialTree;
wr_api_finalize_builder(mWrState, &aOutTransaction.mDLDesc, &dlItems.inner,
&dlCache.inner);
&dlCache.inner, &dlSpatialTree.inner);
aOutTransaction.mDLItems.emplace(dlItems.inner.data, dlItems.inner.length,
dlItems.inner.capacity);
aOutTransaction.mDLCache.emplace(dlCache.inner.data, dlCache.inner.length,
dlCache.inner.capacity);
aOutTransaction.mDLSpatialTree.emplace(dlSpatialTree.inner.data,
dlSpatialTree.inner.length,
dlSpatialTree.inner.capacity);
aOutTransaction.mRemotePipelineIds = std::move(mRemotePipelineIds);
dlItems.inner.capacity = 0;
dlItems.inner.data = nullptr;
dlCache.inner.capacity = 0;
dlCache.inner.data = nullptr;
dlSpatialTree.inner.capacity = 0;
dlSpatialTree.inner.data = nullptr;
}
Maybe<wr::WrSpatialId> DisplayListBuilder::PushStackingContext(

View File

@ -113,7 +113,8 @@ class TransactionBuilder final {
wr::WrPipelineId pipeline_id,
wr::BuiltDisplayListDescriptor dl_descriptor,
wr::Vec<uint8_t>& dl_items_data,
wr::Vec<uint8_t>& dl_cache_data);
wr::Vec<uint8_t>& dl_cache_data,
wr::Vec<uint8_t>& dl_spatial_tree);
void ClearDisplayList(Epoch aEpoch, wr::WrPipelineId aPipeline);

View File

@ -740,6 +740,7 @@ struct ByteBuffer {
struct BuiltDisplayList {
wr::VecU8 dl_items;
wr::VecU8 dl_cache;
wr::VecU8 dl_spatial_tree;
wr::BuiltDisplayListDescriptor dl_desc;
};

View File

@ -1842,6 +1842,7 @@ pub extern "C" fn wr_transaction_set_display_list(
dl_descriptor: BuiltDisplayListDescriptor,
dl_items_data: &mut WrVecU8,
dl_cache_data: &mut WrVecU8,
dl_spatial_tree_data: &mut WrVecU8,
) {
let color = if background.a == 0.0 { None } else { Some(background) };
@ -1853,6 +1854,7 @@ pub extern "C" fn wr_transaction_set_display_list(
let payload = DisplayListPayload {
items_data: dl_items_data.flush_into_vec(),
cache_data: dl_cache_data.flush_into_vec(),
spatial_tree: dl_spatial_tree_data.flush_into_vec(),
};
let dl = BuiltDisplayList::from_data(payload, dl_descriptor);
@ -3774,12 +3776,14 @@ pub unsafe extern "C" fn wr_api_finalize_builder(
dl_descriptor: &mut BuiltDisplayListDescriptor,
dl_items_data: &mut WrVecU8,
dl_cache_data: &mut WrVecU8,
dl_spatial_tree: &mut WrVecU8,
) {
let frame_builder = mem::replace(&mut state.frame_builder, WebRenderFrameBuilder::new(state.pipeline_id));
let (_, dl) = frame_builder.dl_builder.finalize();
let (payload, descriptor) = dl.into_data();
*dl_items_data = WrVecU8::from_vec(payload.items_data);
*dl_cache_data = WrVecU8::from_vec(payload.cache_data);
*dl_spatial_tree = WrVecU8::from_vec(payload.spatial_tree);
*dl_descriptor = descriptor;
}

View File

@ -35,16 +35,16 @@
//! - backdrop filters (see add_backdrop_filter)
//!
use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayListIter, PrimitiveFlags};
use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayListIter, BuiltDisplayList, PrimitiveFlags};
use api::{ClipId, ColorF, CommonItemProperties, ComplexClipRegion, ComponentTransferFuncType, RasterSpace};
use api::{DisplayItem, DisplayItemRef, ExtendMode, ExternalScrollId, FilterData, SharedFontInstanceMap};
use api::{FilterOp, FilterPrimitive, FontInstanceKey, FontSize, GlyphInstance, GlyphOptions, GradientStop};
use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, ColorDepth, QualitySettings};
use api::{LineOrientation, LineStyle, NinePatchBorderSource, PipelineId, MixBlendMode, StackingContextFlags};
use api::{PropertyBinding, ReferenceFrameKind, ScrollFrameDisplayItem, ScrollSensitivity};
use api::{Shadow, SpaceAndClipInfo, SpatialId, StickyFrameDisplayItem, ImageMask, ItemTag};
use api::{PropertyBinding, ReferenceFrameKind, ScrollFrameDescriptor, ScrollSensitivity, ReferenceFrameMapper};
use api::{Shadow, SpaceAndClipInfo, SpatialId, StickyFrameDescriptor, ImageMask, ItemTag};
use api::{ClipMode, PrimitiveKeyKind, TransformStyle, YuvColorSpace, ColorRange, YuvData, TempFilterData};
use api::{ReferenceTransformBinding, Rotation, FillRule};
use api::{ReferenceTransformBinding, Rotation, FillRule, SpatialTreeItem, ReferenceFrameDescriptor};
use api::units::*;
use crate::image_tiling::simplify_repeated_primitive;
use crate::clip::{ClipChainId, ClipItemKey, ClipStore, ClipItemKeyKind};
@ -89,78 +89,6 @@ use crate::util::{MaxRect, VecHelper};
use crate::filterdata::{SFilterDataComponent, SFilterData, SFilterDataKey};
use smallvec::SmallVec;
/// The offset stack for a given reference frame.
struct ReferenceFrameState {
/// A stack of current offsets from the current reference frame scope.
offsets: Vec<LayoutVector2D>,
}
/// Maps from stacking context layout coordinates into reference frame
/// relative coordinates.
struct ReferenceFrameMapper {
/// A stack of reference frame scopes.
frames: Vec<ReferenceFrameState>,
}
impl ReferenceFrameMapper {
fn new() -> Self {
ReferenceFrameMapper {
frames: vec![
ReferenceFrameState {
offsets: vec![
LayoutVector2D::zero(),
],
}
],
}
}
/// Push a new scope. This resets the current offset to zero, and is
/// used when a new reference frame or iframe is pushed.
fn push_scope(&mut self) {
self.frames.push(ReferenceFrameState {
offsets: vec![
LayoutVector2D::zero(),
],
});
}
/// Pop a reference frame scope off the stack.
fn pop_scope(&mut self) {
self.frames.pop().unwrap();
}
/// Push a new offset for the current scope. This is used when
/// a new stacking context is pushed.
fn push_offset(&mut self, offset: LayoutVector2D) {
let frame = self.frames.last_mut().unwrap();
let current_offset = *frame.offsets.last().unwrap();
frame.offsets.push(current_offset + offset);
}
/// Pop a local stacking context offset from the current scope.
fn pop_offset(&mut self) {
let frame = self.frames.last_mut().unwrap();
frame.offsets.pop().unwrap();
}
/// Retrieve the current offset to allow converting a stacking context
/// relative coordinate to be relative to the owing reference frame.
/// TODO(gw): We could perhaps have separate coordinate spaces for this,
/// however that's going to either mean a lot of changes to
/// public API code, or a lot of changes to internal code.
/// Before doing that, we should revisit how Gecko would
/// prefer to provide coordinates.
/// TODO(gw): For now, this includes only the reference frame relative
/// offset. Soon, we will expand this to include the initial
/// scroll offsets that are now available on scroll nodes. This
/// will allow normalizing the coordinates even between display
/// lists where APZ has scrolled the content.
fn current_offset(&self) -> LayoutVector2D {
*self.frames.last().unwrap().offsets.last().unwrap()
}
}
/// Offsets primitives (and clips) by the external scroll offset
/// supplied to scroll nodes.
pub struct ScrollOffsetMapper {
@ -632,6 +560,43 @@ impl<'a> SceneBuilder<'a> {
rf_offset + scroll_offset
}
fn build_spatial_tree_for_display_list(
&mut self,
dl: &BuiltDisplayList,
pipeline_id: PipelineId,
) {
dl.iter_spatial_tree(|item| {
match item {
SpatialTreeItem::ScrollFrame(descriptor) => {
let parent_space = self.get_space(descriptor.parent_space);
self.build_scroll_frame(
descriptor,
parent_space,
pipeline_id,
);
}
SpatialTreeItem::ReferenceFrame(descriptor) => {
let parent_space = self.get_space(descriptor.parent_spatial_id);
self.build_reference_frame(
descriptor,
parent_space,
pipeline_id,
);
}
SpatialTreeItem::StickyFrame(descriptor) => {
let parent_space = self.get_space(descriptor.parent_spatial_id);
self.build_sticky_frame(
descriptor,
parent_space,
);
}
SpatialTreeItem::Invalid => {
unreachable!();
}
}
});
}
fn build_all(&mut self, root_pipeline: &ScenePipeline) {
enum ContextKind<'a> {
Root,
@ -655,6 +620,10 @@ impl<'a> SceneBuilder<'a> {
root_pipeline.pipeline_id,
&root_pipeline.viewport_size,
);
self.build_spatial_tree_for_display_list(
&root_pipeline.display_list.display_list,
root_pipeline.pipeline_id,
);
let mut stack = vec![BuildContext {
pipeline_id: root_pipeline.pipeline_id,
@ -713,67 +682,9 @@ impl<'a> SceneBuilder<'a> {
traversal = subtraversal;
continue 'outer;
}
DisplayItem::PushReferenceFrame(ref info) => {
DisplayItem::PushReferenceFrame(..) => {
profile_scope!("build_reference_frame");
let parent_space = self.get_space(info.parent_spatial_id);
let mut subtraversal = item.sub_iter();
let current_offset = self.current_offset(parent_space);
let transform = match info.reference_frame.transform {
ReferenceTransformBinding::Static { binding } => binding,
ReferenceTransformBinding::Computed { scale_from, vertical_flip, rotation } => {
let content_size = &self.iframe_size.last().unwrap();
let mut transform = if let Some(scale_from) = scale_from {
// If we have a 90/270 degree rotation, then scale_from
// and content_size are in different coordinate spaces and
// we need to swap width/height for them to be correct.
match rotation {
Rotation::Degree0 |
Rotation::Degree180 => {
LayoutTransform::scale(
content_size.width / scale_from.width,
content_size.height / scale_from.height,
1.0
)
},
Rotation::Degree90 |
Rotation::Degree270 => {
LayoutTransform::scale(
content_size.height / scale_from.width,
content_size.width / scale_from.height,
1.0
)
}
}
} else {
LayoutTransform::identity()
};
if vertical_flip {
let content_size = &self.iframe_size.last().unwrap();
transform = transform
.then_translate(LayoutVector3D::new(0.0, content_size.height, 0.0))
.pre_scale(1.0, -1.0, 1.0);
}
let rotate = rotation.to_matrix(**content_size);
let transform = transform.then(&rotate);
PropertyBinding::Value(transform)
},
};
self.push_reference_frame(
info.reference_frame.id,
Some(parent_space),
bc.pipeline_id,
info.reference_frame.transform_style,
transform,
info.reference_frame.kind,
current_offset + info.origin.to_vector(),
);
self.rf_mapper.push_scope();
let new_context = BuildContext {
@ -793,30 +704,11 @@ impl<'a> SceneBuilder<'a> {
profile_scope!("iframe");
let space = self.get_space(info.space_and_clip.spatial_id);
let (size, subtraversal) = match self.push_iframe(info, space) {
let subtraversal = match self.push_iframe(info, space) {
Some(pair) => pair,
None => continue,
};
// Get a clip-chain id for the root clip for this pipeline. We will
// add that as an unconditional clip to any tile cache created within
// this iframe. This ensures these clips are handled by the tile cache
// compositing code, which is more efficient and accurate than applying
// these clips individually to each primitive.
let clip_id = ClipId::root(info.pipeline_id);
let clip_chain_id = self.get_clip_chain(clip_id);
// If this is a root iframe, force a new tile cache both before and after
// adding primitives for this iframe.
if self.iframe_size.is_empty() {
self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
assert!(self.root_iframe_clip.is_none());
self.root_iframe_clip = Some(clip_chain_id);
}
self.rf_mapper.push_scope();
self.iframe_size.push(size);
let new_context = BuildContext {
pipeline_id: info.pipeline_id,
kind: ContextKind::Iframe {
@ -880,7 +772,7 @@ impl<'a> SceneBuilder<'a> {
fn build_sticky_frame(
&mut self,
info: &StickyFrameDisplayItem,
info: &StickyFrameDescriptor,
parent_node_index: SpatialNodeIndex,
) {
let current_offset = self.current_offset(parent_node_index);
@ -901,9 +793,72 @@ impl<'a> SceneBuilder<'a> {
self.id_to_index_mapper.add_spatial_node(info.id, index);
}
fn build_reference_frame(
&mut self,
info: &ReferenceFrameDescriptor,
parent_space: SpatialNodeIndex,
pipeline_id: PipelineId,
) {
let transform = match info.reference_frame.transform {
ReferenceTransformBinding::Static { binding } => binding,
ReferenceTransformBinding::Computed { scale_from, vertical_flip, rotation } => {
let content_size = &self.iframe_size.last().unwrap();
let mut transform = if let Some(scale_from) = scale_from {
// If we have a 90/270 degree rotation, then scale_from
// and content_size are in different coordinate spaces and
// we need to swap width/height for them to be correct.
match rotation {
Rotation::Degree0 |
Rotation::Degree180 => {
LayoutTransform::scale(
content_size.width / scale_from.width,
content_size.height / scale_from.height,
1.0
)
},
Rotation::Degree90 |
Rotation::Degree270 => {
LayoutTransform::scale(
content_size.height / scale_from.width,
content_size.width / scale_from.height,
1.0
)
}
}
} else {
LayoutTransform::identity()
};
if vertical_flip {
let content_size = &self.iframe_size.last().unwrap();
transform = transform
.then_translate(LayoutVector3D::new(0.0, content_size.height, 0.0))
.pre_scale(1.0, -1.0, 1.0);
}
let rotate = rotation.to_matrix(**content_size);
let transform = transform.then(&rotate);
PropertyBinding::Value(transform)
},
};
self.push_reference_frame(
info.reference_frame.id,
Some(parent_space),
pipeline_id,
info.reference_frame.transform_style,
transform,
info.reference_frame.kind,
info.origin.to_vector(),
);
}
fn build_scroll_frame(
&mut self,
info: &ScrollFrameDisplayItem,
info: &ScrollFrameDescriptor,
parent_node_index: SpatialNodeIndex,
pipeline_id: PipelineId,
) {
@ -931,7 +886,7 @@ impl<'a> SceneBuilder<'a> {
&mut self,
info: &IframeDisplayItem,
spatial_node_index: SpatialNodeIndex,
) -> Option<(LayoutSize, BuiltDisplayListIter<'a>)> {
) -> Option<BuiltDisplayListIter<'a>> {
let iframe_pipeline_id = info.pipeline_id;
let pipeline = match self.scene.pipelines.get(&iframe_pipeline_id) {
Some(pipeline) => pipeline,
@ -990,7 +945,30 @@ impl<'a> SceneBuilder<'a> {
LayoutVector2D::zero(),
);
Some((bounds.size(), pipeline.display_list.iter()))
// Get a clip-chain id for the root clip for this pipeline. We will
// add that as an unconditional clip to any tile cache created within
// this iframe. This ensures these clips are handled by the tile cache
// compositing code, which is more efficient and accurate than applying
// these clips individually to each primitive.
let clip_id = ClipId::root(info.pipeline_id);
let clip_chain_id = self.get_clip_chain(clip_id);
// If this is a root iframe, force a new tile cache both before and after
// adding primitives for this iframe.
if self.iframe_size.is_empty() {
self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
assert!(self.root_iframe_clip.is_none());
self.root_iframe_clip = Some(clip_chain_id);
}
self.iframe_size.push(info.bounds.size());
self.rf_mapper.push_scope();
self.build_spatial_tree_for_display_list(
&pipeline.display_list.display_list,
iframe_pipeline_id,
);
Some(pipeline.display_list.iter())
}
fn get_space(
@ -1534,25 +1512,6 @@ impl<'a> SceneBuilder<'a> {
&clips,
);
},
DisplayItem::ScrollFrame(ref info) => {
profile_scope!("scrollframe");
let parent_space = self.get_space(info.parent_space);
self.build_scroll_frame(
info,
parent_space,
pipeline_id,
);
}
DisplayItem::StickyFrame(ref info) => {
profile_scope!("stickyframe");
let parent_space = self.get_space(info.parent_spatial_id);
self.build_sticky_frame(
info,
parent_space,
);
}
DisplayItem::BackdropFilter(ref info) => {
profile_scope!("backdrop");

View File

@ -115,6 +115,15 @@ impl SpaceAndClipInfo {
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
pub enum SpatialTreeItem {
ScrollFrame(ScrollFrameDescriptor),
ReferenceFrame(ReferenceFrameDescriptor),
StickyFrame(StickyFrameDescriptor),
Invalid,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
pub enum DisplayItem {
@ -142,8 +151,6 @@ pub enum DisplayItem {
ClipChain(ClipChainItem),
// Spaces and Frames that content can be scoped under.
ScrollFrame(ScrollFrameDisplayItem),
StickyFrame(StickyFrameDisplayItem),
Iframe(IframeDisplayItem),
PushReferenceFrame(ReferenceFrameDisplayListItem),
PushStackingContext(PushStackingContextDisplayItem),
@ -192,8 +199,6 @@ pub enum DebugDisplayItem {
RectClip(RectClipDisplayItem),
ClipChain(ClipChainItem, Vec<ClipId>),
ScrollFrame(ScrollFrameDisplayItem),
StickyFrame(StickyFrameDisplayItem),
Iframe(IframeDisplayItem),
PushReferenceFrame(ReferenceFrameDisplayListItem),
PushStackingContext(PushStackingContextDisplayItem),
@ -260,7 +265,7 @@ impl StickyOffsetBounds {
}
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
pub struct StickyFrameDisplayItem {
pub struct StickyFrameDescriptor {
pub id: SpatialId,
pub parent_spatial_id: SpatialId,
pub bounds: LayoutRect,
@ -297,7 +302,7 @@ pub enum ScrollSensitivity {
}
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
pub struct ScrollFrameDisplayItem {
pub struct ScrollFrameDescriptor {
/// The id of the space this scroll frame creates
pub scroll_frame_id: SpatialId,
/// The size of the contents this contains (so the backend knows how far it can scroll).
@ -732,6 +737,10 @@ pub struct BackdropFilterDisplayItem {
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
pub struct ReferenceFrameDisplayListItem {
}
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
pub struct ReferenceFrameDescriptor {
pub origin: LayoutPoint,
pub parent_spatial_id: SpatialId,
pub reference_frame: ReferenceFrame,
@ -1710,11 +1719,9 @@ impl DisplayItem {
DisplayItem::SetPoints => "set_points",
DisplayItem::RadialGradient(..) => "radial_gradient",
DisplayItem::Rectangle(..) => "rectangle",
DisplayItem::ScrollFrame(..) => "scroll_frame",
DisplayItem::SetGradientStops => "set_gradient_stops",
DisplayItem::ReuseItems(..) => "reuse_item",
DisplayItem::RetainedItems(..) => "retained_items",
DisplayItem::StickyFrame(..) => "sticky_frame",
DisplayItem::Text(..) => "text",
DisplayItem::YuvImage(..) => "yuv_image",
DisplayItem::BackdropFilter(..) => "backdrop_filter",

View File

@ -8,7 +8,7 @@ use peek_poke::{poke_inplace_slice, poke_into_vec, Poke};
#[cfg(feature = "deserialize")]
use serde::de::Deserializer;
#[cfg(feature = "serialize")]
use serde::ser::{Serializer, SerializeSeq};
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
use std::io::Write;
use std::marker::PhantomData;
@ -114,19 +114,35 @@ pub struct DisplayListPayload {
/// Serde encoded DisplayItemCache structs
pub cache_data: Vec<u8>,
/// Serde encoded SpatialTreeItem structs
pub spatial_tree: Vec<u8>,
}
impl DisplayListPayload {
fn size_in_bytes(&self) -> usize {
self.items_data.len() +
self.cache_data.len()
self.cache_data.len() +
self.spatial_tree.len()
}
#[cfg(feature = "serialize")]
fn create_debug_spatial_tree_items(&self) -> Vec<di::SpatialTreeItem> {
let mut items = Vec::new();
iter_spatial_tree(&self.spatial_tree, |item| {
items.push(*item);
});
items
}
}
impl MallocSizeOf for DisplayListPayload {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
self.items_data.size_of(ops) +
self.cache_data.size_of(ops)
self.cache_data.size_of(ops) +
self.spatial_tree.size_of(ops)
}
}
@ -174,7 +190,7 @@ pub struct BuiltDisplayListDescriptor {
#[derive(Clone)]
pub struct DisplayListWithCache {
display_list: BuiltDisplayList,
pub display_list: BuiltDisplayList,
cache: DisplayItemCache,
}
@ -217,13 +233,33 @@ impl MallocSizeOf for DisplayListWithCache {
}
}
/// A debug (human-readable) representation of a built display list that
/// can be used for capture and replay.
#[cfg(any(feature = "serialize", feature = "deserialize"))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
struct DisplayListCapture {
display_items: Vec<di::DebugDisplayItem>,
spatial_tree_items: Vec<di::SpatialTreeItem>,
descriptor: BuiltDisplayListDescriptor,
}
#[cfg(feature = "serialize")]
impl Serialize for DisplayListWithCache {
fn serialize<S: Serializer>(
&self,
serializer: S
) -> Result<S::Ok, S::Error> {
BuiltDisplayList::serialize_with_iterator(serializer, self.iter())
let display_items = BuiltDisplayList::create_debug_display_items(self.iter());
let spatial_tree_items = self.display_list.payload.create_debug_spatial_tree_items();
let dl = DisplayListCapture {
display_items,
spatial_tree_items,
descriptor: self.display_list.descriptor,
};
dl.serialize(serializer)
}
}
@ -233,20 +269,112 @@ impl<'de> Deserialize<'de> for DisplayListWithCache {
where
D: Deserializer<'de>,
{
let display_list = BuiltDisplayList::deserialize(deserializer)?;
let cache = DisplayItemCache::new();
use crate::display_item::DisplayItem as Real;
use crate::display_item::DebugDisplayItem as Debug;
let capture = DisplayListCapture::deserialize(deserializer)?;
let mut spatial_tree = Vec::new();
for item in capture.spatial_tree_items {
poke_into_vec(&item, &mut spatial_tree);
}
ensure_red_zone::<di::SpatialTreeItem>(&mut spatial_tree);
let mut items_data = Vec::new();
let mut temp = Vec::new();
for complete in capture.display_items {
let item = match complete {
Debug::ClipChain(v, clip_chain_ids) => {
DisplayListBuilder::push_iter_impl(&mut temp, clip_chain_ids);
Real::ClipChain(v)
}
Debug::Text(v, glyphs) => {
DisplayListBuilder::push_iter_impl(&mut temp, glyphs);
Real::Text(v)
},
Debug::Iframe(v) => {
Real::Iframe(v)
}
Debug::PushReferenceFrame(v) => {
Real::PushReferenceFrame(v)
}
Debug::SetFilterOps(filters) => {
DisplayListBuilder::push_iter_impl(&mut temp, filters);
Real::SetFilterOps
},
Debug::SetFilterData(filter_data) => {
let func_types: Vec<di::ComponentTransferFuncType> =
[filter_data.func_r_type,
filter_data.func_g_type,
filter_data.func_b_type,
filter_data.func_a_type].to_vec();
DisplayListBuilder::push_iter_impl(&mut temp, func_types);
DisplayListBuilder::push_iter_impl(&mut temp, filter_data.r_values);
DisplayListBuilder::push_iter_impl(&mut temp, filter_data.g_values);
DisplayListBuilder::push_iter_impl(&mut temp, filter_data.b_values);
DisplayListBuilder::push_iter_impl(&mut temp, filter_data.a_values);
Real::SetFilterData
},
Debug::SetFilterPrimitives(filter_primitives) => {
DisplayListBuilder::push_iter_impl(&mut temp, filter_primitives);
Real::SetFilterPrimitives
}
Debug::SetGradientStops(stops) => {
DisplayListBuilder::push_iter_impl(&mut temp, stops);
Real::SetGradientStops
},
Debug::SetPoints(points) => {
DisplayListBuilder::push_iter_impl(&mut temp, points);
Real::SetPoints
},
Debug::RectClip(v) => Real::RectClip(v),
Debug::RoundedRectClip(v) => Real::RoundedRectClip(v),
Debug::ImageMaskClip(v) => Real::ImageMaskClip(v),
Debug::Rectangle(v) => Real::Rectangle(v),
Debug::ClearRectangle(v) => Real::ClearRectangle(v),
Debug::HitTest(v) => Real::HitTest(v),
Debug::Line(v) => Real::Line(v),
Debug::Image(v) => Real::Image(v),
Debug::RepeatingImage(v) => Real::RepeatingImage(v),
Debug::YuvImage(v) => Real::YuvImage(v),
Debug::Border(v) => Real::Border(v),
Debug::BoxShadow(v) => Real::BoxShadow(v),
Debug::Gradient(v) => Real::Gradient(v),
Debug::RadialGradient(v) => Real::RadialGradient(v),
Debug::ConicGradient(v) => Real::ConicGradient(v),
Debug::PushStackingContext(v) => Real::PushStackingContext(v),
Debug::PushShadow(v) => Real::PushShadow(v),
Debug::BackdropFilter(v) => Real::BackdropFilter(v),
Debug::PopStackingContext => Real::PopStackingContext,
Debug::PopReferenceFrame => Real::PopReferenceFrame,
Debug::PopAllShadows => Real::PopAllShadows,
};
poke_into_vec(&item, &mut items_data);
// the aux data is serialized after the item, hence the temporary
items_data.extend(temp.drain(..));
}
// Add `DisplayItem::max_size` zone of zeroes to the end of display list
// so there is at least this amount available in the display list during
// serialization.
ensure_red_zone::<di::DisplayItem>(&mut items_data);
Ok(DisplayListWithCache {
display_list,
cache,
display_list: BuiltDisplayList {
descriptor: capture.descriptor,
payload: DisplayListPayload {
cache_data: Vec::new(),
items_data,
spatial_tree,
},
},
cache: DisplayItemCache::new(),
})
}
}
impl BuiltDisplayListDescriptor {}
pub struct BuiltDisplayListIter<'a> {
list: &'a BuiltDisplayList,
data: &'a [u8],
cache: Option<&'a DisplayItemCache>,
pending_items: std::slice::Iter<'a, CachedDisplayItem>,
@ -335,10 +463,6 @@ pub struct DisplayItemRef<'a: 'b, 'b> {
// Some of these might just become ItemRanges
impl<'a, 'b> DisplayItemRef<'a, 'b> {
pub fn display_list(&self) -> &BuiltDisplayList {
self.iter.display_list()
}
// Creates a new iterator where this element's iterator is, to hack around borrowck.
pub fn sub_iter(&self) -> BuiltDisplayListIter<'a> {
self.iter.sub_iter()
@ -448,18 +572,18 @@ impl BuiltDisplayList {
}
pub fn iter(&self) -> BuiltDisplayListIter {
BuiltDisplayListIter::new(self, self.items_data(), None)
BuiltDisplayListIter::new(self.items_data(), None)
}
pub fn cache_data_iter(&self) -> BuiltDisplayListIter {
BuiltDisplayListIter::new(self, self.cache_data(), None)
BuiltDisplayListIter::new(self.cache_data(), None)
}
pub fn iter_with_cache<'a>(
&'a self,
cache: &'a DisplayItemCache
) -> BuiltDisplayListIter<'a> {
BuiltDisplayListIter::new(self, self.items_data(), Some(cache))
BuiltDisplayListIter::new(self.items_data(), Some(cache))
}
pub fn cache_size(&self) -> usize {
@ -470,15 +594,17 @@ impl BuiltDisplayList {
self.payload.size_in_bytes()
}
#[cfg(feature = "serialize")]
pub fn serialize_with_iterator<S: Serializer>(
serializer: S,
mut iterator: BuiltDisplayListIter,
) -> Result<S::Ok, S::Error> {
use crate::display_item::DisplayItem as Real;
use crate::display_item::DebugDisplayItem as Debug;
pub fn iter_spatial_tree<F>(&self, f: F) where F: FnMut(&di::SpatialTreeItem) {
iter_spatial_tree(&self.payload.spatial_tree, f)
}
let mut seq = serializer.serialize_seq(None)?;
#[cfg(feature = "serialize")]
pub fn create_debug_display_items(
mut iterator: BuiltDisplayListIter,
) -> Vec<di::DebugDisplayItem> {
use di::DisplayItem as Real;
use di::DebugDisplayItem as Debug;
let mut debug_items = Vec::new();
while let Some(item) = iterator.next_raw() {
let serial_di = match *item.item() {
@ -486,7 +612,6 @@ impl BuiltDisplayList {
v,
item.iter.cur_clip_chain_items.iter().collect()
),
Real::ScrollFrame(v) => Debug::ScrollFrame(v),
Real::Text(v) => Debug::Text(
v,
item.iter.cur_glyphs.iter().collect()
@ -526,7 +651,6 @@ impl BuiltDisplayList {
Real::RectClip(v) => Debug::RectClip(v),
Real::RoundedRectClip(v) => Debug::RoundedRectClip(v),
Real::ImageMaskClip(v) => Debug::ImageMaskClip(v),
Real::StickyFrame(v) => Debug::StickyFrame(v),
Real::Rectangle(v) => Debug::Rectangle(v),
Real::ClearRectangle(v) => Debug::ClearRectangle(v),
Real::HitTest(v) => Debug::HitTest(v),
@ -551,9 +675,10 @@ impl BuiltDisplayList {
Real::ReuseItems(_) |
Real::RetainedItems(_) => unreachable!("Unexpected item"),
};
seq.serialize_element(&serial_di)?
debug_items.push(serial_di);
}
seq.end()
debug_items
}
}
@ -574,12 +699,10 @@ fn skip_slice<'a, T: peek_poke::Peek>(data: &mut &'a [u8]) -> ItemRange<'a, T> {
impl<'a> BuiltDisplayListIter<'a> {
pub fn new(
list: &'a BuiltDisplayList,
data: &'a [u8],
cache: Option<&'a DisplayItemCache>,
) -> Self {
Self {
list,
data,
cache,
pending_items: [].iter(),
@ -602,16 +725,12 @@ impl<'a> BuiltDisplayListIter<'a> {
pub fn sub_iter(&self) -> Self {
let mut iter = BuiltDisplayListIter::new(
self.list, self.data, self.cache
self.data, self.cache
);
iter.pending_items = self.pending_items.clone();
iter
}
pub fn display_list(&self) -> &'a BuiltDisplayList {
self.list
}
pub fn current_item(&self) -> &di::DisplayItem {
match self.cur_cached_item {
Some(cached_item) => cached_item.display_item(),
@ -861,141 +980,6 @@ impl<'a, T: Copy + peek_poke::Peek> Iterator for AuxIter<'a, T> {
impl<'a, T: Copy + peek_poke::Peek> ::std::iter::ExactSizeIterator for AuxIter<'a, T> {}
#[cfg(feature = "serialize")]
impl Serialize for BuiltDisplayList {
fn serialize<S: Serializer>(
&self,
serializer: S
) -> Result<S::Ok, S::Error> {
Self::serialize_with_iterator(serializer, self.iter())
}
}
// The purpose of this implementation is to deserialize
// a display list from one format just to immediately
// serialize then into a "built" `Vec<u8>`.
#[cfg(feature = "deserialize")]
impl<'de> Deserialize<'de> for BuiltDisplayList {
fn deserialize<D: Deserializer<'de>>(
deserializer: D
) -> Result<Self, D::Error> {
use crate::display_item::DisplayItem as Real;
use crate::display_item::DebugDisplayItem as Debug;
let list = Vec::<Debug>::deserialize(deserializer)?;
let mut items_data = Vec::new();
let mut temp = Vec::new();
let mut total_clip_nodes = FIRST_CLIP_NODE_INDEX;
let mut total_spatial_nodes = FIRST_SPATIAL_NODE_INDEX;
for complete in list {
let item = match complete {
Debug::ClipChain(v, clip_chain_ids) => {
DisplayListBuilder::push_iter_impl(&mut temp, clip_chain_ids);
Real::ClipChain(v)
}
Debug::ScrollFrame(v) => {
total_spatial_nodes += 1;
total_clip_nodes += 1;
Real::ScrollFrame(v)
}
Debug::StickyFrame(v) => {
total_spatial_nodes += 1;
Real::StickyFrame(v)
}
Debug::Text(v, glyphs) => {
DisplayListBuilder::push_iter_impl(&mut temp, glyphs);
Real::Text(v)
},
Debug::Iframe(v) => {
total_clip_nodes += 1;
Real::Iframe(v)
}
Debug::PushReferenceFrame(v) => {
total_spatial_nodes += 1;
Real::PushReferenceFrame(v)
}
Debug::SetFilterOps(filters) => {
DisplayListBuilder::push_iter_impl(&mut temp, filters);
Real::SetFilterOps
},
Debug::SetFilterData(filter_data) => {
let func_types: Vec<di::ComponentTransferFuncType> =
[filter_data.func_r_type,
filter_data.func_g_type,
filter_data.func_b_type,
filter_data.func_a_type].to_vec();
DisplayListBuilder::push_iter_impl(&mut temp, func_types);
DisplayListBuilder::push_iter_impl(&mut temp, filter_data.r_values);
DisplayListBuilder::push_iter_impl(&mut temp, filter_data.g_values);
DisplayListBuilder::push_iter_impl(&mut temp, filter_data.b_values);
DisplayListBuilder::push_iter_impl(&mut temp, filter_data.a_values);
Real::SetFilterData
},
Debug::SetFilterPrimitives(filter_primitives) => {
DisplayListBuilder::push_iter_impl(&mut temp, filter_primitives);
Real::SetFilterPrimitives
}
Debug::SetGradientStops(stops) => {
DisplayListBuilder::push_iter_impl(&mut temp, stops);
Real::SetGradientStops
},
Debug::SetPoints(points) => {
DisplayListBuilder::push_iter_impl(&mut temp, points);
Real::SetPoints
},
Debug::RectClip(v) => Real::RectClip(v),
Debug::RoundedRectClip(v) => Real::RoundedRectClip(v),
Debug::ImageMaskClip(v) => Real::ImageMaskClip(v),
Debug::Rectangle(v) => Real::Rectangle(v),
Debug::ClearRectangle(v) => Real::ClearRectangle(v),
Debug::HitTest(v) => Real::HitTest(v),
Debug::Line(v) => Real::Line(v),
Debug::Image(v) => Real::Image(v),
Debug::RepeatingImage(v) => Real::RepeatingImage(v),
Debug::YuvImage(v) => Real::YuvImage(v),
Debug::Border(v) => Real::Border(v),
Debug::BoxShadow(v) => Real::BoxShadow(v),
Debug::Gradient(v) => Real::Gradient(v),
Debug::RadialGradient(v) => Real::RadialGradient(v),
Debug::ConicGradient(v) => Real::ConicGradient(v),
Debug::PushStackingContext(v) => Real::PushStackingContext(v),
Debug::PushShadow(v) => Real::PushShadow(v),
Debug::BackdropFilter(v) => Real::BackdropFilter(v),
Debug::PopStackingContext => Real::PopStackingContext,
Debug::PopReferenceFrame => Real::PopReferenceFrame,
Debug::PopAllShadows => Real::PopAllShadows,
};
poke_into_vec(&item, &mut items_data);
// the aux data is serialized after the item, hence the temporary
items_data.extend(temp.drain(..));
}
// Add `DisplayItem::max_size` zone of zeroes to the end of display list
// so there is at least this amount available in the display list during
// serialization.
ensure_red_zone::<di::DisplayItem>(&mut items_data);
Ok(BuiltDisplayList {
payload: DisplayListPayload {
items_data,
cache_data: Vec::new(),
},
descriptor: BuiltDisplayListDescriptor {
gecko_display_list_type: GeckoDisplayListType::None,
builder_start_time: 0,
builder_finish_time: 1,
send_start_time: 1,
total_clip_nodes,
total_spatial_nodes,
cache_size: 0,
},
})
}
}
#[derive(Clone, Debug)]
pub struct SaveState {
dl_items_len: usize,
@ -1016,6 +1000,23 @@ pub enum DisplayListSection {
Chunk,
}
/// A small portion of a normal spatial node that we store during DL construction to
/// enable snapping and reference frame <-> stacking context coord mapping. In future
/// we'll aim to remove this and have the full spatial tree available during DL build.
#[derive(Clone)]
pub struct SpatialNodeInfo {
/// The total external scroll offset applicable at this node
accumulated_external_scroll_offset: LayoutVector2D,
}
impl SpatialNodeInfo {
fn identity() -> Self {
SpatialNodeInfo {
accumulated_external_scroll_offset: LayoutVector2D::zero(),
}
}
}
#[derive(Clone)]
pub struct DisplayListBuilder {
payload: DisplayListPayload,
@ -1033,12 +1034,19 @@ pub struct DisplayListBuilder {
cache_size: usize,
serialized_content_buffer: Option<String>,
/// Helper struct to map stacking context coords <-> reference frame coords.
rf_mapper: ReferenceFrameMapper,
/// Minimal info about encountered spatial nodes to allow snapping during DL building
spatial_nodes: Vec<SpatialNodeInfo>,
}
#[repr(C)]
pub struct DisplayListCapacity {
items_size: usize,
cache_size: usize,
spatial_tree_size: usize,
}
impl DisplayListCapacity {
@ -1046,6 +1054,7 @@ impl DisplayListCapacity {
DisplayListCapacity {
items_size: 0,
cache_size: 0,
spatial_tree_size: 0,
}
}
}
@ -1065,6 +1074,7 @@ impl DisplayListBuilder {
payload: DisplayListPayload {
items_data: Vec::with_capacity(capacity.items_size),
cache_data: Vec::with_capacity(capacity.cache_size),
spatial_tree: Vec::with_capacity(capacity.spatial_tree_size),
},
pipeline_id,
@ -1078,6 +1088,9 @@ impl DisplayListBuilder {
save_state: None,
cache_size: 0,
serialized_content_buffer: None,
rf_mapper: ReferenceFrameMapper::new(),
spatial_nodes: vec![SpatialNodeInfo::identity(); FIRST_SPATIAL_NODE_INDEX + 1],
}
}
@ -1213,6 +1226,11 @@ impl DisplayListBuilder {
self.push_item_to_section(item, self.default_section());
}
#[inline]
pub fn push_spatial_tree_item(&mut self, item: &di::SpatialTreeItem) {
poke_into_vec(item, &mut self.payload.spatial_tree);
}
fn push_iter_impl<I>(data: &mut Vec<u8>, iter_source: I)
where
I: IntoIterator,
@ -1599,7 +1617,15 @@ impl DisplayListBuilder {
) -> di::SpatialId {
let id = self.generate_spatial_index();
let item = di::DisplayItem::PushReferenceFrame(di::ReferenceFrameDisplayListItem {
let current_offset = self.current_offset(parent_spatial_id);
let origin = origin + current_offset;
self.add_spatial_node_info(
id,
LayoutVector2D::zero(),
);
let descriptor = di::SpatialTreeItem::ReferenceFrame(di::ReferenceFrameDescriptor {
parent_spatial_id,
origin,
reference_frame: di::ReferenceFrame {
@ -1611,8 +1637,14 @@ impl DisplayListBuilder {
id,
},
});
self.push_spatial_tree_item(&descriptor);
self.rf_mapper.push_scope();
let item = di::DisplayItem::PushReferenceFrame(di::ReferenceFrameDisplayListItem {
});
self.push_item(&item);
id
}
@ -1626,7 +1658,10 @@ impl DisplayListBuilder {
) -> di::SpatialId {
let id = self.generate_spatial_index();
let item = di::DisplayItem::PushReferenceFrame(di::ReferenceFrameDisplayListItem {
let current_offset = self.current_offset(parent_spatial_id);
let origin = origin + current_offset;
let descriptor = di::SpatialTreeItem::ReferenceFrame(di::ReferenceFrameDescriptor {
parent_spatial_id,
origin,
reference_frame: di::ReferenceFrame {
@ -1643,12 +1678,19 @@ impl DisplayListBuilder {
id,
},
});
self.push_spatial_tree_item(&descriptor);
self.rf_mapper.push_scope();
let item = di::DisplayItem::PushReferenceFrame(di::ReferenceFrameDisplayListItem {
});
self.push_item(&item);
id
}
pub fn pop_reference_frame(&mut self) {
self.rf_mapper.pop_scope();
self.push_item(&di::DisplayItem::PopReferenceFrame);
}
@ -1681,6 +1723,7 @@ impl DisplayListBuilder {
},
});
self.rf_mapper.push_offset(origin.to_vector());
self.push_item(&item);
}
@ -1727,6 +1770,7 @@ impl DisplayListBuilder {
}
pub fn pop_stacking_context(&mut self) {
self.rf_mapper.pop_offset();
self.push_item(&di::DisplayItem::PopStackingContext);
}
@ -1807,7 +1851,15 @@ impl DisplayListBuilder {
external_scroll_offset: LayoutVector2D,
) -> di::SpatialId {
let scroll_frame_id = self.generate_spatial_index();
let item = di::DisplayItem::ScrollFrame(di::ScrollFrameDisplayItem {
let parent = self.spatial_nodes[parent_space.0].clone();
self.add_spatial_node_info(
scroll_frame_id,
parent.accumulated_external_scroll_offset + external_scroll_offset,
);
let descriptor = di::SpatialTreeItem::ScrollFrame(di::ScrollFrameDescriptor {
content_rect,
frame_rect,
parent_space,
@ -1817,7 +1869,7 @@ impl DisplayListBuilder {
external_scroll_offset,
});
self.push_item(&item);
self.push_spatial_tree_item(&descriptor);
scroll_frame_id
}
@ -1906,7 +1958,14 @@ impl DisplayListBuilder {
previously_applied_offset: LayoutVector2D,
) -> di::SpatialId {
let id = self.generate_spatial_index();
let item = di::DisplayItem::StickyFrame(di::StickyFrameDisplayItem {
let parent = self.spatial_nodes[parent_spatial_id.0].clone();
self.add_spatial_node_info(
id,
parent.accumulated_external_scroll_offset,
);
let descriptor = di::SpatialTreeItem::StickyFrame(di::StickyFrameDescriptor {
parent_spatial_id,
id,
bounds: frame_rect,
@ -1916,7 +1975,7 @@ impl DisplayListBuilder {
previously_applied_offset,
});
self.push_item(&item);
self.push_spatial_tree_item(&descriptor);
id
}
@ -2029,6 +2088,7 @@ impl DisplayListBuilder {
// serialization.
ensure_red_zone::<di::DisplayItem>(&mut self.payload.items_data);
ensure_red_zone::<di::DisplayItem>(&mut self.payload.cache_data);
ensure_red_zone::<di::SpatialTreeItem>(&mut self.payload.spatial_tree);
let end_time = precise_time_ns();
(
@ -2047,4 +2107,117 @@ impl DisplayListBuilder {
},
)
}
/// Retrieve the current offset to allow converting a stacking context
/// relative coordinate to be relative to the owing reference frame,
/// also considering any external scroll offset on the provided
/// spatial node.
fn current_offset(
&mut self,
spatial_id: di::SpatialId,
) -> LayoutVector2D {
// Get the current offset from stacking context <-> reference frame space.
let rf_offset = self.rf_mapper.current_offset();
// Get the external scroll offset, if applicable.
let scroll_offset = self.spatial_nodes[spatial_id.0].accumulated_external_scroll_offset;
rf_offset + scroll_offset
}
/// Add info about a spatial node that is needed during DL building.
fn add_spatial_node_info(
&mut self,
id: di::SpatialId,
accumulated_external_scroll_offset: LayoutVector2D,
) {
self.spatial_nodes.resize(id.0 + 1, SpatialNodeInfo::identity());
let info = &mut self.spatial_nodes[id.0];
info.accumulated_external_scroll_offset = accumulated_external_scroll_offset;
}
}
fn iter_spatial_tree<F>(spatial_tree: &[u8], mut f: F) where F: FnMut(&di::SpatialTreeItem) {
let mut src = spatial_tree;
let mut item = di::SpatialTreeItem::Invalid;
while src.len() > di::SpatialTreeItem::max_size() {
src = peek_from_slice(src, &mut item);
f(&item);
}
}
/// The offset stack for a given reference frame.
#[derive(Clone)]
struct ReferenceFrameState {
/// A stack of current offsets from the current reference frame scope.
offsets: Vec<LayoutVector2D>,
}
/// Maps from stacking context layout coordinates into reference frame
/// relative coordinates.
#[derive(Clone)]
pub struct ReferenceFrameMapper {
/// A stack of reference frame scopes.
frames: Vec<ReferenceFrameState>,
}
impl ReferenceFrameMapper {
pub fn new() -> Self {
ReferenceFrameMapper {
frames: vec![
ReferenceFrameState {
offsets: vec![
LayoutVector2D::zero(),
],
}
],
}
}
/// Push a new scope. This resets the current offset to zero, and is
/// used when a new reference frame or iframe is pushed.
pub fn push_scope(&mut self) {
self.frames.push(ReferenceFrameState {
offsets: vec![
LayoutVector2D::zero(),
],
});
}
/// Pop a reference frame scope off the stack.
pub fn pop_scope(&mut self) {
self.frames.pop().unwrap();
}
/// Push a new offset for the current scope. This is used when
/// a new stacking context is pushed.
pub fn push_offset(&mut self, offset: LayoutVector2D) {
let frame = self.frames.last_mut().unwrap();
let current_offset = *frame.offsets.last().unwrap();
frame.offsets.push(current_offset + offset);
}
/// Pop a local stacking context offset from the current scope.
pub fn pop_offset(&mut self) {
let frame = self.frames.last_mut().unwrap();
frame.offsets.pop().unwrap();
}
/// Retrieve the current offset to allow converting a stacking context
/// relative coordinate to be relative to the owing reference frame.
/// TODO(gw): We could perhaps have separate coordinate spaces for this,
/// however that's going to either mean a lot of changes to
/// public API code, or a lot of changes to internal code.
/// Before doing that, we should revisit how Gecko would
/// prefer to provide coordinates.
/// TODO(gw): For now, this includes only the reference frame relative
/// offset. Soon, we will expand this to include the initial
/// scroll offsets that are now available on scroll nodes. This
/// will allow normalizing the coordinates even between display
/// lists where APZ has scrolled the content.
pub fn current_offset(&self) -> LayoutVector2D {
*self.frames.last().unwrap().offsets.last().unwrap()
}
}

View File

@ -89,7 +89,7 @@ fuzzy(0-20,0-999) fails-if(OSX>=1008&&!skiaContent) != downscale-2e.html?205,53,
# Skip on Android because it runs reftests via http, and moz-icon isn't
# accessible from http/https origins anymore.
fuzzy(0-17,0-3940) fuzzy-if(gtkWidget&&!webrender,4-4,2616-2616) fuzzy-if(gtkWidget&&!webrender&&!layersGPUAccelerated,0-0,0-0) fuzzy-if(gtkWidget&&webrender,0-0,0-0) skip-if(Android) fuzzy-if(appleSilicon,0-20,0-10123) == downscale-moz-icon-1.html downscale-moz-icon-1-ref.html # gtkWidget Bug 1592059: regular is 2616, no-accel is 0, qr passes with 0
fuzzy(0-25,0-3940) fuzzy-if(gtkWidget&&!webrender,4-4,2616-2616) fuzzy-if(gtkWidget&&!webrender&&!layersGPUAccelerated,0-0,0-0) fuzzy-if(gtkWidget&&webrender,0-0,0-0) skip-if(Android) fuzzy-if(appleSilicon,0-20,0-10123) == downscale-moz-icon-1.html downscale-moz-icon-1-ref.html # gtkWidget Bug 1592059: regular is 2616, no-accel is 0, qr passes with 0
== downscale-png.html?16,16,interlaced downscale-png.html?16,16,normal
== downscale-png.html?24,24,interlaced downscale-png.html?24,24,normal

View File

@ -1,3 +1,3 @@
[devicepixel.html]
fuzzy: maxDifference=0-2;totalPixels=0-1391
fuzzy: maxDifference=0-52;totalPixels=0-1100
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1723618