Bug 1623792 - Pt 8 - Add IS_IDENTITY to spatial node flags. r=jnicol

Add a new flag for spatial nodes that identifies if the spatial
node is guaranteed to be identity (no transformation at all) for
the entirety of the scene (i.e. either a fixed identity reference
frame or a redundant scroll frame).

Although not used yet, this will be useful in future to help
remove clips during scene building that can't possibly affect
the content rendering no matter what occurs during frame building.

At the same time, port the other bool fields in spatial node into
a single bitflags.

Differential Revision: https://phabricator.services.mozilla.com/D84957
This commit is contained in:
Glenn Watson 2020-07-27 09:56:59 +00:00
Родитель 90071d7b7c
Коммит bfe5060361
5 изменённых файлов: 114 добавлений и 34 удалений

Просмотреть файл

@ -530,7 +530,7 @@ impl FrameBuilder {
let spatial_node = &scene let spatial_node = &scene
.spatial_tree .spatial_tree
.spatial_nodes[spatial_node_index.0 as usize]; .spatial_nodes[spatial_node_index.0 as usize];
spatial_node.is_ancestor_or_self_zooming spatial_node.is_ancestor_or_self_zooming()
}); });
let mut composite_state = CompositeState::new( let mut composite_state = CompositeState::new(

Просмотреть файл

@ -4677,7 +4677,7 @@ impl PicturePrimitive {
/// change in zoom level, as that would be too expensive. /// change in zoom level, as that would be too expensive.
pub fn get_raster_space(&self, spatial_tree: &SpatialTree) -> RasterSpace { pub fn get_raster_space(&self, spatial_tree: &SpatialTree) -> RasterSpace {
let spatial_node = &spatial_tree.spatial_nodes[self.spatial_node_index.0 as usize]; let spatial_node = &spatial_tree.spatial_nodes[self.spatial_node_index.0 as usize];
if spatial_node.is_ancestor_or_self_zooming { if spatial_node.is_ancestor_or_self_zooming() {
let scale_factors = spatial_tree let scale_factors = spatial_tree
.get_relative_transform(self.spatial_node_index, ROOT_SPATIAL_NODE_INDEX) .get_relative_transform(self.spatial_node_index, ROOT_SPATIAL_NODE_INDEX)
.scale_factors(); .scale_factors();
@ -6025,7 +6025,7 @@ impl PicturePrimitive {
let spatial_node = &frame_context let spatial_node = &frame_context
.spatial_tree .spatial_tree
.spatial_nodes[cluster.spatial_node_index.0 as usize]; .spatial_nodes[cluster.spatial_node_index.0 as usize];
if !spatial_node.invertible { if !spatial_node.is_invertible() {
continue; continue;
} }

Просмотреть файл

@ -616,8 +616,8 @@ impl Document {
let node = self.scene.spatial_tree.spatial_nodes.iter_mut() let node = self.scene.spatial_tree.spatial_nodes.iter_mut()
.find(|node| node.is_transform_bound_to_property(animation_id)); .find(|node| node.is_transform_bound_to_property(animation_id));
if let Some(node) = node { if let Some(node) = node {
if node.is_async_zooming != is_zooming { if node.is_async_zooming() != is_zooming {
node.is_async_zooming = is_zooming; node.set_async_zooming(is_zooming);
self.frame_is_valid = false; self.frame_is_valid = false;
} }
} }

Просмотреть файл

@ -27,6 +27,29 @@ pub enum SpatialNodeType {
ReferenceFrame(ReferenceFrameInfo), ReferenceFrame(ReferenceFrameInfo),
} }
bitflags! {
/// Bits of pre-calculated information about a given spatial node.
pub struct SpatialNodeFlags : u32 {
/// True if this node is transformed by an invertible transform. If not, display items
/// transformed by this node will not be displayed and display items not transformed by this
/// node will not be clipped by clips that are transformed by this node.
const IS_INVERTIBLE = 1;
/// Whether this specific node is currently being async zoomed.
/// Should be set when a SetIsTransformAsyncZooming FrameMsg is received.
const IS_ASYNC_ZOOMING = 2;
/// Whether this node or any of its ancestors is being pinch zoomed.
/// This is calculated in update(). This will be used to decide whether
/// to override corresponding picture's raster space as an optimisation.
const IS_ANCESTOR_OR_SELF_ZOOMING = 4;
/// If true, this spatial node and all parents are guaranteed to never introduce
/// a transformation. Effectively means that this node implies local space == world space.
const IS_IDENTITY = 8;
}
}
/// Contains information common among all types of SpatialTree nodes. /// Contains information common among all types of SpatialTree nodes.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SpatialNode { pub struct SpatialNode {
@ -60,19 +83,8 @@ pub struct SpatialNode {
/// The type of this node and any data associated with that node type. /// The type of this node and any data associated with that node type.
pub node_type: SpatialNodeType, pub node_type: SpatialNodeType,
/// True if this node is transformed by an invertible transform. If not, display items /// Various flags related to the current state of this spatial node.
/// transformed by this node will not be displayed and display items not transformed by this pub flags: SpatialNodeFlags,
/// node will not be clipped by clips that are transformed by this node.
pub invertible: bool,
/// Whether this specific node is currently being async zoomed.
/// Should be set when a SetIsTransformAsyncZooming FrameMsg is received.
pub is_async_zooming: bool,
/// Whether this node or any of its ancestors is being pinch zoomed.
/// This is calculated in update(). This will be used to decide whether
/// to override corresponding picture's raster space as an optimisation.
pub is_ancestor_or_self_zooming: bool,
} }
fn compute_offset_from( fn compute_offset_from(
@ -132,7 +144,11 @@ impl SpatialNode {
pipeline_id: PipelineId, pipeline_id: PipelineId,
parent_index: Option<SpatialNodeIndex>, parent_index: Option<SpatialNodeIndex>,
node_type: SpatialNodeType, node_type: SpatialNodeType,
is_identity: bool,
) -> Self { ) -> Self {
let mut flags = SpatialNodeFlags::IS_INVERTIBLE;
flags.set(SpatialNodeFlags::IS_IDENTITY, is_identity);
SpatialNode { SpatialNode {
viewport_transform: ScaleOffset::identity(), viewport_transform: ScaleOffset::identity(),
content_transform: ScaleOffset::identity(), content_transform: ScaleOffset::identity(),
@ -143,12 +159,34 @@ impl SpatialNode {
children: Vec::new(), children: Vec::new(),
pipeline_id, pipeline_id,
node_type, node_type,
invertible: true, flags,
is_async_zooming: false,
is_ancestor_or_self_zooming: false,
} }
} }
pub fn is_invertible(&self) -> bool {
self.flags.contains(SpatialNodeFlags::IS_INVERTIBLE)
}
pub fn is_async_zooming(&self) -> bool {
self.flags.contains(SpatialNodeFlags::IS_ASYNC_ZOOMING)
}
pub fn set_async_zooming(&mut self, value: bool) {
self.flags.set(SpatialNodeFlags::IS_ASYNC_ZOOMING, value);
}
pub fn is_ancestor_or_self_zooming(&self) -> bool {
self.flags.contains(SpatialNodeFlags::IS_ANCESTOR_OR_SELF_ZOOMING)
}
pub fn set_is_ancestor_or_self_zooming(&mut self, value: bool) {
self.flags.set(SpatialNodeFlags::IS_ANCESTOR_OR_SELF_ZOOMING, value);
}
pub fn is_identity(&self) -> bool {
self.flags.contains(SpatialNodeFlags::IS_IDENTITY)
}
pub fn new_scroll_frame( pub fn new_scroll_frame(
pipeline_id: PipelineId, pipeline_id: PipelineId,
parent_index: SpatialNodeIndex, parent_index: SpatialNodeIndex,
@ -158,21 +196,34 @@ impl SpatialNode {
scroll_sensitivity: ScrollSensitivity, scroll_sensitivity: ScrollSensitivity,
frame_kind: ScrollFrameKind, frame_kind: ScrollFrameKind,
external_scroll_offset: LayoutVector2D, external_scroll_offset: LayoutVector2D,
parent_is_identity: bool,
) -> Self { ) -> Self {
let scrollable_size = LayoutSize::new(
(content_size.width - frame_rect.size.width).max(0.0),
(content_size.height - frame_rect.size.height).max(0.0)
);
// TODO(gw): We might want to consider an epsilon check here, but we
// generally only care about the root pipeline scroll frames,
// which are fixed as zero.
let is_identity = scrollable_size == LayoutSize::zero();
let node_type = SpatialNodeType::ScrollFrame(ScrollFrameInfo::new( let node_type = SpatialNodeType::ScrollFrame(ScrollFrameInfo::new(
*frame_rect, *frame_rect,
scroll_sensitivity, scroll_sensitivity,
LayoutSize::new( scrollable_size,
(content_size.width - frame_rect.size.width).max(0.0),
(content_size.height - frame_rect.size.height).max(0.0)
),
external_id, external_id,
frame_kind, frame_kind,
external_scroll_offset, external_scroll_offset,
) )
); );
Self::new(pipeline_id, Some(parent_index), node_type) Self::new(
pipeline_id,
Some(parent_index),
node_type,
parent_is_identity && is_identity,
)
} }
pub fn new_reference_frame( pub fn new_reference_frame(
@ -182,7 +233,19 @@ impl SpatialNode {
kind: ReferenceFrameKind, kind: ReferenceFrameKind,
origin_in_parent_reference_frame: LayoutVector2D, origin_in_parent_reference_frame: LayoutVector2D,
pipeline_id: PipelineId, pipeline_id: PipelineId,
parent_is_identity: bool,
) -> Self { ) -> Self {
let is_identity = match source_transform {
PropertyBinding::Value(m) => {
// TODO(gw): We might want to consider an epsilon check here, but we
// generally only care about the root pipeline reference frames,
// which are fixed as zero.
m == LayoutTransform::identity()
}
PropertyBinding::Binding(..) => {
false
}
};
let info = ReferenceFrameInfo { let info = ReferenceFrameInfo {
transform_style, transform_style,
source_transform, source_transform,
@ -190,7 +253,12 @@ impl SpatialNode {
origin_in_parent_reference_frame, origin_in_parent_reference_frame,
invertible: true, invertible: true,
}; };
Self::new(pipeline_id, parent_index, SpatialNodeType::ReferenceFrame(info)) Self::new(
pipeline_id,
parent_index,
SpatialNodeType::ReferenceFrame(info),
parent_is_identity && is_identity,
)
} }
pub fn new_sticky_frame( pub fn new_sticky_frame(
@ -198,7 +266,12 @@ impl SpatialNode {
sticky_frame_info: StickyFrameInfo, sticky_frame_info: StickyFrameInfo,
pipeline_id: PipelineId, pipeline_id: PipelineId,
) -> Self { ) -> Self {
Self::new(pipeline_id, Some(parent_index), SpatialNodeType::StickyFrame(sticky_frame_info)) Self::new(
pipeline_id,
Some(parent_index),
SpatialNodeType::StickyFrame(sticky_frame_info),
false,
)
} }
pub fn add_child(&mut self, child: SpatialNodeIndex) { pub fn add_child(&mut self, child: SpatialNodeIndex) {
@ -259,7 +332,7 @@ impl SpatialNode {
&mut self, &mut self,
state: &TransformUpdateState, state: &TransformUpdateState,
) { ) {
self.invertible = false; self.flags.remove(SpatialNodeFlags::IS_INVERTIBLE);
self.viewport_transform = ScaleOffset::identity(); self.viewport_transform = ScaleOffset::identity();
self.content_transform = ScaleOffset::identity(); self.content_transform = ScaleOffset::identity();
self.coordinate_system_id = state.current_coordinate_system_id; self.coordinate_system_id = state.current_coordinate_system_id;
@ -289,10 +362,10 @@ impl SpatialNode {
}; };
let is_parent_zooming = match self.parent { let is_parent_zooming = match self.parent {
Some(parent) => previous_spatial_nodes[parent.0 as usize].is_ancestor_or_self_zooming, Some(parent) => previous_spatial_nodes[parent.0 as usize].is_ancestor_or_self_zooming(),
_ => false, _ => false,
}; };
self.is_ancestor_or_self_zooming = self.is_async_zooming | is_parent_zooming; self.set_is_ancestor_or_self_zooming(self.is_async_zooming() || is_parent_zooming);
// If this node is a reference frame, we check if it has a non-invertible matrix. // If this node is a reference frame, we check if it has a non-invertible matrix.
// For non-reference-frames we assume that they will produce only additional // For non-reference-frames we assume that they will produce only additional
@ -301,7 +374,7 @@ impl SpatialNode {
SpatialNodeType::ReferenceFrame(info) if !info.invertible => { SpatialNodeType::ReferenceFrame(info) if !info.invertible => {
self.mark_uninvertible(state); self.mark_uninvertible(state);
} }
_ => self.invertible = true, _ => self.flags.insert(SpatialNodeFlags::IS_INVERTIBLE),
} }
} }
@ -425,7 +498,7 @@ impl SpatialNode {
self.coordinate_system_id = state.current_coordinate_system_id; self.coordinate_system_id = state.current_coordinate_system_id;
self.viewport_transform = cs_scale_offset; self.viewport_transform = cs_scale_offset;
self.content_transform = cs_scale_offset; self.content_transform = cs_scale_offset;
self.invertible = info.invertible; self.flags.set(SpatialNodeFlags::IS_INVERTIBLE, info.invertible);
} }
_ => { _ => {
// We calculate this here to avoid a double-borrow later. // We calculate this here to avoid a double-borrow later.
@ -572,7 +645,7 @@ impl SpatialNode {
} }
pub fn prepare_state_for_children(&self, state: &mut TransformUpdateState) { pub fn prepare_state_for_children(&self, state: &mut TransformUpdateState) {
if !self.invertible { if !self.is_invertible() {
state.invertible = false; state.invertible = false;
return; return;
} }

Просмотреть файл

@ -558,6 +558,8 @@ impl SpatialTree {
frame_kind: ScrollFrameKind, frame_kind: ScrollFrameKind,
external_scroll_offset: LayoutVector2D, external_scroll_offset: LayoutVector2D,
) -> SpatialNodeIndex { ) -> SpatialNodeIndex {
let parent_is_identity = self.spatial_nodes[parent_index.0 as usize].is_identity();
let node = SpatialNode::new_scroll_frame( let node = SpatialNode::new_scroll_frame(
pipeline_id, pipeline_id,
parent_index, parent_index,
@ -567,6 +569,7 @@ impl SpatialTree {
scroll_sensitivity, scroll_sensitivity,
frame_kind, frame_kind,
external_scroll_offset, external_scroll_offset,
parent_is_identity,
); );
self.add_spatial_node(node) self.add_spatial_node(node)
} }
@ -580,6 +583,9 @@ impl SpatialTree {
origin_in_parent_reference_frame: LayoutVector2D, origin_in_parent_reference_frame: LayoutVector2D,
pipeline_id: PipelineId, pipeline_id: PipelineId,
) -> SpatialNodeIndex { ) -> SpatialNodeIndex {
let parent_is_identity = parent_index.map_or(true, |parent_index| {
self.spatial_nodes[parent_index.0 as usize].is_identity()
});
let node = SpatialNode::new_reference_frame( let node = SpatialNode::new_reference_frame(
parent_index, parent_index,
transform_style, transform_style,
@ -587,6 +593,7 @@ impl SpatialTree {
kind, kind,
origin_in_parent_reference_frame, origin_in_parent_reference_frame,
pipeline_id, pipeline_id,
parent_is_identity,
); );
self.add_spatial_node(node) self.add_spatial_node(node)
} }