--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -66,11 +66,11 @@ mozangle = "0.1"
[target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
freetype = { version = "0.4", default-features = false }
[target.'cfg(target_os = "windows")'.dependencies]
dwrote = "0.4.1"
[target.'cfg(target_os = "macos")'.dependencies]
-core-foundation = "0.5"
-core-graphics = "0.13"
-core-text = { version = "9.2.0", default-features = false }
+core-foundation = "0.6"
+core-graphics = "0.14"
+core-text = { version = "10", default-features = false }
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -470,18 +470,18 @@ impl AlphaBatchBuilder {
};
// Even though most of the time a splitter isn't used or needed,
// they are cheap to construct so we will always pass one down.
let mut splitter = BspSplitter::new();
// Add each run in this picture to the batch.
for run in &pic.runs {
- let scroll_node = &ctx.clip_scroll_tree.nodes[run.clip_and_scroll.scroll_node_id.0];
- let transform_id = ctx.transforms.get_id(scroll_node.transform_index);
+ let transform_id =
+ ctx.transforms.get_id(run.clip_and_scroll.scroll_node_id.transform_index());
self.add_run_to_batch(
run,
transform_id,
ctx,
gpu_cache,
render_tasks,
task_id,
task_address,
@@ -665,17 +665,17 @@ impl AlphaBatchBuilder {
// If this picture is participating in a 3D rendering context,
// then don't add it to any batches here. Instead, create a polygon
// for it and add it to the current plane splitter.
if picture.is_in_3d_context {
// Push into parent plane splitter.
debug_assert!(picture.surface.is_some());
let real_xf = &ctx.clip_scroll_tree
- .nodes[picture.reference_frame_index.0]
+ .spatial_nodes[picture.reference_frame_index.0]
.world_content_transform
.into();
let polygon = make_polygon(
picture.real_local_rect,
real_xf,
prim_index.0,
);
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -2,21 +2,21 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{BorderRadius, ClipMode, ComplexClipRegion, DeviceIntRect, DevicePixelScale, ImageMask};
use api::{ImageRendering, LayoutRect, LayoutSize, LayoutPoint, LayoutVector2D, LocalClip};
use api::{BoxShadowClipMode, LayoutToWorldScale, LineOrientation, LineStyle};
use border::{ensure_no_corner_overlap};
use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey};
-use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId, TransformIndex};
+use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId};
use ellipse::Ellipse;
use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
-use gpu_types::{BoxShadowStretchMode};
+use gpu_types::{BoxShadowStretchMode, TransformIndex};
use prim_store::{ClipData, ImageMaskData};
use render_task::to_cache_size;
use resource_cache::{ImageRequest, ResourceCache};
use util::{LayoutToWorldFastTransform, MaxRect, calculate_screen_bounding_rect};
use util::{extract_inner_rect_safe, pack_as_float};
use std::sync::Arc;
#[derive(Debug)]
@@ -252,38 +252,60 @@ impl ClipSource {
..*info
})
}
_ => {
panic!("bug: other clip sources not expected here yet");
}
}
}
+
+ pub fn is_rect(&self) -> bool {
+ match *self {
+ ClipSource::Rectangle(..) => true,
+ _ => false,
+ }
+ }
+
+ pub fn is_image_or_line_decoration_clip(&self) -> bool {
+ match *self {
+ ClipSource::Image(..) | ClipSource::LineDecoration(..) => true,
+ _ => false,
+ }
+ }
}
#[derive(Debug)]
pub struct ClipSources {
pub clips: Vec<(ClipSource, GpuCacheHandle)>,
pub local_inner_rect: LayoutRect,
- pub local_outer_rect: Option<LayoutRect>
+ pub local_outer_rect: Option<LayoutRect>,
+ pub only_rectangular_clips: bool,
+ pub has_image_or_line_decoration_clip: bool,
}
impl ClipSources {
pub fn new(clips: Vec<ClipSource>) -> Self {
let (local_inner_rect, local_outer_rect) = Self::calculate_inner_and_outer_rects(&clips);
+ let has_image_or_line_decoration_clip =
+ clips.iter().any(|clip| clip.is_image_or_line_decoration_clip());
+ let only_rectangular_clips =
+ !has_image_or_line_decoration_clip && clips.iter().all(|clip| clip.is_rect());
let clips = clips
.into_iter()
.map(|clip| (clip, GpuCacheHandle::new()))
.collect();
ClipSources {
clips,
local_inner_rect,
local_outer_rect,
+ only_rectangular_clips,
+ has_image_or_line_decoration_clip,
}
}
pub fn clips(&self) -> &[(ClipSource, GpuCacheHandle)] {
&self.clips
}
fn calculate_inner_and_outer_rects(clips: &Vec<ClipSource>) -> (LayoutRect, Option<LayoutRect>) {
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/src/clip_node.rs
@@ -0,0 +1,100 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use api::DevicePixelScale;
+use clip::{ClipChain, ClipChainNode, ClipSourcesHandle, ClipStore, ClipWorkItem};
+use clip_scroll_tree::{ClipChainIndex, SpatialNodeIndex};
+use gpu_cache::GpuCache;
+use resource_cache::ResourceCache;
+use spatial_node::SpatialNode;
+
+#[derive(Debug)]
+pub struct ClipNode {
+ /// The node that determines how this clip node is positioned.
+ pub spatial_node: SpatialNodeIndex,
+
+ /// A handle to this clip nodes clips in the ClipStore.
+ pub handle: Option<ClipSourcesHandle>,
+
+ /// An index to a ClipChain defined by this ClipNode's hiearchy in the display
+ /// list.
+ pub clip_chain_index: ClipChainIndex,
+
+ /// The index of the parent ClipChain of this node's hiearchical ClipChain.
+ pub parent_clip_chain_index: ClipChainIndex,
+
+ /// A copy of the ClipChainNode this node would produce. We need to keep a copy,
+ /// because the ClipChain may not contain our node if is optimized out, but API
+ /// defined ClipChains will still need to access it.
+ pub clip_chain_node: Option<ClipChainNode>,
+}
+
+impl ClipNode {
+ const EMPTY: ClipNode = ClipNode {
+ spatial_node: SpatialNodeIndex(0),
+ handle: None,
+ clip_chain_index: ClipChainIndex(0),
+ parent_clip_chain_index: ClipChainIndex(0),
+ clip_chain_node: None,
+ };
+
+ pub fn empty() -> ClipNode {
+ ClipNode::EMPTY
+ }
+
+ pub fn update(
+ &mut self,
+ spatial_node: &SpatialNode,
+ device_pixel_scale: DevicePixelScale,
+ clip_store: &mut ClipStore,
+ resource_cache: &mut ResourceCache,
+ gpu_cache: &mut GpuCache,
+ clip_chains: &mut [ClipChain],
+ ) {
+ let (clip_sources, weak_handle) = match self.handle {
+ Some(ref handle) => (clip_store.get_mut(handle), handle.weak()),
+ None => {
+ warn!("Tried to process an empty clip node");
+ return;
+ }
+ };
+ clip_sources.update(gpu_cache, resource_cache, device_pixel_scale);
+
+ let (screen_inner_rect, screen_outer_rect) = clip_sources.get_screen_bounds(
+ &spatial_node.world_content_transform,
+ device_pixel_scale,
+ None,
+ );
+
+ // All clipping SpatialNodes should have outer rectangles, because they never
+ // use the BorderCorner clip type and they always have at last one non-ClipOut
+ // Rectangle ClipSource.
+ let screen_outer_rect = screen_outer_rect
+ .expect("Clipping node didn't have outer rect.");
+ let local_outer_rect = clip_sources.local_outer_rect
+ .expect("Clipping node didn't have outer rect.");
+
+ let new_node = ClipChainNode {
+ work_item: ClipWorkItem {
+ transform_index: self.spatial_node.transform_index(),
+ clip_sources: weak_handle,
+ coordinate_system_id: spatial_node.coordinate_system_id,
+ },
+ local_clip_rect: spatial_node
+ .coordinate_system_relative_transform
+ .transform_rect(&local_outer_rect),
+ screen_outer_rect,
+ screen_inner_rect,
+ prev: None,
+ };
+
+ let mut clip_chain =
+ clip_chains[self.parent_clip_chain_index.0]
+ .new_with_added_node(&new_node);
+
+ self.clip_chain_node = Some(new_node);
+ clip_chain.parent_index = Some(self.parent_clip_chain_index);
+ clip_chains[self.clip_chain_index.0] = clip_chain;
+ }
+}
deleted file mode 100644
--- a/gfx/webrender/src/clip_scroll_node.rs
+++ /dev/null
@@ -1,844 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use api::{DevicePixelScale, ExternalScrollId, LayoutPixel, LayoutPoint, LayoutRect, LayoutSize};
-use api::{LayoutVector2D, LayoutTransform, PipelineId, PropertyBinding};
-use api::{ScrollClamping, ScrollLocation, ScrollSensitivity, StickyOffsetBounds};
-use clip::{ClipChain, ClipChainNode, ClipSourcesHandle, ClipStore, ClipWorkItem};
-use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, CoordinateSystemId};
-use clip_scroll_tree::{TransformUpdateState, TransformIndex};
-use euclid::SideOffsets2D;
-use gpu_cache::GpuCache;
-use gpu_types::{TransformData, TransformPalette};
-use resource_cache::ResourceCache;
-use scene::SceneProperties;
-use util::{LayoutToWorldFastTransform, LayoutFastTransform};
-use util::{TransformedRectKind};
-
-#[derive(Debug)]
-pub struct StickyFrameInfo {
- pub frame_rect: LayoutRect,
- pub margins: SideOffsets2D<Option<f32>>,
- pub vertical_offset_bounds: StickyOffsetBounds,
- pub horizontal_offset_bounds: StickyOffsetBounds,
- pub previously_applied_offset: LayoutVector2D,
- pub current_offset: LayoutVector2D,
-}
-
-impl StickyFrameInfo {
- pub fn new(
- frame_rect: LayoutRect,
- margins: SideOffsets2D<Option<f32>>,
- vertical_offset_bounds: StickyOffsetBounds,
- horizontal_offset_bounds: StickyOffsetBounds,
- previously_applied_offset: LayoutVector2D
- ) -> StickyFrameInfo {
- StickyFrameInfo {
- frame_rect,
- margins,
- vertical_offset_bounds,
- horizontal_offset_bounds,
- previously_applied_offset,
- current_offset: LayoutVector2D::zero(),
- }
- }
-}
-
-#[derive(Debug)]
-pub enum SpatialNodeKind {
- /// A special kind of node that adjusts its position based on the position
- /// of its parent node and a given set of sticky positioning offset bounds.
- /// Sticky positioned is described in the CSS Positioned Layout Module Level 3 here:
- /// https://www.w3.org/TR/css-position-3/#sticky-pos
- StickyFrame(StickyFrameInfo),
-
- /// Transforms it's content, but doesn't clip it. Can also be adjusted
- /// by scroll events or setting scroll offsets.
- ScrollFrame(ScrollFrameInfo),
-
- /// A reference frame establishes a new coordinate space in the tree.
- ReferenceFrame(ReferenceFrameInfo),
-}
-
-#[derive(Debug)]
-pub enum NodeType {
- Spatial {
- kind: SpatialNodeKind,
- },
-
- /// Other nodes just do clipping, but no transformation.
- Clip {
- handle: ClipSourcesHandle,
- clip_chain_index: ClipChainIndex,
-
- /// A copy of the ClipChainNode this node would produce. We need to keep a copy,
- /// because the ClipChain may not contain our node if is optimized out, but API
- /// defined ClipChains will still need to access it.
- clip_chain_node: Option<ClipChainNode>,
- },
-
- /// An empty node, used to pad the ClipScrollTree's array of nodes so that
- /// we can immediately use each assigned ClipScrollNodeIndex. After display
- /// list flattening this node type should never be used.
- Empty,
-}
-
-impl NodeType {
- fn is_reference_frame(&self) -> bool {
- match *self {
- NodeType::Spatial { kind: SpatialNodeKind::ReferenceFrame(_), .. } => true,
- _ => false,
- }
- }
-}
-
-/// Contains information common among all types of ClipScrollTree nodes.
-#[derive(Debug)]
-pub struct ClipScrollNode {
- /// The transformation for this viewport in world coordinates is the transformation for
- /// our parent reference frame, plus any accumulated scrolling offsets from nodes
- /// between our reference frame and this node. For reference frames, we also include
- /// whatever local transformation this reference frame provides.
- pub world_viewport_transform: LayoutToWorldFastTransform,
-
- /// World transform for content transformed by this node.
- pub world_content_transform: LayoutToWorldFastTransform,
-
- /// The current transform kind of world_content_transform.
- pub transform_kind: TransformedRectKind,
-
- /// Pipeline that this layer belongs to
- pub pipeline_id: PipelineId,
-
- /// Parent layer. If this is None, we are the root node.
- pub parent: Option<ClipScrollNodeIndex>,
-
- /// Child layers
- pub children: Vec<ClipScrollNodeIndex>,
-
- /// The type of this node and any data associated with that node type.
- pub node_type: NodeType,
-
- /// 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.
- pub invertible: bool,
-
- /// The axis-aligned coordinate system id of this node.
- pub coordinate_system_id: CoordinateSystemId,
-
- /// The transformation from the coordinate system which established our compatible coordinate
- /// system (same coordinate system id) and us. This can change via scroll offsets and via new
- /// reference frame transforms.
- pub coordinate_system_relative_transform: LayoutFastTransform,
-
- /// The index of the spatial node that provides positioning information for this node.
- /// For reference frames, scroll and sticky frames it is a unique identfier.
- /// For clip nodes, this is the nearest ancestor spatial node.
- pub transform_index: TransformIndex,
-}
-
-impl ClipScrollNode {
- pub fn new(
- pipeline_id: PipelineId,
- parent_index: Option<ClipScrollNodeIndex>,
- node_type: NodeType,
- transform_index: TransformIndex,
- ) -> Self {
- ClipScrollNode {
- world_viewport_transform: LayoutToWorldFastTransform::identity(),
- world_content_transform: LayoutToWorldFastTransform::identity(),
- transform_kind: TransformedRectKind::AxisAligned,
- parent: parent_index,
- children: Vec::new(),
- pipeline_id,
- node_type,
- invertible: true,
- coordinate_system_id: CoordinateSystemId(0),
- coordinate_system_relative_transform: LayoutFastTransform::identity(),
- transform_index,
- }
- }
-
- pub fn empty() -> ClipScrollNode {
- Self::new(
- PipelineId::dummy(),
- None,
- NodeType::Empty,
- TransformIndex(0),
- )
- }
-
- pub fn new_scroll_frame(
- pipeline_id: PipelineId,
- parent_index: ClipScrollNodeIndex,
- external_id: Option<ExternalScrollId>,
- frame_rect: &LayoutRect,
- content_size: &LayoutSize,
- scroll_sensitivity: ScrollSensitivity,
- transform_index: TransformIndex,
- ) -> Self {
- let node_type = NodeType::Spatial {
- kind: SpatialNodeKind::ScrollFrame(ScrollFrameInfo::new(
- *frame_rect,
- scroll_sensitivity,
- LayoutSize::new(
- (content_size.width - frame_rect.size.width).max(0.0),
- (content_size.height - frame_rect.size.height).max(0.0)
- ),
- external_id,
- )),
- };
-
- Self::new(
- pipeline_id,
- Some(parent_index),
- node_type,
- transform_index,
- )
- }
-
- pub fn new_reference_frame(
- parent_index: Option<ClipScrollNodeIndex>,
- source_transform: Option<PropertyBinding<LayoutTransform>>,
- source_perspective: Option<LayoutTransform>,
- origin_in_parent_reference_frame: LayoutVector2D,
- pipeline_id: PipelineId,
- transform_index: TransformIndex,
- ) -> Self {
- let identity = LayoutTransform::identity();
- let source_perspective = source_perspective.map_or_else(
- LayoutFastTransform::identity, |perspective| perspective.into());
- let info = ReferenceFrameInfo {
- resolved_transform: LayoutFastTransform::identity(),
- source_transform: source_transform.unwrap_or(PropertyBinding::Value(identity)),
- source_perspective,
- origin_in_parent_reference_frame,
- invertible: true,
- };
- Self::new(
- pipeline_id,
- parent_index,
- NodeType::Spatial {
- kind: SpatialNodeKind::ReferenceFrame(info),
- },
- transform_index,
- )
- }
-
- pub fn new_sticky_frame(
- parent_index: ClipScrollNodeIndex,
- sticky_frame_info: StickyFrameInfo,
- pipeline_id: PipelineId,
- transform_index: TransformIndex,
- ) -> Self {
- let node_type = NodeType::Spatial {
- kind: SpatialNodeKind::StickyFrame(sticky_frame_info),
- };
- Self::new(
- pipeline_id,
- Some(parent_index),
- node_type,
- transform_index,
- )
- }
-
-
- pub fn add_child(&mut self, child: ClipScrollNodeIndex) {
- self.children.push(child);
- }
-
- pub fn apply_old_scrolling_state(&mut self, old_scroll_info: &ScrollFrameInfo) {
- match self.node_type {
- NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(ref mut scrolling), .. } => {
- *scrolling = scrolling.combine_with_old_scroll_info(old_scroll_info);
- }
- _ if old_scroll_info.offset != LayoutVector2D::zero() => {
- warn!("Tried to scroll a non-scroll node.")
- }
- _ => {}
- }
- }
-
- pub fn set_scroll_origin(&mut self, origin: &LayoutPoint, clamp: ScrollClamping) -> bool {
- let scrollable_size = self.scrollable_size();
- let scrollable_width = scrollable_size.width;
- let scrollable_height = scrollable_size.height;
-
- let scrolling = match self.node_type {
- NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(ref mut scrolling), .. } => scrolling,
- _ => {
- warn!("Tried to scroll a non-scroll node.");
- return false;
- }
- };
-
- let new_offset = match clamp {
- ScrollClamping::ToContentBounds => {
- if scrollable_height <= 0. && scrollable_width <= 0. {
- return false;
- }
-
- let origin = LayoutPoint::new(origin.x.max(0.0), origin.y.max(0.0));
- LayoutVector2D::new(
- (-origin.x).max(-scrollable_width).min(0.0).round(),
- (-origin.y).max(-scrollable_height).min(0.0).round(),
- )
- }
- ScrollClamping::NoClamping => LayoutPoint::zero() - *origin,
- };
-
- if new_offset == scrolling.offset {
- return false;
- }
-
- scrolling.offset = new_offset;
- true
- }
-
- pub fn mark_uninvertible(&mut self) {
- self.invertible = false;
- self.world_content_transform = LayoutToWorldFastTransform::identity();
- self.world_viewport_transform = LayoutToWorldFastTransform::identity();
- }
-
- pub fn push_gpu_data(
- &mut self,
- transform_palette: &mut TransformPalette,
- ) {
- if let NodeType::Spatial { .. } = self.node_type {
- if !self.invertible {
- transform_palette.set(self.transform_index, TransformData::invalid());
- return;
- }
-
- let inv_transform = match self.world_content_transform.inverse() {
- Some(inverted) => inverted.to_transform(),
- None => {
- transform_palette.set(self.transform_index, TransformData::invalid());
- return;
- }
- };
-
- let data = TransformData {
- transform: self.world_content_transform.into(),
- inv_transform,
- };
-
- // Write the data that will be made available to the GPU for this node.
- transform_palette.set(self.transform_index, data);
- }
- }
-
- pub fn update(
- &mut self,
- state: &mut TransformUpdateState,
- next_coordinate_system_id: &mut CoordinateSystemId,
- device_pixel_scale: DevicePixelScale,
- clip_store: &mut ClipStore,
- resource_cache: &mut ResourceCache,
- gpu_cache: &mut GpuCache,
- scene_properties: &SceneProperties,
- clip_chains: &mut Vec<ClipChain>,
- ) {
- // If any of our parents was not rendered, we are not rendered either and can just
- // quit here.
- if !state.invertible {
- self.mark_uninvertible();
- return;
- }
-
- self.update_transform(state, next_coordinate_system_id, scene_properties);
-
- self.transform_kind = if self.world_content_transform.preserves_2d_axis_alignment() {
- TransformedRectKind::AxisAligned
- } else {
- TransformedRectKind::Complex
- };
-
- // 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
- // translations which should be invertible.
- match self.node_type {
- NodeType::Spatial { kind: SpatialNodeKind::ReferenceFrame(info), .. } if !info.invertible => {
- self.mark_uninvertible();
- return;
- }
- _ => self.invertible = true,
- }
-
- self.update_clip_work_item(
- state,
- device_pixel_scale,
- clip_store,
- resource_cache,
- gpu_cache,
- clip_chains,
- );
- }
-
- pub fn update_clip_work_item(
- &mut self,
- state: &mut TransformUpdateState,
- device_pixel_scale: DevicePixelScale,
- clip_store: &mut ClipStore,
- resource_cache: &mut ResourceCache,
- gpu_cache: &mut GpuCache,
- clip_chains: &mut [ClipChain],
- ) {
- let (clip_sources_handle, clip_chain_index, stored_clip_chain_node) = match self.node_type {
- NodeType::Clip { ref handle, clip_chain_index, ref mut clip_chain_node, .. } =>
- (handle, clip_chain_index, clip_chain_node),
- _ => {
- self.invertible = true;
- return;
- }
- };
-
- let clip_sources = clip_store.get_mut(clip_sources_handle);
- clip_sources.update(
- gpu_cache,
- resource_cache,
- device_pixel_scale,
- );
-
- let (screen_inner_rect, screen_outer_rect) = clip_sources.get_screen_bounds(
- &self.world_viewport_transform,
- device_pixel_scale,
- None,
- );
-
- // All clipping ClipScrollNodes should have outer rectangles, because they never
- // use the BorderCorner clip type and they always have at last one non-ClipOut
- // Rectangle ClipSource.
- let screen_outer_rect = screen_outer_rect
- .expect("Clipping node didn't have outer rect.");
- let local_outer_rect = clip_sources.local_outer_rect
- .expect("Clipping node didn't have outer rect.");
-
- let new_node = ClipChainNode {
- work_item: ClipWorkItem {
- transform_index: self.transform_index,
- clip_sources: clip_sources_handle.weak(),
- coordinate_system_id: state.current_coordinate_system_id,
- },
- local_clip_rect:
- self.coordinate_system_relative_transform.transform_rect(&local_outer_rect),
- screen_outer_rect,
- screen_inner_rect,
- prev: None,
- };
-
- let mut clip_chain =
- clip_chains[state.parent_clip_chain_index.0].new_with_added_node(&new_node);
-
- *stored_clip_chain_node = Some(new_node);
- clip_chain.parent_index = Some(state.parent_clip_chain_index);
- clip_chains[clip_chain_index.0] = clip_chain;
- state.parent_clip_chain_index = clip_chain_index;
- }
-
- pub fn update_transform(
- &mut self,
- state: &mut TransformUpdateState,
- next_coordinate_system_id: &mut CoordinateSystemId,
- scene_properties: &SceneProperties,
- ) {
- if self.node_type.is_reference_frame() {
- self.update_transform_for_reference_frame(
- state,
- next_coordinate_system_id,
- scene_properties
- );
- return;
- }
-
- // We calculate this here to avoid a double-borrow later.
- let sticky_offset = self.calculate_sticky_offset(
- &state.nearest_scrolling_ancestor_offset,
- &state.nearest_scrolling_ancestor_viewport,
- );
-
- // The transformation for the bounds of our viewport is the parent reference frame
- // transform, plus any accumulated scroll offset from our parents, plus any offset
- // provided by our own sticky positioning.
- let accumulated_offset = state.parent_accumulated_scroll_offset + sticky_offset;
- self.world_viewport_transform = if accumulated_offset != LayoutVector2D::zero() {
- state.parent_reference_frame_transform.pre_translate(&accumulated_offset)
- } else {
- state.parent_reference_frame_transform
- };
-
- // The transformation for any content inside of us is the viewport transformation, plus
- // whatever scrolling offset we supply as well.
- let scroll_offset = self.scroll_offset();
- self.world_content_transform = if scroll_offset != LayoutVector2D::zero() {
- self.world_viewport_transform.pre_translate(&scroll_offset)
- } else {
- self.world_viewport_transform
- };
-
- let added_offset = state.parent_accumulated_scroll_offset + sticky_offset + scroll_offset;
- self.coordinate_system_relative_transform =
- state.coordinate_system_relative_transform.offset(added_offset);
-
- if let NodeType::Spatial { kind: SpatialNodeKind::StickyFrame(ref mut info), .. } = self.node_type {
- info.current_offset = sticky_offset;
- }
-
- self.coordinate_system_id = state.current_coordinate_system_id;
- }
-
- pub fn update_transform_for_reference_frame(
- &mut self,
- state: &mut TransformUpdateState,
- next_coordinate_system_id: &mut CoordinateSystemId,
- scene_properties: &SceneProperties,
- ) {
- let info = match self.node_type {
- NodeType::Spatial { kind: SpatialNodeKind::ReferenceFrame(ref mut info), .. } => info,
- _ => unreachable!("Called update_transform_for_reference_frame on non-ReferenceFrame"),
- };
-
- // Resolve the transform against any property bindings.
- let source_transform = scene_properties.resolve_layout_transform(&info.source_transform);
- info.resolved_transform =
- LayoutFastTransform::with_vector(info.origin_in_parent_reference_frame)
- .pre_mul(&source_transform.into())
- .pre_mul(&info.source_perspective);
-
- // The transformation for this viewport in world coordinates is the transformation for
- // our parent reference frame, plus any accumulated scrolling offsets from nodes
- // between our reference frame and this node. Finally, we also include
- // whatever local transformation this reference frame provides.
- let relative_transform = info.resolved_transform
- .post_translate(state.parent_accumulated_scroll_offset)
- .to_transform()
- .with_destination::<LayoutPixel>();
- self.world_viewport_transform =
- state.parent_reference_frame_transform.pre_mul(&relative_transform.into());
- self.world_content_transform = self.world_viewport_transform;
-
- info.invertible = self.world_viewport_transform.is_invertible();
- if !info.invertible {
- return;
- }
-
- // Try to update our compatible coordinate system transform. If we cannot, start a new
- // incompatible coordinate system.
- match state.coordinate_system_relative_transform.update(relative_transform) {
- Some(offset) => self.coordinate_system_relative_transform = offset,
- None => {
- self.coordinate_system_relative_transform = LayoutFastTransform::identity();
- state.current_coordinate_system_id = *next_coordinate_system_id;
- next_coordinate_system_id.advance();
- }
- }
-
- self.coordinate_system_id = state.current_coordinate_system_id;
- }
-
- fn calculate_sticky_offset(
- &self,
- viewport_scroll_offset: &LayoutVector2D,
- viewport_rect: &LayoutRect,
- ) -> LayoutVector2D {
- let info = match self.node_type {
- NodeType::Spatial { kind: SpatialNodeKind::StickyFrame(ref info), .. } => info,
- _ => return LayoutVector2D::zero(),
- };
-
- if info.margins.top.is_none() && info.margins.bottom.is_none() &&
- info.margins.left.is_none() && info.margins.right.is_none() {
- return LayoutVector2D::zero();
- }
-
- // The viewport and margins of the item establishes the maximum amount that it can
- // be offset in order to keep it on screen. Since we care about the relationship
- // between the scrolled content and unscrolled viewport we adjust the viewport's
- // position by the scroll offset in order to work with their relative positions on the
- // page.
- let sticky_rect = info.frame_rect.translate(viewport_scroll_offset);
-
- let mut sticky_offset = LayoutVector2D::zero();
- if let Some(margin) = info.margins.top {
- let top_viewport_edge = viewport_rect.min_y() + margin;
- if sticky_rect.min_y() < top_viewport_edge {
- // If the sticky rect is positioned above the top edge of the viewport (plus margin)
- // we move it down so that it is fully inside the viewport.
- sticky_offset.y = top_viewport_edge - sticky_rect.min_y();
- } else if info.previously_applied_offset.y > 0.0 &&
- sticky_rect.min_y() > top_viewport_edge {
- // However, if the sticky rect is positioned *below* the top edge of the viewport
- // and there is already some offset applied to the sticky rect's position, then
- // we need to move it up so that it remains at the correct position. This
- // makes sticky_offset.y negative and effectively reduces the amount of the
- // offset that was already applied. We limit the reduction so that it can, at most,
- // cancel out the already-applied offset, but should never end up adjusting the
- // position the other way.
- sticky_offset.y = top_viewport_edge - sticky_rect.min_y();
- sticky_offset.y = sticky_offset.y.max(-info.previously_applied_offset.y);
- }
- debug_assert!(sticky_offset.y + info.previously_applied_offset.y >= 0.0);
- }
-
- // If we don't have a sticky-top offset (sticky_offset.y + info.previously_applied_offset.y
- // == 0), or if we have a previously-applied bottom offset (previously_applied_offset.y < 0)
- // then we check for handling the bottom margin case.
- if sticky_offset.y + info.previously_applied_offset.y <= 0.0 {
- if let Some(margin) = info.margins.bottom {
- // Same as the above case, but inverted for bottom-sticky items. Here
- // we adjust items upwards, resulting in a negative sticky_offset.y,
- // or reduce the already-present upward adjustment, resulting in a positive
- // sticky_offset.y.
- let bottom_viewport_edge = viewport_rect.max_y() - margin;
- if sticky_rect.max_y() > bottom_viewport_edge {
- sticky_offset.y = bottom_viewport_edge - sticky_rect.max_y();
- } else if info.previously_applied_offset.y < 0.0 &&
- sticky_rect.max_y() < bottom_viewport_edge {
- sticky_offset.y = bottom_viewport_edge - sticky_rect.max_y();
- sticky_offset.y = sticky_offset.y.min(-info.previously_applied_offset.y);
- }
- debug_assert!(sticky_offset.y + info.previously_applied_offset.y <= 0.0);
- }
- }
-
- // Same as above, but for the x-axis.
- if let Some(margin) = info.margins.left {
- let left_viewport_edge = viewport_rect.min_x() + margin;
- if sticky_rect.min_x() < left_viewport_edge {
- sticky_offset.x = left_viewport_edge - sticky_rect.min_x();
- } else if info.previously_applied_offset.x > 0.0 &&
- sticky_rect.min_x() > left_viewport_edge {
- sticky_offset.x = left_viewport_edge - sticky_rect.min_x();
- sticky_offset.x = sticky_offset.x.max(-info.previously_applied_offset.x);
- }
- debug_assert!(sticky_offset.x + info.previously_applied_offset.x >= 0.0);
- }
-
- if sticky_offset.x + info.previously_applied_offset.x <= 0.0 {
- if let Some(margin) = info.margins.right {
- let right_viewport_edge = viewport_rect.max_x() - margin;
- if sticky_rect.max_x() > right_viewport_edge {
- sticky_offset.x = right_viewport_edge - sticky_rect.max_x();
- } else if info.previously_applied_offset.x < 0.0 &&
- sticky_rect.max_x() < right_viewport_edge {
- sticky_offset.x = right_viewport_edge - sticky_rect.max_x();
- sticky_offset.x = sticky_offset.x.min(-info.previously_applied_offset.x);
- }
- debug_assert!(sticky_offset.x + info.previously_applied_offset.x <= 0.0);
- }
- }
-
- // The total "sticky offset" (which is the sum that was already applied by
- // the calling code, stored in info.previously_applied_offset, and the extra amount we
- // computed as a result of scrolling, stored in sticky_offset) needs to be
- // clamped to the provided bounds.
- let clamp_adjusted = |value: f32, adjust: f32, bounds: &StickyOffsetBounds| {
- (value + adjust).max(bounds.min).min(bounds.max) - adjust
- };
- sticky_offset.y = clamp_adjusted(sticky_offset.y,
- info.previously_applied_offset.y,
- &info.vertical_offset_bounds);
- sticky_offset.x = clamp_adjusted(sticky_offset.x,
- info.previously_applied_offset.x,
- &info.horizontal_offset_bounds);
-
- sticky_offset
- }
-
- pub fn prepare_state_for_children(&self, state: &mut TransformUpdateState) {
- if !self.invertible {
- state.invertible = false;
- return;
- }
-
- // The transformation we are passing is the transformation of the parent
- // reference frame and the offset is the accumulated offset of all the nodes
- // between us and the parent reference frame. If we are a reference frame,
- // we need to reset both these values.
- match self.node_type {
- NodeType::Spatial { ref kind, .. } => {
- match *kind {
- SpatialNodeKind::StickyFrame(ref info) => {
- // We don't translate the combined rect by the sticky offset, because sticky
- // offsets actually adjust the node position itself, whereas scroll offsets
- // only apply to contents inside the node.
- state.parent_accumulated_scroll_offset =
- info.current_offset + state.parent_accumulated_scroll_offset;
- }
- SpatialNodeKind::ScrollFrame(ref scrolling) => {
- state.parent_accumulated_scroll_offset =
- scrolling.offset + state.parent_accumulated_scroll_offset;
- state.nearest_scrolling_ancestor_offset = scrolling.offset;
- state.nearest_scrolling_ancestor_viewport = scrolling.viewport_rect;
- }
- SpatialNodeKind::ReferenceFrame(ref info) => {
- state.parent_reference_frame_transform = self.world_viewport_transform;
- state.parent_accumulated_scroll_offset = LayoutVector2D::zero();
- state.coordinate_system_relative_transform =
- self.coordinate_system_relative_transform.clone();
- let translation = -info.origin_in_parent_reference_frame;
- state.nearest_scrolling_ancestor_viewport =
- state.nearest_scrolling_ancestor_viewport
- .translate(&translation);
- }
- }
- }
- NodeType::Clip{ .. } => { }
- NodeType::Empty => unreachable!("Empty node remaining in ClipScrollTree."),
- }
- }
-
- pub fn scrollable_size(&self) -> LayoutSize {
- match self.node_type {
- NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(state), .. } => state.scrollable_size,
- _ => LayoutSize::zero(),
- }
- }
-
- pub fn scroll(&mut self, scroll_location: ScrollLocation) -> bool {
- let scrolling = match self.node_type {
- NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(ref mut scrolling), .. } => scrolling,
- _ => return false,
- };
-
- let delta = match scroll_location {
- ScrollLocation::Delta(delta) => delta,
- ScrollLocation::Start => {
- if scrolling.offset.y.round() >= 0.0 {
- // Nothing to do on this layer.
- return false;
- }
-
- scrolling.offset.y = 0.0;
- return true;
- }
- ScrollLocation::End => {
- let end_pos = -scrolling.scrollable_size.height;
- if scrolling.offset.y.round() <= end_pos {
- // Nothing to do on this layer.
- return false;
- }
-
- scrolling.offset.y = end_pos;
- return true;
- }
- };
-
- let scrollable_width = scrolling.scrollable_size.width;
- let scrollable_height = scrolling.scrollable_size.height;
- let original_layer_scroll_offset = scrolling.offset;
-
- if scrollable_width > 0. {
- scrolling.offset.x = (scrolling.offset.x + delta.x)
- .min(0.0)
- .max(-scrollable_width)
- .round();
- }
-
- if scrollable_height > 0. {
- scrolling.offset.y = (scrolling.offset.y + delta.y)
- .min(0.0)
- .max(-scrollable_height)
- .round();
- }
-
- scrolling.offset != original_layer_scroll_offset
- }
-
- pub fn scroll_offset(&self) -> LayoutVector2D {
- match self.node_type {
- NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(ref scrolling), .. } => scrolling.offset,
- _ => LayoutVector2D::zero(),
- }
- }
-
- pub fn matches_external_id(&self, external_id: ExternalScrollId) -> bool {
- match self.node_type {
- NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(info), .. } if info.external_id == Some(external_id) => true,
- _ => false,
- }
- }
-}
-
-#[derive(Copy, Clone, Debug)]
-pub struct ScrollFrameInfo {
- /// The rectangle of the viewport of this scroll frame. This is important for
- /// positioning of items inside child StickyFrames.
- pub viewport_rect: LayoutRect,
-
- pub offset: LayoutVector2D,
- pub scroll_sensitivity: ScrollSensitivity,
-
- /// Amount that this ScrollFrame can scroll in both directions.
- pub scrollable_size: LayoutSize,
-
- /// An external id to identify this scroll frame to API clients. This
- /// allows setting scroll positions via the API without relying on ClipsIds
- /// which may change between frames.
- pub external_id: Option<ExternalScrollId>,
-
-}
-
-/// Manages scrolling offset.
-impl ScrollFrameInfo {
- pub fn new(
- viewport_rect: LayoutRect,
- scroll_sensitivity: ScrollSensitivity,
- scrollable_size: LayoutSize,
- external_id: Option<ExternalScrollId>,
- ) -> ScrollFrameInfo {
- ScrollFrameInfo {
- viewport_rect,
- offset: LayoutVector2D::zero(),
- scroll_sensitivity,
- scrollable_size,
- external_id,
- }
- }
-
- pub fn sensitive_to_input_events(&self) -> bool {
- match self.scroll_sensitivity {
- ScrollSensitivity::ScriptAndInputEvents => true,
- ScrollSensitivity::Script => false,
- }
- }
-
- pub fn combine_with_old_scroll_info(
- self,
- old_scroll_info: &ScrollFrameInfo
- ) -> ScrollFrameInfo {
- ScrollFrameInfo {
- viewport_rect: self.viewport_rect,
- offset: old_scroll_info.offset,
- scroll_sensitivity: self.scroll_sensitivity,
- scrollable_size: self.scrollable_size,
- external_id: self.external_id,
- }
- }
-}
-
-/// Contains information about reference frames.
-#[derive(Copy, Clone, Debug)]
-pub struct ReferenceFrameInfo {
- /// The transformation that establishes this reference frame, relative to the parent
- /// reference frame. The origin of the reference frame is included in the transformation.
- pub resolved_transform: LayoutFastTransform,
-
- /// The source transform and perspective matrices provided by the stacking context
- /// that forms this reference frame. We maintain the property binding information
- /// here so that we can resolve the animated transform and update the tree each
- /// frame.
- pub source_transform: PropertyBinding<LayoutTransform>,
- pub source_perspective: LayoutFastTransform,
-
- /// The original, not including the transform and relative to the parent reference frame,
- /// origin of this reference frame. This is already rolled into the `transform' property, but
- /// we also store it here to properly transform the viewport for sticky positioning.
- pub origin_in_parent_reference_frame: LayoutVector2D,
-
- /// True if the resolved transform is invertible.
- pub invertible: bool,
-}
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -1,50 +1,51 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{DeviceIntRect, DevicePixelScale, ExternalScrollId, LayoutPoint, LayoutRect, LayoutVector2D};
use api::{PipelineId, ScrollClamping, ScrollLocation, ScrollNodeState};
use api::{LayoutSize, LayoutTransform, PropertyBinding, ScrollSensitivity, WorldPoint};
use clip::{ClipChain, ClipSourcesHandle, ClipStore};
-use clip_scroll_node::{ClipScrollNode, NodeType, SpatialNodeKind, ScrollFrameInfo, StickyFrameInfo};
+use clip_node::ClipNode;
use gpu_cache::GpuCache;
-use gpu_types::TransformPalette;
+use gpu_types::{TransformIndex, TransformPalette};
use internal_types::{FastHashMap, FastHashSet};
use print_tree::{PrintTree, PrintTreePrinter};
use resource_cache::ResourceCache;
use scene::SceneProperties;
+use spatial_node::{ScrollFrameInfo, SpatialNode, SpatialNodeType, StickyFrameInfo};
use util::{LayoutFastTransform, LayoutToWorldFastTransform};
pub type ScrollStates = FastHashMap<ExternalScrollId, ScrollFrameInfo>;
/// An id that identifies coordinate systems in the ClipScrollTree. Each
/// coordinate system has an id and those ids will be shared when the coordinates
/// system are the same or are in the same axis-aligned space. This allows
/// for optimizing mask generation.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct CoordinateSystemId(pub u32);
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
-pub struct ClipScrollNodeIndex(pub usize);
+pub struct SpatialNodeIndex(pub usize);
+
+#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
+pub struct ClipNodeIndex(pub usize);
-// Used to index the smaller subset of nodes in the CST that define
-// new transform / positioning.
-// TODO(gw): In the future if we split the CST into a positioning and
-// clipping tree, this can be tidied up a bit.
-#[derive(Copy, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-pub struct TransformIndex(pub u32);
+impl SpatialNodeIndex {
+ pub fn transform_index(&self) -> TransformIndex {
+ TransformIndex(self.0 as u32)
+ }
+}
-const ROOT_REFERENCE_FRAME_INDEX: ClipScrollNodeIndex = ClipScrollNodeIndex(0);
-const TOPMOST_SCROLL_NODE_INDEX: ClipScrollNodeIndex = ClipScrollNodeIndex(1);
+const ROOT_REFERENCE_FRAME_INDEX: SpatialNodeIndex = SpatialNodeIndex(0);
+const TOPMOST_SCROLL_NODE_INDEX: SpatialNodeIndex = SpatialNodeIndex(1);
impl CoordinateSystemId {
pub fn root() -> Self {
CoordinateSystemId(0)
}
pub fn next(&self) -> Self {
let CoordinateSystemId(id) = *self;
@@ -54,55 +55,53 @@ impl CoordinateSystemId {
pub fn advance(&mut self) {
self.0 += 1;
}
}
pub struct ClipChainDescriptor {
pub index: ClipChainIndex,
pub parent: Option<ClipChainIndex>,
- pub clips: Vec<ClipScrollNodeIndex>,
+ pub clips: Vec<ClipNodeIndex>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ClipChainIndex(pub usize);
pub struct ClipScrollTree {
- pub nodes: Vec<ClipScrollNode>,
+ /// Nodes which determine the positions (offsets and transforms) for primitives
+ /// and clips.
+ pub spatial_nodes: Vec<SpatialNode>,
+
+ /// Nodes which clip primitives.
+ pub clip_nodes: Vec<ClipNode>,
/// A Vec of all descriptors that describe ClipChains in the order in which they are
/// encountered during display list flattening. ClipChains are expected to never be
/// the children of ClipChains later in the list.
pub clip_chains_descriptors: Vec<ClipChainDescriptor>,
/// A vector of all ClipChains in this ClipScrollTree including those from
/// ClipChainDescriptors and also those defined by the clipping node hierarchy.
pub clip_chains: Vec<ClipChain>,
pub pending_scroll_offsets: FastHashMap<ExternalScrollId, (LayoutPoint, ScrollClamping)>,
/// A set of pipelines which should be discarded the next time this
/// tree is drained.
pub pipelines_to_discard: FastHashSet<PipelineId>,
-
- /// The number of nodes in the CST that are spatial. Currently, this is all
- /// nodes that are not clip nodes.
- spatial_node_count: usize,
}
#[derive(Clone)]
pub struct TransformUpdateState {
pub parent_reference_frame_transform: LayoutToWorldFastTransform,
pub parent_accumulated_scroll_offset: LayoutVector2D,
pub nearest_scrolling_ancestor_offset: LayoutVector2D,
pub nearest_scrolling_ancestor_viewport: LayoutRect,
- /// The index of the current parent's clip chain.
- pub parent_clip_chain_index: ClipChainIndex,
-
/// An id for keeping track of the axis-aligned space of this node. This is used in
/// order to to track what kinds of clip optimizations can be done for a particular
/// display list item, since optimizations can usually only be done among
/// coordinate systems which are relatively axis aligned.
pub current_coordinate_system_id: CoordinateSystemId,
/// Transform from the coordinate system that started this compatible coordinate system.
pub coordinate_system_relative_transform: LayoutFastTransform,
@@ -111,461 +110,433 @@ pub struct TransformUpdateState {
/// 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.
pub invertible: bool,
}
impl ClipScrollTree {
pub fn new() -> Self {
ClipScrollTree {
- nodes: Vec::new(),
+ spatial_nodes: Vec::new(),
+ clip_nodes: Vec::new(),
clip_chains_descriptors: Vec::new(),
clip_chains: vec![ClipChain::empty(&DeviceIntRect::zero())],
pending_scroll_offsets: FastHashMap::default(),
pipelines_to_discard: FastHashSet::default(),
- spatial_node_count: 0,
}
}
/// The root reference frame, which is the true root of the ClipScrollTree. Initially
- /// this ID is not valid, which is indicated by ```nodes``` being empty.
- pub fn root_reference_frame_index(&self) -> ClipScrollNodeIndex {
+ /// this ID is not valid, which is indicated by ```spatial_nodes``` being empty.
+ pub fn root_reference_frame_index(&self) -> SpatialNodeIndex {
// TODO(mrobinson): We should eventually make this impossible to misuse.
- debug_assert!(!self.nodes.is_empty());
+ debug_assert!(!self.spatial_nodes.is_empty());
ROOT_REFERENCE_FRAME_INDEX
}
/// The root scroll node which is the first child of the root reference frame.
- /// Initially this ID is not valid, which is indicated by ```nodes``` being empty.
- pub fn topmost_scroll_node_index(&self) -> ClipScrollNodeIndex {
+ /// Initially this ID is not valid, which is indicated by ```spatial_nodes``` being empty.
+ pub fn topmost_scroll_node_index(&self) -> SpatialNodeIndex {
// TODO(mrobinson): We should eventually make this impossible to misuse.
- debug_assert!(self.nodes.len() >= 1);
+ debug_assert!(self.spatial_nodes.len() >= 1);
TOPMOST_SCROLL_NODE_INDEX
}
pub fn get_scroll_node_state(&self) -> Vec<ScrollNodeState> {
let mut result = vec![];
- for node in &self.nodes {
- if let NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(info), .. } = node.node_type {
+ for node in &self.spatial_nodes {
+ if let SpatialNodeType::ScrollFrame(info) = node.node_type {
if let Some(id) = info.external_id {
result.push(ScrollNodeState { id, scroll_offset: info.offset })
}
}
}
result
}
pub fn drain(&mut self) -> ScrollStates {
let mut scroll_states = FastHashMap::default();
- for old_node in &mut self.nodes.drain(..) {
+ for old_node in &mut self.spatial_nodes.drain(..) {
if self.pipelines_to_discard.contains(&old_node.pipeline_id) {
continue;
}
match old_node.node_type {
- NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(info), .. } if info.external_id.is_some() => {
+ SpatialNodeType::ScrollFrame(info) if info.external_id.is_some() => {
scroll_states.insert(info.external_id.unwrap(), info);
}
_ => {}
}
}
- self.spatial_node_count = 0;
+ self.clip_nodes.clear();
self.pipelines_to_discard.clear();
self.clip_chains = vec![ClipChain::empty(&DeviceIntRect::zero())];
self.clip_chains_descriptors.clear();
scroll_states
}
pub fn scroll_node(
&mut self,
origin: LayoutPoint,
id: ExternalScrollId,
clamp: ScrollClamping
) -> bool {
- for node in &mut self.nodes {
+ for node in &mut self.spatial_nodes {
if node.matches_external_id(id) {
return node.set_scroll_origin(&origin, clamp);
}
}
self.pending_scroll_offsets.insert(id, (origin, clamp));
false
}
fn find_nearest_scrolling_ancestor(
&self,
- index: Option<ClipScrollNodeIndex>
- ) -> ClipScrollNodeIndex {
+ index: Option<SpatialNodeIndex>
+ ) -> SpatialNodeIndex {
let index = match index {
Some(index) => index,
None => return self.topmost_scroll_node_index(),
};
- let node = &self.nodes[index.0];
+ let node = &self.spatial_nodes[index.0];
match node.node_type {
- NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(state), .. } if state.sensitive_to_input_events() => index,
+ SpatialNodeType::ScrollFrame(state) if state.sensitive_to_input_events() => index,
_ => self.find_nearest_scrolling_ancestor(node.parent)
}
}
pub fn scroll_nearest_scrolling_ancestor(
&mut self,
scroll_location: ScrollLocation,
- node_index: Option<ClipScrollNodeIndex>,
+ node_index: Option<SpatialNodeIndex>,
) -> bool {
- if self.nodes.is_empty() {
+ if self.spatial_nodes.is_empty() {
return false;
}
let node_index = self.find_nearest_scrolling_ancestor(node_index);
- self.nodes[node_index.0].scroll(scroll_location)
+ self.spatial_nodes[node_index.0].scroll(scroll_location)
}
pub fn update_tree(
&mut self,
screen_rect: &DeviceIntRect,
device_pixel_scale: DevicePixelScale,
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
pan: WorldPoint,
scene_properties: &SceneProperties,
) -> TransformPalette {
- let mut transform_palette = TransformPalette::new(self.spatial_node_count);
+ let mut transform_palette = TransformPalette::new(self.spatial_nodes.len());
+ if self.spatial_nodes.is_empty() {
+ return transform_palette;
+ }
- if !self.nodes.is_empty() {
- self.clip_chains[0] = ClipChain::empty(screen_rect);
+ self.clip_chains[0] = ClipChain::empty(screen_rect);
- let root_reference_frame_index = self.root_reference_frame_index();
- let mut state = TransformUpdateState {
- parent_reference_frame_transform: LayoutVector2D::new(pan.x, pan.y).into(),
- parent_accumulated_scroll_offset: LayoutVector2D::zero(),
- nearest_scrolling_ancestor_offset: LayoutVector2D::zero(),
- nearest_scrolling_ancestor_viewport: LayoutRect::zero(),
- parent_clip_chain_index: ClipChainIndex(0),
- current_coordinate_system_id: CoordinateSystemId::root(),
- coordinate_system_relative_transform: LayoutFastTransform::identity(),
- invertible: true,
- };
- let mut next_coordinate_system_id = state.current_coordinate_system_id.next();
- self.update_node(
- root_reference_frame_index,
- &mut state,
- &mut next_coordinate_system_id,
+ let root_reference_frame_index = self.root_reference_frame_index();
+ let mut state = TransformUpdateState {
+ parent_reference_frame_transform: LayoutVector2D::new(pan.x, pan.y).into(),
+ parent_accumulated_scroll_offset: LayoutVector2D::zero(),
+ nearest_scrolling_ancestor_offset: LayoutVector2D::zero(),
+ nearest_scrolling_ancestor_viewport: LayoutRect::zero(),
+ current_coordinate_system_id: CoordinateSystemId::root(),
+ coordinate_system_relative_transform: LayoutFastTransform::identity(),
+ invertible: true,
+ };
+
+ let mut next_coordinate_system_id = state.current_coordinate_system_id.next();
+ self.update_node(
+ root_reference_frame_index,
+ &mut state,
+ &mut next_coordinate_system_id,
+ &mut transform_palette,
+ scene_properties,
+ );
+
+ for clip_node in &mut self.clip_nodes {
+ let spatial_node = &self.spatial_nodes[clip_node.spatial_node.0];
+ clip_node.update(
+ spatial_node,
device_pixel_scale,
clip_store,
resource_cache,
gpu_cache,
- &mut transform_palette,
- scene_properties,
+ &mut self.clip_chains,
);
-
- self.build_clip_chains(screen_rect);
}
+ self.build_clip_chains(screen_rect);
transform_palette
}
fn update_node(
&mut self,
- node_index: ClipScrollNodeIndex,
+ node_index: SpatialNodeIndex,
state: &mut TransformUpdateState,
next_coordinate_system_id: &mut CoordinateSystemId,
- device_pixel_scale: DevicePixelScale,
- clip_store: &mut ClipStore,
- resource_cache: &mut ResourceCache,
- gpu_cache: &mut GpuCache,
transform_palette: &mut TransformPalette,
scene_properties: &SceneProperties,
) {
// TODO(gw): This is an ugly borrow check workaround to clone these.
// Restructure this to avoid the clones!
let mut state = state.clone();
let node_children = {
- let node = match self.nodes.get_mut(node_index.0) {
+ let node = match self.spatial_nodes.get_mut(node_index.0) {
Some(node) => node,
None => return,
};
- node.update(
- &mut state,
- next_coordinate_system_id,
- device_pixel_scale,
- clip_store,
- resource_cache,
- gpu_cache,
- scene_properties,
- &mut self.clip_chains,
- );
-
- node.push_gpu_data(transform_palette);
+ node.update(&mut state, next_coordinate_system_id, scene_properties);
+ node.push_gpu_data(transform_palette, node_index);
if node.children.is_empty() {
return;
}
node.prepare_state_for_children(&mut state);
node.children.clone()
};
for child_node_index in node_children {
self.update_node(
child_node_index,
&mut state,
next_coordinate_system_id,
- device_pixel_scale,
- clip_store,
- resource_cache,
- gpu_cache,
transform_palette,
scene_properties,
);
}
}
pub fn build_clip_chains(&mut self, screen_rect: &DeviceIntRect) {
for descriptor in &self.clip_chains_descriptors {
// A ClipChain is an optional parent (which is another ClipChain) and a list of
- // ClipScrollNode clipping nodes. Here we start the ClipChain with a clone of the
+ // SpatialNode clipping nodes. Here we start the ClipChain with a clone of the
// parent's node, if necessary.
let mut chain = match descriptor.parent {
Some(index) => self.clip_chains[index.0].clone(),
None => ClipChain::empty(screen_rect),
};
- // Now we walk through each ClipScrollNode in the vector of clip nodes and
- // extract their ClipChain nodes to construct the final list.
+ // Now we walk through each ClipNode in the vector and extract their ClipChain nodes to
+ // construct the final list.
for clip_index in &descriptor.clips {
- match self.nodes[clip_index.0].node_type {
- NodeType::Clip { clip_chain_node: Some(ref node), .. } => {
+ match self.clip_nodes[clip_index.0] {
+ ClipNode { clip_chain_node: Some(ref node), .. } => {
chain.add_node(node.clone());
}
- NodeType::Clip { .. } => warn!("Found uninitialized clipping ClipScrollNode."),
- _ => warn!("Tried to create a clip chain with non-clipping node."),
+ ClipNode { .. } => warn!("Found uninitialized clipping ClipNode."),
};
}
chain.parent_index = descriptor.parent;
self.clip_chains[descriptor.index.0] = chain;
}
}
pub fn finalize_and_apply_pending_scroll_offsets(&mut self, old_states: ScrollStates) {
- for node in &mut self.nodes {
+ for node in &mut self.spatial_nodes {
let external_id = match node.node_type {
- NodeType::Spatial { kind: SpatialNodeKind::ScrollFrame(ScrollFrameInfo { external_id: Some(id), ..} ), .. } => id,
+ SpatialNodeType::ScrollFrame(ScrollFrameInfo { external_id: Some(id), ..} ) => id,
_ => continue,
};
if let Some(scrolling_state) = old_states.get(&external_id) {
node.apply_old_scrolling_state(scrolling_state);
}
if let Some((offset, clamping)) = self.pending_scroll_offsets.remove(&external_id) {
node.set_scroll_origin(&offset, clamping);
}
}
}
- // Generate the next valid TransformIndex for the CST.
- fn next_transform_index(&mut self) -> TransformIndex {
- let transform_index = TransformIndex(self.spatial_node_count as u32);
- self.spatial_node_count += 1;
- transform_index
- }
-
pub fn add_clip_node(
&mut self,
- index: ClipScrollNodeIndex,
- parent_index: ClipScrollNodeIndex,
+ index: ClipNodeIndex,
+ parent_clip_chain_index: ClipChainIndex,
+ spatial_node: SpatialNodeIndex,
handle: ClipSourcesHandle,
- pipeline_id: PipelineId,
) -> ClipChainIndex {
let clip_chain_index = self.allocate_clip_chain();
- let transform_index = self.nodes[parent_index.0].transform_index;
-
- let node_type = NodeType::Clip {
- handle,
+ let node = ClipNode {
+ parent_clip_chain_index,
+ spatial_node,
+ handle: Some(handle),
clip_chain_index,
clip_chain_node: None,
};
- let node = ClipScrollNode::new(
- pipeline_id,
- Some(parent_index),
- node_type,
- transform_index,
- );
- self.add_node(node, index);
+ self.push_clip_node(node, index);
clip_chain_index
}
pub fn add_scroll_frame(
&mut self,
- index: ClipScrollNodeIndex,
- parent_index: ClipScrollNodeIndex,
+ index: SpatialNodeIndex,
+ parent_index: SpatialNodeIndex,
external_id: Option<ExternalScrollId>,
pipeline_id: PipelineId,
frame_rect: &LayoutRect,
content_size: &LayoutSize,
scroll_sensitivity: ScrollSensitivity,
) {
- let node = ClipScrollNode::new_scroll_frame(
+ let node = SpatialNode::new_scroll_frame(
pipeline_id,
parent_index,
external_id,
frame_rect,
content_size,
scroll_sensitivity,
- self.next_transform_index(),
);
- self.add_node(node, index);
+ self.add_spatial_node(node, index);
}
pub fn add_reference_frame(
&mut self,
- index: ClipScrollNodeIndex,
- parent_index: Option<ClipScrollNodeIndex>,
+ index: SpatialNodeIndex,
+ parent_index: Option<SpatialNodeIndex>,
source_transform: Option<PropertyBinding<LayoutTransform>>,
source_perspective: Option<LayoutTransform>,
origin_in_parent_reference_frame: LayoutVector2D,
pipeline_id: PipelineId,
) {
- let node = ClipScrollNode::new_reference_frame(
+ let node = SpatialNode::new_reference_frame(
parent_index,
source_transform,
source_perspective,
origin_in_parent_reference_frame,
pipeline_id,
- self.next_transform_index(),
);
- self.add_node(node, index);
+ self.add_spatial_node(node, index);
}
pub fn add_sticky_frame(
&mut self,
- index: ClipScrollNodeIndex,
- parent_index: ClipScrollNodeIndex,
+ index: SpatialNodeIndex,
+ parent_index: SpatialNodeIndex,
sticky_frame_info: StickyFrameInfo,
pipeline_id: PipelineId,
) {
- let node = ClipScrollNode::new_sticky_frame(
+ let node = SpatialNode::new_sticky_frame(
parent_index,
sticky_frame_info,
pipeline_id,
- self.next_transform_index(),
);
- self.add_node(node, index);
+ self.add_spatial_node(node, index);
}
pub fn add_clip_chain_descriptor(
&mut self,
parent: Option<ClipChainIndex>,
- clips: Vec<ClipScrollNodeIndex>
+ clips: Vec<ClipNodeIndex>
) -> ClipChainIndex {
let index = self.allocate_clip_chain();
self.clip_chains_descriptors.push(ClipChainDescriptor { index, parent, clips });
index
}
- pub fn add_node(&mut self, node: ClipScrollNode, index: ClipScrollNodeIndex) {
- // When the parent node is None this means we are adding the root.
- if let Some(parent_index) = node.parent {
- self.nodes[parent_index.0].add_child(index);
- }
-
- if index.0 == self.nodes.len() {
- self.nodes.push(node);
+ pub fn push_clip_node(&mut self, node: ClipNode, index: ClipNodeIndex) {
+ if index.0 == self.clip_nodes.len() {
+ self.clip_nodes.push(node);
return;
}
-
- if let Some(empty_node) = self.nodes.get_mut(index.0) {
+ if let Some(empty_node) = self.clip_nodes.get_mut(index.0) {
*empty_node = node;
return
}
- let length_to_reserve = index.0 + 1 - self.nodes.len();
- self.nodes.reserve_exact(length_to_reserve);
+ let length_to_reserve = index.0 + 1 - self.clip_nodes.len();
+ self.clip_nodes.reserve_exact(length_to_reserve);
// We would like to use `Vec::resize` here, but the Clone trait is not supported
- // for ClipScrollNodes. We can fix this either by splitting the clip nodes out into
- // their own tree or when support is added for something like `Vec::resize_default`.
- let length_to_extend = self.nodes.len() .. index.0;
- self.nodes.extend(length_to_extend.map(|_| ClipScrollNode::empty()));
+ // for ClipNodes. We can fix this either when support is added for something like
+ // `Vec::resize_default`.
+ let length_to_extend = self.clip_nodes.len() .. index.0;
+ self.clip_nodes.extend(length_to_extend.map(|_| ClipNode::empty()));
+ self.clip_nodes.push(node);
+ }
+
+ pub fn add_spatial_node(&mut self, node: SpatialNode, index: SpatialNodeIndex) {
+ // When the parent node is None this means we are adding the root.
+ if let Some(parent_index) = node.parent {
+ self.spatial_nodes[parent_index.0].add_child(index);
+ }
- self.nodes.push(node);
+ if index.0 == self.spatial_nodes.len() {
+ self.spatial_nodes.push(node);
+ return;
+ }
+
+ if let Some(empty_node) = self.spatial_nodes.get_mut(index.0) {
+ *empty_node = node;
+ return
+ }
+
+ debug_assert!(index.0 > self.spatial_nodes.len() - 1);
+ self.spatial_nodes.resize(index.0, SpatialNode::empty());
+ self.spatial_nodes.push(node);
}
pub fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
self.pipelines_to_discard.insert(pipeline_id);
}
fn print_node<T: PrintTreePrinter>(
&self,
- index: ClipScrollNodeIndex,
+ index: SpatialNodeIndex,
pt: &mut T,
clip_store: &ClipStore
) {
- let node = &self.nodes[index.0];
+ let node = &self.spatial_nodes[index.0];
match node.node_type {
- NodeType::Spatial { ref kind, .. } => {
- match *kind {
- SpatialNodeKind::StickyFrame(ref sticky_frame_info) => {
- pt.new_level(format!("StickyFrame"));
- pt.add_item(format!("index: {:?}", index));
- pt.add_item(format!("sticky info: {:?}", sticky_frame_info));
- }
- SpatialNodeKind::ScrollFrame(scrolling_info) => {
- pt.new_level(format!("ScrollFrame"));
- pt.add_item(format!("index: {:?}", index));
- pt.add_item(format!("viewport: {:?}", scrolling_info.viewport_rect));
- pt.add_item(format!("scrollable_size: {:?}", scrolling_info.scrollable_size));
- pt.add_item(format!("scroll offset: {:?}", scrolling_info.offset));
- }
- SpatialNodeKind::ReferenceFrame(ref info) => {
- pt.new_level(format!("ReferenceFrame {:?}", info.resolved_transform));
- pt.add_item(format!("index: {:?}", index));
- }
- }
+ SpatialNodeType::StickyFrame(ref sticky_frame_info) => {
+ pt.new_level(format!("StickyFrame"));
+ pt.add_item(format!("index: {:?}", index));
+ pt.add_item(format!("sticky info: {:?}", sticky_frame_info));
}
- NodeType::Clip { ref handle, .. } => {
- pt.new_level("Clip".to_owned());
-
+ SpatialNodeType::ScrollFrame(scrolling_info) => {
+ pt.new_level(format!("ScrollFrame"));
pt.add_item(format!("index: {:?}", index));
- let clips = clip_store.get(handle).clips();
- pt.new_level(format!("Clip Sources [{}]", clips.len()));
- for source in clips {
- pt.add_item(format!("{:?}", source));
- }
- pt.end_level();
+ pt.add_item(format!("viewport: {:?}", scrolling_info.viewport_rect));
+ pt.add_item(format!("scrollable_size: {:?}", scrolling_info.scrollable_size));
+ pt.add_item(format!("scroll offset: {:?}", scrolling_info.offset));
}
- NodeType::Empty => unreachable!("Empty node remaining in ClipScrollTree."),
+ SpatialNodeType::ReferenceFrame(ref info) => {
+ pt.new_level(format!("ReferenceFrame {:?}", info.resolved_transform));
+ pt.add_item(format!("index: {:?}", index));
+ }
+ SpatialNodeType::Empty => unreachable!("Empty node remaining in ClipScrollTree."),
}
pt.add_item(format!("world_viewport_transform: {:?}", node.world_viewport_transform));
pt.add_item(format!("world_content_transform: {:?}", node.world_content_transform));
pt.add_item(format!("coordinate_system_id: {:?}", node.coordinate_system_id));
for child_index in &node.children {
self.print_node(*child_index, pt, clip_store);
}
pt.end_level();
}
#[allow(dead_code)]
pub fn print(&self, clip_store: &ClipStore) {
- if !self.nodes.is_empty() {
+ if !self.spatial_nodes.is_empty() {
let mut pt = PrintTree::new("clip_scroll tree");
self.print_with(clip_store, &mut pt);
}
}
pub fn print_with<T: PrintTreePrinter>(&self, clip_store: &ClipStore, pt: &mut T) {
- if !self.nodes.is_empty() {
+ if !self.spatial_nodes.is_empty() {
self.print_node(self.root_reference_frame_index(), pt, clip_store);
}
}
pub fn allocate_clip_chain(&mut self) -> ClipChainIndex {
debug_assert!(!self.clip_chains.is_empty());
let new_clip_chain =self.clip_chains[0].clone();
self.clip_chains.push(new_clip_chain);
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -9,18 +9,17 @@ use api::{DevicePixelScale, DeviceUintRe
use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, GlyphRasterSpace, GradientStop};
use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, LayoutPoint};
use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
use api::{LineOrientation, LineStyle, LocalClip, NinePatchBorderSource, PipelineId};
use api::{PropertyBinding, ReferenceFrame, RepeatMode, ScrollFrameDisplayItem, ScrollSensitivity};
use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect};
use api::{TransformStyle, YuvColorSpace, YuvData};
use clip::{ClipRegion, ClipSource, ClipSources, ClipStore};
-use clip_scroll_node::{NodeType, SpatialNodeKind, StickyFrameInfo};
-use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree};
+use clip_scroll_tree::{ClipChainIndex, ClipNodeIndex, ClipScrollTree, SpatialNodeIndex};
use euclid::vec2;
use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig};
use glyph_rasterizer::FontInstance;
use gpu_cache::GpuCacheHandle;
use gpu_types::BrushFlags;
use hit_test::{HitTestingItem, HitTestingRun};
use image::simplify_repeated_primitive;
use internal_types::{FastHashMap, FastHashSet};
@@ -28,55 +27,68 @@ use picture::PictureCompositeMode;
use prim_store::{BrushClipMaskKind, BrushKind, BrushPrimitive, BrushSegmentDescriptor};
use prim_store::{EdgeAaSegmentMask, ImageSource};
use prim_store::{BorderSource, BrushSegment, PictureIndex, PrimitiveContainer, PrimitiveIndex, PrimitiveStore};
use prim_store::{OpacityBinding, ScrollNodeAndClipChain, TextRunPrimitiveCpu};
use render_backend::{DocumentView};
use resource_cache::{FontInstanceMap, ImageRequest};
use scene::{Scene, ScenePipeline, StackingContextHelpers};
use scene_builder::{BuiltScene, SceneRequest};
+use spatial_node::{SpatialNodeType, StickyFrameInfo};
use std::{f32, mem, usize};
use tiling::{CompositeOps, ScrollbarPrimitive};
use util::{MaxRect, RectHelpers, recycle_vec};
static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF {
r: 0.3,
g: 0.3,
b: 0.3,
a: 0.6,
};
+#[derive(Clone, Copy)]
+pub struct PipelineOffset {
+ pipeline: PipelineId,
+ spatial_offset: usize,
+ clip_offset: usize,
+}
+
/// A data structure that keeps track of mapping between API ClipIds and the indices used
/// internally in the ClipScrollTree to avoid having to do HashMap lookups. ClipIdToIndexMapper is
-/// responsible for mapping both ClipId to ClipChainIndex and ClipId to ClipScrollNodeIndex. We
+/// responsible for mapping both ClipId to ClipChainIndex and ClipId to SpatialNodeIndex. We
/// also include two small LRU caches. Currently the caches are small (1 entry), but in the future
/// we could use uluru here to do something more involved.
#[derive(Default)]
pub struct ClipIdToIndexMapper {
/// A map which converts a ClipId for a clipping node or an API-defined ClipChain into
/// a ClipChainIndex, which is the index used internally in the ClipScrollTree to
/// identify ClipChains.
clip_chain_map: FastHashMap<ClipId, ClipChainIndex>,
/// The last mapped ClipChainIndex, used to avoid having to do lots of consecutive
/// HashMap lookups.
cached_clip_chain_index: Option<(ClipId, ClipChainIndex)>,
- /// The offset in the ClipScrollTree's array of ClipScrollNodes for a particular pipeline.
- /// This is used to convert a ClipId into a ClipScrollNodeIndex.
- pipeline_offsets: FastHashMap<PipelineId, usize>,
+ /// The offset in the ClipScrollTree's array of SpatialNodes and ClipNodes for a particular
+ /// pipeline. This is used to convert ClipIds into SpatialNodeIndex or ClipNodeIndex.
+ pipeline_offsets: FastHashMap<PipelineId, PipelineOffset>,
/// The last mapped pipeline offset for this mapper. This is used to avoid having to
/// consult `pipeline_offsets` repeatedly when flattening the display list.
- cached_pipeline_offset: Option<(PipelineId, usize)>,
+ cached_pipeline_offset: Option<PipelineOffset>,
- /// The next available pipeline offset for ClipScrollNodeIndex. When we encounter a pipeline
- /// we will use this value and increment it by the total number of ClipScrollNodes in the
+ /// The next available pipeline offset for ClipNodeIndex. When we encounter a pipeline
+ /// we will use this value and increment it by the total number of clip nodes in the
/// pipeline's display list.
- next_available_offset: usize,
+ next_available_clip_offset: usize,
+
+ /// The next available pipeline offset for SpatialNodeIndex. When we encounter a pipeline
+ /// we will use this value and increment it by the total number of spatial nodes in the
+ /// pipeline's display list.
+ next_available_spatial_offset: usize,
}
impl ClipIdToIndexMapper {
pub fn add_clip_chain(&mut self, id: ClipId, index: ClipChainIndex) {
debug_assert!(!self.clip_chain_map.contains_key(&id));
self.clip_chain_map.insert(id, index);
}
@@ -96,49 +108,56 @@ impl ClipIdToIndexMapper {
}
pub fn get_clip_chain_index_and_cache_result(&mut self, id: &ClipId) -> ClipChainIndex {
let index = self.get_clip_chain_index(id);
self.cached_clip_chain_index = Some((*id, index));
index
}
- pub fn map_clip_and_scroll(&mut self, info: &ClipAndScrollInfo) -> ScrollNodeAndClipChain {
- ScrollNodeAndClipChain::new(
- self.get_node_index(info.scroll_node_id),
- self.get_clip_chain_index_and_cache_result(&info.clip_node_id())
- )
- }
-
- pub fn simple_scroll_and_clip_chain(&mut self, id: &ClipId) -> ScrollNodeAndClipChain {
- self.map_clip_and_scroll(&ClipAndScrollInfo::simple(*id))
- }
-
pub fn initialize_for_pipeline(&mut self, pipeline: &ScenePipeline) {
debug_assert!(!self.pipeline_offsets.contains_key(&pipeline.pipeline_id));
- self.pipeline_offsets.insert(pipeline.pipeline_id, self.next_available_offset);
- self.next_available_offset += pipeline.display_list.total_clip_ids();
+ self.pipeline_offsets.insert(
+ pipeline.pipeline_id,
+ PipelineOffset {
+ pipeline: pipeline.pipeline_id,
+ spatial_offset: self.next_available_spatial_offset,
+ clip_offset: self.next_available_clip_offset,
+ }
+ );
+
+ self.next_available_clip_offset += pipeline.display_list.total_clip_nodes();
+ self.next_available_spatial_offset += pipeline.display_list.total_spatial_nodes();
}
- pub fn get_node_index(&mut self, id: ClipId) -> ClipScrollNodeIndex {
- let (index, pipeline_id) = match id {
- ClipId::Clip(index, pipeline_id) => (index, pipeline_id),
- ClipId::ClipChain(_) => panic!("Tried to use ClipChain as scroll node."),
- };
-
- let pipeline_offset = match self.cached_pipeline_offset {
- Some((last_used_id, offset)) if last_used_id == pipeline_id => offset,
+ pub fn get_pipeline_offet<'a>(&'a mut self, id: PipelineId) -> &'a PipelineOffset {
+ match self.cached_pipeline_offset {
+ Some(ref offset) if offset.pipeline == id => offset,
_ => {
- let offset = self.pipeline_offsets[&pipeline_id];
- self.cached_pipeline_offset = Some((pipeline_id, offset));
+ let offset = &self.pipeline_offsets[&id];
+ self.cached_pipeline_offset = Some(*offset);
offset
}
- };
+ }
+ }
- ClipScrollNodeIndex(pipeline_offset + index)
+ pub fn get_clip_node_index(&mut self, id: ClipId) -> ClipNodeIndex {
+ match id {
+ ClipId::Clip(index, pipeline_id) => {
+ let pipeline_offset = self.get_pipeline_offet(pipeline_id);
+ ClipNodeIndex(pipeline_offset.clip_offset + index)
+ }
+ ClipId::Spatial(..) => {
+ // We could theoretically map back to the containing clip node with the current
+ // design, but we will eventually fully separate out clipping from spatial nodes
+ // in the display list. We don't ever need to do this anyway.
+ panic!("Tried to use positioning node as clip node.");
+ }
+ ClipId::ClipChain(_) => panic!("Tried to use ClipChain as scroll node."),
+ }
}
}
/// A structure that converts a serialized display list into a form that WebRender
/// can use to later build a frame. This structure produces a FrameBuilder. Public
/// members are typically those that are destructured into the FrameBuilder.
pub struct DisplayListFlattener<'a> {
/// The scene that we are currently flattening.
@@ -155,17 +174,17 @@ pub struct DisplayListFlattener<'a> {
output_pipelines: &'a FastHashSet<PipelineId>,
/// The data structure that converting between ClipId and the various index
/// types that the ClipScrollTree uses.
id_to_index_mapper: ClipIdToIndexMapper,
/// A stack of scroll nodes used during display list processing to properly
/// parent new scroll nodes.
- reference_frame_stack: Vec<(ClipId, ClipScrollNodeIndex)>,
+ reference_frame_stack: Vec<(ClipId, SpatialNodeIndex)>,
/// A stack of stacking context properties.
sc_stack: Vec<FlattenedStackingContext>,
/// A stack of the current pictures.
picture_stack: Vec<PictureIndex>,
/// A stack of the currently active shadows
@@ -268,24 +287,22 @@ impl<'a> DisplayListFlattener<'a> {
if items.is_empty() {
return vec![];
}
self.scene.get_display_list_for_pipeline(pipeline_id).get(items).collect()
}
fn flatten_root(&mut self, pipeline: &'a ScenePipeline, frame_size: &LayoutSize) {
let pipeline_id = pipeline.pipeline_id;
- let reference_frame_info = self.id_to_index_mapper.simple_scroll_and_clip_chain(
- &ClipId::root_reference_frame(pipeline_id)
+ let reference_frame_info = self.simple_scroll_and_clip_chain(
+ &ClipId::root_reference_frame(pipeline_id),
);
let root_scroll_node = ClipId::root_scroll_node(pipeline_id);
- let scroll_frame_info = self.id_to_index_mapper.simple_scroll_and_clip_chain(
- &root_scroll_node,
- );
+ let scroll_frame_info = self.simple_scroll_and_clip_chain(&root_scroll_node);
self.push_stacking_context(
pipeline_id,
CompositeOps::default(),
TransformStyle::Flat,
true,
true,
root_scroll_node,
@@ -375,17 +392,17 @@ impl<'a> DisplayListFlattener<'a> {
let sticky_frame_info = StickyFrameInfo::new(
frame_rect,
info.margins,
info.vertical_offset_bounds,
info.horizontal_offset_bounds,
info.previously_applied_offset,
);
- let index = self.id_to_index_mapper.get_node_index(info.id);
+ let index = self.get_spatial_node_index_for_clip_id(info.id);
self.clip_scroll_tree.add_sticky_frame(
index,
clip_and_scroll.scroll_node_id, /* parent id */
sticky_frame_info,
info.id.pipeline_id(),
);
self.id_to_index_mapper.map_to_parent_clip_chain(info.id, parent_id);
}
@@ -402,17 +419,17 @@ impl<'a> DisplayListFlattener<'a> {
let clip_region = ClipRegion::create_for_clip_node(
*item.clip_rect(),
complex_clips,
info.image_mask,
reference_frame_relative_offset,
);
// Just use clip rectangle as the frame rect for this scroll frame.
// This is useful when calculating scroll extents for the
- // ClipScrollNode::scroll(..) API as well as for properly setting sticky
+ // SpatialNode::scroll(..) API as well as for properly setting sticky
// positioning offsets.
let frame_rect = item.clip_rect().translate(reference_frame_relative_offset);
let content_rect = item.rect().translate(reference_frame_relative_offset);
debug_assert!(info.clip_id != info.scroll_frame_id);
self.add_clip_node(info.clip_id, clip_and_scroll_ids.scroll_node_id, clip_region);
@@ -551,17 +568,17 @@ impl<'a> DisplayListFlattener<'a> {
fn flatten_item<'b>(
&'b mut self,
item: DisplayItemRef<'a, 'b>,
pipeline_id: PipelineId,
reference_frame_relative_offset: LayoutVector2D,
) -> Option<BuiltDisplayListIter<'a>> {
let clip_and_scroll_ids = item.clip_and_scroll();
- let clip_and_scroll = self.id_to_index_mapper.map_clip_and_scroll(&clip_and_scroll_ids);
+ let clip_and_scroll = self.map_clip_and_scroll(&clip_and_scroll_ids);
let prim_info = item.get_layout_primitive_info(&reference_frame_relative_offset);
match *item.item() {
SpecificDisplayItem::Image(ref info) => {
self.add_image(
clip_and_scroll,
&prim_info,
info.stretch_size,
@@ -712,17 +729,17 @@ impl<'a> DisplayListFlattener<'a> {
info.image_mask,
&reference_frame_relative_offset,
);
self.add_clip_node(info.id, clip_and_scroll_ids.scroll_node_id, clip_region);
}
SpecificDisplayItem::ClipChain(ref info) => {
let items = self.get_clip_chain_items(pipeline_id, item.clip_chain_items())
.iter()
- .map(|id| self.id_to_index_mapper.get_node_index(*id))
+ .map(|id| self.id_to_index_mapper.get_clip_node_index(*id))
.collect();
let parent = info.parent.map(|id|
self.id_to_index_mapper.get_clip_chain_index(&ClipId::ClipChain(id))
);
let clip_chain_index =
self.clip_scroll_tree.add_clip_chain_descriptor(parent, items);
self.id_to_index_mapper.add_clip_chain(ClipId::ClipChain(info.id), clip_chain_index);
},
@@ -878,26 +895,26 @@ impl<'a> DisplayListFlattener<'a> {
pub fn push_stacking_context(
&mut self,
pipeline_id: PipelineId,
composite_ops: CompositeOps,
transform_style: TransformStyle,
is_backface_visible: bool,
is_pipeline_root: bool,
- positioning_node: ClipId,
+ spatial_node: ClipId,
clipping_node: Option<ClipId>,
glyph_raster_space: GlyphRasterSpace,
) {
let clip_chain_id = match clipping_node {
Some(ref clipping_node) => self.id_to_index_mapper.get_clip_chain_index(clipping_node),
None => ClipChainIndex(0), // This means no clipping.
};
let clip_and_scroll = ScrollNodeAndClipChain::new(
- self.id_to_index_mapper.get_node_index(positioning_node),
+ self.get_spatial_node_index_for_clip_id(spatial_node),
clip_chain_id
);
// Construct the necessary set of Picture primitives
// to draw this stacking context.
let current_reference_frame_index = self.current_reference_frame_index();
// An arbitrary large clip rect. For now, we don't
@@ -1186,19 +1203,19 @@ impl<'a> DisplayListFlattener<'a> {
pub fn push_reference_frame(
&mut self,
reference_frame_id: ClipId,
parent_id: Option<ClipId>,
pipeline_id: PipelineId,
source_transform: Option<PropertyBinding<LayoutTransform>>,
source_perspective: Option<LayoutTransform>,
origin_in_parent_reference_frame: LayoutVector2D,
- ) -> ClipScrollNodeIndex {
- let index = self.id_to_index_mapper.get_node_index(reference_frame_id);
- let parent_index = parent_id.map(|id| self.id_to_index_mapper.get_node_index(id));
+ ) -> SpatialNodeIndex {
+ let index = self.get_spatial_node_index_for_clip_id(reference_frame_id);
+ let parent_index = parent_id.map(|id| self.get_spatial_node_index_for_clip_id(id));
self.clip_scroll_tree.add_reference_frame(
index,
parent_index,
source_transform,
source_perspective,
origin_in_parent_reference_frame,
pipeline_id,
);
@@ -1207,29 +1224,29 @@ impl<'a> DisplayListFlattener<'a> {
match parent_id {
Some(ref parent_id) =>
self.id_to_index_mapper.map_to_parent_clip_chain(reference_frame_id, parent_id),
_ => self.id_to_index_mapper.add_clip_chain(reference_frame_id, ClipChainIndex(0)),
}
index
}
- pub fn current_reference_frame_index(&self) -> ClipScrollNodeIndex {
+ pub fn current_reference_frame_index(&self) -> SpatialNodeIndex {
self.reference_frame_stack.last().unwrap().1
}
pub fn setup_viewport_offset(
&mut self,
inner_rect: DeviceUintRect,
device_pixel_scale: DevicePixelScale,
) {
let viewport_offset = (inner_rect.origin.to_vector().to_f32() / device_pixel_scale).round();
let root_id = self.clip_scroll_tree.root_reference_frame_index();
- let root_node = &mut self.clip_scroll_tree.nodes[root_id.0];
- if let NodeType::Spatial { kind: SpatialNodeKind::ReferenceFrame(ref mut info), .. } = root_node.node_type {
+ let root_node = &mut self.clip_scroll_tree.spatial_nodes[root_id.0];
+ if let SpatialNodeType::ReferenceFrame(ref mut info) = root_node.node_type {
info.resolved_transform =
LayoutVector2D::new(viewport_offset.x, viewport_offset.y).into();
}
}
pub fn push_root(
&mut self,
pipeline_id: PipelineId,
@@ -1256,45 +1273,48 @@ impl<'a> DisplayListFlattener<'a> {
);
}
pub fn add_clip_node(
&mut self,
new_node_id: ClipId,
parent_id: ClipId,
clip_region: ClipRegion,
- ) -> ClipScrollNodeIndex {
+ ) {
let clip_sources = ClipSources::from(clip_region);
let handle = self.clip_store.insert(clip_sources);
- let node_index = self.id_to_index_mapper.get_node_index(new_node_id);
+ let node_index = self.id_to_index_mapper.get_clip_node_index(new_node_id);
+ let parent_clip_chain_index =
+ self.id_to_index_mapper.get_clip_chain_index_and_cache_result(&parent_id);
+ let spatial_node = self.get_spatial_node_index_for_clip_id(parent_id);
let clip_chain_index = self.clip_scroll_tree.add_clip_node(
node_index,
- self.id_to_index_mapper.get_node_index(parent_id),
+ parent_clip_chain_index,
+ spatial_node,
handle,
- new_node_id.pipeline_id(),
);
self.id_to_index_mapper.add_clip_chain(new_node_id, clip_chain_index);
- node_index
}
pub fn add_scroll_frame(
&mut self,
new_node_id: ClipId,
parent_id: ClipId,
external_id: Option<ExternalScrollId>,
pipeline_id: PipelineId,
frame_rect: &LayoutRect,
content_size: &LayoutSize,
scroll_sensitivity: ScrollSensitivity,
- ) -> ClipScrollNodeIndex {
- let node_index = self.id_to_index_mapper.get_node_index(new_node_id);
+ ) -> SpatialNodeIndex {
+ let node_index = self.get_spatial_node_index_for_clip_id(new_node_id);
+ let parent_node_index = self.get_spatial_node_index_for_clip_id(parent_id);
self.clip_scroll_tree.add_scroll_frame(
node_index,
- self.id_to_index_mapper.get_node_index(parent_id),
+ parent_node_index,
external_id,
pipeline_id,
frame_rect,
content_size,
scroll_sensitivity,
);
self.id_to_index_mapper.map_to_parent_clip_chain(new_node_id, &parent_id);
node_index
@@ -1928,16 +1948,41 @@ impl<'a> DisplayListFlattener<'a> {
self.add_primitive(
clip_and_scroll,
info,
Vec::new(),
PrimitiveContainer::Brush(prim),
);
}
+
+ pub fn map_clip_and_scroll(&mut self, info: &ClipAndScrollInfo) -> ScrollNodeAndClipChain {
+ ScrollNodeAndClipChain::new(
+ self.get_spatial_node_index_for_clip_id(info.scroll_node_id),
+ self.id_to_index_mapper.get_clip_chain_index_and_cache_result(&info.clip_node_id())
+ )
+ }
+
+ pub fn simple_scroll_and_clip_chain(&mut self, id: &ClipId) -> ScrollNodeAndClipChain {
+ self.map_clip_and_scroll(&ClipAndScrollInfo::simple(*id))
+ }
+
+ pub fn get_spatial_node_index_for_clip_id(&mut self, id: ClipId,) -> SpatialNodeIndex {
+ match id {
+ ClipId::Spatial(index, pipeline_id) => {
+ let pipeline_offset = self.id_to_index_mapper.get_pipeline_offet(pipeline_id);
+ SpatialNodeIndex(pipeline_offset.spatial_offset + index)
+ }
+ ClipId::Clip(..) => {
+ let clip_node_index = self.id_to_index_mapper.get_clip_node_index(id);
+ self.clip_scroll_tree.clip_nodes[clip_node_index.0].spatial_node
+ }
+ ClipId::ClipChain(_) => panic!("Tried to use ClipChain as scroll node."),
+ }
+ }
}
pub fn build_scene(config: &FrameBuilderConfig, request: SceneRequest) -> BuiltScene {
let mut clip_scroll_tree = ClipScrollTree::new();
let mut new_scene = Scene::new();
let frame_builder = DisplayListFlattener::create_frame_builder(
@@ -1982,9 +2027,9 @@ struct FlattenedStackingContext {
/// If Some(..), this stacking context establishes a new
/// 3d rendering context, and the value is the picture
// index of the 3d context container.
rendering_context_3d_pic_index: Option<PictureIndex>,
}
#[derive(Debug)]
-pub struct ScrollbarInfo(pub ClipScrollNodeIndex, pub LayoutRect);
+pub struct ScrollbarInfo(pub SpatialNodeIndex, pub LayoutRect);
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -1,30 +1,30 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{BuiltDisplayList, ColorF, DeviceIntPoint, DeviceIntRect, DevicePixelScale};
use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentLayer, FontRenderMode};
use api::{LayoutPoint, LayoutRect, LayoutSize, PipelineId, WorldPoint};
use clip::{ClipChain, ClipStore};
-use clip_scroll_node::{ClipScrollNode};
-use clip_scroll_tree::{ClipScrollNodeIndex, ClipScrollTree};
+use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex};
use display_list_flattener::{DisplayListFlattener};
use gpu_cache::GpuCache;
-use gpu_types::{PrimitiveHeaders, TransformData, UvRectKind};
+use gpu_types::{PrimitiveHeaders, TransformData, TransformIndex, UvRectKind};
use hit_test::{HitTester, HitTestingRun};
use internal_types::{FastHashMap};
use picture::PictureSurface;
use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveStore};
use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
use render_backend::FrameId;
use render_task::{RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree};
use resource_cache::{ResourceCache};
use scene::{ScenePipeline, SceneProperties};
+use spatial_node::SpatialNode;
use std::{mem, f32};
use std::sync::Arc;
use tiling::{Frame, RenderPass, RenderPassKind, RenderTargetContext};
use tiling::{ScrollbarPrimitive, SpecialRenderPasses};
use util::{self, WorldToLayoutFastTransform};
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -83,17 +83,17 @@ pub struct FrameBuildingState<'a> {
pub resource_cache: &'a mut ResourceCache,
pub gpu_cache: &'a mut GpuCache,
pub special_render_passes: &'a mut SpecialRenderPasses,
}
pub struct PictureContext<'a> {
pub pipeline_id: PipelineId,
pub prim_runs: Vec<PrimitiveRun>,
- pub original_reference_frame_index: Option<ClipScrollNodeIndex>,
+ pub original_reference_frame_index: Option<SpatialNodeIndex>,
pub display_list: &'a BuiltDisplayList,
pub inv_world_transform: Option<WorldToLayoutFastTransform>,
pub apply_local_clip_rect: bool,
pub inflation_factor: f32,
pub allow_subpixel_aa: bool,
}
pub struct PictureState {
@@ -109,30 +109,33 @@ impl PictureState {
has_non_root_coord_system: false,
local_rect_changed: false,
}
}
}
pub struct PrimitiveRunContext<'a> {
pub clip_chain: &'a ClipChain,
- pub scroll_node: &'a ClipScrollNode,
+ pub scroll_node: &'a SpatialNode,
+ pub transform_index: TransformIndex,
pub local_clip_rect: LayoutRect,
}
impl<'a> PrimitiveRunContext<'a> {
pub fn new(
clip_chain: &'a ClipChain,
- scroll_node: &'a ClipScrollNode,
+ scroll_node: &'a SpatialNode,
+ transform_index: TransformIndex,
local_clip_rect: LayoutRect,
) -> Self {
PrimitiveRunContext {
clip_chain,
scroll_node,
local_clip_rect,
+ transform_index,
}
}
}
impl FrameBuilder {
pub fn empty() -> Self {
FrameBuilder {
hit_testing_runs: Vec::new(),
@@ -190,21 +193,21 @@ impl FrameBuilder {
) -> Option<RenderTaskId> {
profile_scope!("cull");
if self.prim_store.pictures.is_empty() {
return None
}
// The root picture is always the first one added.
- let root_clip_scroll_node =
- &clip_scroll_tree.nodes[clip_scroll_tree.root_reference_frame_index().0];
+ let root_spatial_node =
+ &clip_scroll_tree.spatial_nodes[clip_scroll_tree.root_reference_frame_index().0];
let display_list = &pipelines
- .get(&root_clip_scroll_node.pipeline_id)
+ .get(&root_spatial_node.pipeline_id)
.expect("No display list?")
.display_list;
const MAX_CLIP_COORD: f32 = 1.0e9;
let frame_context = FrameBuildingContext {
scene_id: self.scene_id,
device_pixel_scale,
@@ -224,17 +227,17 @@ impl FrameBuilder {
profile_counters,
clip_store: &mut self.clip_store,
resource_cache,
gpu_cache,
special_render_passes,
};
let pic_context = PictureContext {
- pipeline_id: root_clip_scroll_node.pipeline_id,
+ pipeline_id: root_spatial_node.pipeline_id,
prim_runs: mem::replace(&mut self.prim_store.pictures[0].runs, Vec::new()),
original_reference_frame_index: None,
display_list,
inv_world_transform: None,
apply_local_clip_rect: true,
inflation_factor: 0.0,
allow_subpixel_aa: true,
};
@@ -265,17 +268,17 @@ impl FrameBuilder {
Some(render_task_id)
}
fn update_scroll_bars(&mut self, clip_scroll_tree: &ClipScrollTree, gpu_cache: &mut GpuCache) {
static SCROLLBAR_PADDING: f32 = 8.0;
for scrollbar_prim in &self.scrollbar_prims {
let metadata = &mut self.prim_store.cpu_metadata[scrollbar_prim.prim_index.0];
- let scroll_frame = &clip_scroll_tree.nodes[scrollbar_prim.scroll_frame_index.0];
+ let scroll_frame = &clip_scroll_tree.spatial_nodes[scrollbar_prim.scroll_frame_index.0];
// Invalidate what's in the cache so it will get rebuilt.
gpu_cache.invalidate(&metadata.gpu_location);
let scrollable_distance = scroll_frame.scrollable_size().height;
if scrollable_distance <= 0.0 {
metadata.local_clip_rect.size = LayoutSize::zero();
continue;
--- a/gfx/webrender/src/gpu_types.rs
+++ b/gfx/webrender/src/gpu_types.rs
@@ -1,15 +1,14 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{DevicePoint, DeviceSize, DeviceRect, LayoutRect, LayoutToWorldTransform};
use api::{PremultipliedColorF, WorldToLayoutTransform};
-use clip_scroll_tree::TransformIndex;
use gpu_cache::{GpuCacheAddress, GpuDataRequest};
use prim_store::{EdgeAaSegmentMask};
use render_task::RenderTaskAddress;
use util::{MatrixHelpers, TransformedRectKind};
// Contains type that must exactly match the same structures declared in GLSL.
#[derive(Copy, Clone, Debug)]
@@ -395,16 +394,21 @@ pub struct TransformMetadata {
// the future, the transform palette will support
// specifying a coordinate system that the transform
// should be relative to.
pub struct TransformPalette {
pub transforms: Vec<TransformData>,
metadata: Vec<TransformMetadata>,
}
+#[derive(Copy, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct TransformIndex(pub u32);
+
impl TransformPalette {
pub fn new(spatial_node_count: usize) -> TransformPalette {
TransformPalette {
transforms: Vec::with_capacity(spatial_node_count),
metadata: Vec::with_capacity(spatial_node_count),
}
}
--- a/gfx/webrender/src/hit_test.rs
+++ b/gfx/webrender/src/hit_test.rs
@@ -1,48 +1,53 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{BorderRadius, ClipMode, HitTestFlags, HitTestItem, HitTestResult, ItemTag, LayoutPoint};
use api::{LayoutPrimitiveInfo, LayoutRect, PipelineId, WorldPoint};
use clip::{ClipSource, ClipStore, rounded_rectangle_contains_point};
-use clip_scroll_node::{ClipScrollNode, NodeType};
-use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree};
+use clip_node::ClipNode;
+use clip_scroll_tree::{ClipChainIndex, ClipNodeIndex, SpatialNodeIndex, ClipScrollTree};
use internal_types::FastHashMap;
use prim_store::ScrollNodeAndClipChain;
use util::LayoutToWorldFastTransform;
/// A copy of important clip scroll node data to use during hit testing. This a copy of
/// data from the ClipScrollTree that will persist as a new frame is under construction,
/// allowing hit tests consistent with the currently rendered frame.
-pub struct HitTestClipScrollNode {
+pub struct HitTestSpatialNode {
/// The pipeline id of this node.
pipeline_id: PipelineId,
- /// A particular point must be inside all of these regions to be considered clipped in
- /// for the purposes of a hit test.
- regions: Vec<HitTestRegion>,
-
/// World transform for content transformed by this node.
world_content_transform: LayoutToWorldFastTransform,
/// World viewport transform for content transformed by this node.
world_viewport_transform: LayoutToWorldFastTransform,
}
+pub struct HitTestClipNode {
+ /// The positioning node for this clip node.
+ spatial_node: SpatialNodeIndex,
+
+ /// A particular point must be inside all of these regions to be considered clipped in
+ /// for the purposes of a hit test.
+ regions: Vec<HitTestRegion>,
+}
+
/// A description of a clip chain in the HitTester. This is used to describe
/// hierarchical clip scroll nodes as well as ClipChains, so that they can be
/// handled the same way during hit testing. Once we represent all ClipChains
/// using ClipChainDescriptors, we can get rid of this and just use the
/// ClipChainDescriptor here.
#[derive(Clone)]
struct HitTestClipChainDescriptor {
parent: Option<ClipChainIndex>,
- clips: Vec<ClipScrollNodeIndex>,
+ clips: Vec<ClipNodeIndex>,
}
impl HitTestClipChainDescriptor {
fn empty() -> HitTestClipChainDescriptor {
HitTestClipChainDescriptor {
parent: None,
clips: Vec::new(),
}
@@ -88,69 +93,79 @@ impl HitTestRegion {
HitTestRegion::RoundedRectangle(rect, radii, ClipMode::ClipOut) =>
!rounded_rectangle_contains_point(point, &rect, &radii),
}
}
}
pub struct HitTester {
runs: Vec<HitTestingRun>,
- nodes: Vec<HitTestClipScrollNode>,
+ spatial_nodes: Vec<HitTestSpatialNode>,
+ clip_nodes: Vec<HitTestClipNode>,
clip_chains: Vec<HitTestClipChainDescriptor>,
- pipeline_root_nodes: FastHashMap<PipelineId, ClipScrollNodeIndex>,
+ pipeline_root_nodes: FastHashMap<PipelineId, SpatialNodeIndex>,
}
impl HitTester {
pub fn new(
runs: &Vec<HitTestingRun>,
clip_scroll_tree: &ClipScrollTree,
clip_store: &ClipStore
) -> HitTester {
let mut hit_tester = HitTester {
runs: runs.clone(),
- nodes: Vec::new(),
+ spatial_nodes: Vec::new(),
+ clip_nodes: Vec::new(),
clip_chains: Vec::new(),
pipeline_root_nodes: FastHashMap::default(),
};
hit_tester.read_clip_scroll_tree(clip_scroll_tree, clip_store);
hit_tester
}
fn read_clip_scroll_tree(
&mut self,
clip_scroll_tree: &ClipScrollTree,
clip_store: &ClipStore
) {
- self.nodes.clear();
+ self.spatial_nodes.clear();
self.clip_chains.clear();
self.clip_chains.resize(
clip_scroll_tree.clip_chains.len(),
HitTestClipChainDescriptor::empty()
);
- for (index, node) in clip_scroll_tree.nodes.iter().enumerate() {
- let index = ClipScrollNodeIndex(index);
+ for (index, node) in clip_scroll_tree.spatial_nodes.iter().enumerate() {
+ let index = SpatialNodeIndex(index);
// If we haven't already seen a node for this pipeline, record this one as the root
// node.
self.pipeline_root_nodes.entry(node.pipeline_id).or_insert(index);
- self.nodes.push(HitTestClipScrollNode {
+ self.spatial_nodes.push(HitTestSpatialNode {
pipeline_id: node.pipeline_id,
- regions: get_regions_for_clip_scroll_node(node, clip_store),
world_content_transform: node.world_content_transform,
world_viewport_transform: node.world_viewport_transform,
});
+ }
- if let NodeType::Clip { clip_chain_index, .. } = node.node_type {
- let clip_chain = self.clip_chains.get_mut(clip_chain_index.0).unwrap();
- clip_chain.parent =
- clip_scroll_tree.get_clip_chain(clip_chain_index).parent_index;
- clip_chain.clips = vec![index];
- }
+ for (index, node) in clip_scroll_tree.clip_nodes.iter().enumerate() {
+ let regions = match get_regions_for_clip_node(node, clip_store) {
+ Some(regions) => regions,
+ None => continue,
+ };
+ self.clip_nodes.push(HitTestClipNode {
+ spatial_node: node.spatial_node,
+ regions,
+ });
+
+ let clip_chain = self.clip_chains.get_mut(node.clip_chain_index.0).unwrap();
+ clip_chain.parent =
+ clip_scroll_tree.get_clip_chain(node.clip_chain_index).parent_index;
+ clip_chain.clips = vec![ClipNodeIndex(index)];
}
for descriptor in &clip_scroll_tree.clip_chains_descriptors {
let clip_chain = self.clip_chains.get_mut(descriptor.index.0).unwrap();
clip_chain.parent = clip_scroll_tree.get_clip_chain(descriptor.index).parent_index;
clip_chain.clips = descriptor.clips.clone();
}
}
@@ -172,38 +187,38 @@ impl HitTester {
};
if !parent_clipped_in {
test.set_in_clip_chain_cache(clip_chain_index, ClippedIn::NotClippedIn);
return false;
}
for clip_node_index in &descriptor.clips {
- if !self.is_point_clipped_in_for_node(point, *clip_node_index, test) {
+ if !self.is_point_clipped_in_for_clip_node(point, *clip_node_index, test) {
test.set_in_clip_chain_cache(clip_chain_index, ClippedIn::NotClippedIn);
return false;
}
}
test.set_in_clip_chain_cache(clip_chain_index, ClippedIn::ClippedIn);
true
}
- fn is_point_clipped_in_for_node(
+ fn is_point_clipped_in_for_clip_node(
&self,
point: WorldPoint,
- node_index: ClipScrollNodeIndex,
+ node_index: ClipNodeIndex,
test: &mut HitTest
) -> bool {
if let Some(clipped_in) = test.node_cache.get(&node_index) {
return *clipped_in == ClippedIn::ClippedIn;
}
- let node = &self.nodes[node_index.0];
- let transform = node.world_viewport_transform;
+ let node = &self.clip_nodes[node_index.0];
+ let transform = self.spatial_nodes[node.spatial_node.0].world_viewport_transform;
let transformed_point = match transform.inverse() {
Some(inverted) => inverted.transform_point2d(&point),
None => {
test.node_cache.insert(node_index, ClippedIn::NotClippedIn);
return false;
}
};
@@ -213,22 +228,22 @@ impl HitTester {
return false;
}
}
test.node_cache.insert(node_index, ClippedIn::ClippedIn);
true
}
- pub fn find_node_under_point(&self, mut test: HitTest) -> Option<ClipScrollNodeIndex> {
+ pub fn find_node_under_point(&self, mut test: HitTest) -> Option<SpatialNodeIndex> {
let point = test.get_absolute_point(self);
for &HitTestingRun(ref items, ref clip_and_scroll) in self.runs.iter().rev() {
let scroll_node_id = clip_and_scroll.scroll_node_id;
- let scroll_node = &self.nodes[scroll_node_id.0];
+ let scroll_node = &self.spatial_nodes[scroll_node_id.0];
let transform = scroll_node.world_content_transform;
let point_in_layer = match transform.inverse() {
Some(inverted) => inverted.transform_point2d(&point),
None => continue,
};
let mut clipped_in = false;
for item in items.iter().rev() {
@@ -252,17 +267,17 @@ impl HitTester {
}
pub fn hit_test(&self, mut test: HitTest) -> HitTestResult {
let point = test.get_absolute_point(self);
let mut result = HitTestResult::default();
for &HitTestingRun(ref items, ref clip_and_scroll) in self.runs.iter().rev() {
let scroll_node_id = clip_and_scroll.scroll_node_id;
- let scroll_node = &self.nodes[scroll_node_id.0];
+ let scroll_node = &self.spatial_nodes[scroll_node_id.0];
let pipeline_id = scroll_node.pipeline_id;
match (test.pipeline_id, pipeline_id) {
(Some(id), node_id) if node_id != id => continue,
_ => {},
}
let transform = scroll_node.world_content_transform;
let mut facing_backwards: Option<bool> = None; // will be computed on first use
@@ -291,17 +306,17 @@ impl HitTester {
continue;
}
}
// We need to calculate the position of the test point relative to the origin of
// the pipeline of the hit item. If we cannot get a transformed point, we are
// in a situation with an uninvertible transformation so we should just skip this
// result.
- let root_node = &self.nodes[self.pipeline_root_nodes[&pipeline_id].0];
+ let root_node = &self.spatial_nodes[self.pipeline_root_nodes[&pipeline_id].0];
let point_in_viewport = match root_node.world_viewport_transform.inverse() {
Some(inverted) => inverted.transform_point2d(&point),
None => continue,
};
result.items.push(HitTestItem {
pipeline: pipeline_id,
tag: item.tag,
@@ -313,55 +328,59 @@ impl HitTester {
}
}
}
result.items.dedup();
result
}
- pub fn get_pipeline_root(&self, pipeline_id: PipelineId) -> &HitTestClipScrollNode {
- &self.nodes[self.pipeline_root_nodes[&pipeline_id].0]
+ pub fn get_pipeline_root(&self, pipeline_id: PipelineId) -> &HitTestSpatialNode {
+ &self.spatial_nodes[self.pipeline_root_nodes[&pipeline_id].0]
}
}
-fn get_regions_for_clip_scroll_node(
- node: &ClipScrollNode,
+fn get_regions_for_clip_node(
+ node: &ClipNode,
clip_store: &ClipStore
-) -> Vec<HitTestRegion> {
- let clips = match node.node_type {
- NodeType::Clip{ ref handle, .. } => clip_store.get(handle).clips(),
- _ => return Vec::new(),
+) -> Option<Vec<HitTestRegion>> {
+ let handle = match node.handle.as_ref() {
+ Some(handle) => handle,
+ None => {
+ warn!("Encountered an empty clip node unexpectedly.");
+ return None;
+ }
};
- clips.iter().map(|source| {
+ let clips = clip_store.get(handle).clips();
+ Some(clips.iter().map(|source| {
match source.0 {
ClipSource::Rectangle(ref rect, mode) => HitTestRegion::Rectangle(*rect, mode),
ClipSource::RoundedRectangle(ref rect, ref radii, ref mode) =>
HitTestRegion::RoundedRectangle(*rect, *radii, *mode),
ClipSource::Image(ref mask) => HitTestRegion::Rectangle(mask.rect, ClipMode::Clip),
ClipSource::LineDecoration(_) |
ClipSource::BoxShadow(_) => {
unreachable!("Didn't expect to hit test against BorderCorner / BoxShadow / LineDecoration");
}
}
- }).collect()
+ }).collect())
}
#[derive(Clone, Copy, PartialEq)]
enum ClippedIn {
ClippedIn,
NotClippedIn,
}
pub struct HitTest {
pipeline_id: Option<PipelineId>,
point: WorldPoint,
flags: HitTestFlags,
- node_cache: FastHashMap<ClipScrollNodeIndex, ClippedIn>,
+ node_cache: FastHashMap<ClipNodeIndex, ClippedIn>,
clip_chain_cache: Vec<Option<ClippedIn>>,
}
impl HitTest {
pub fn new(
pipeline_id: Option<PipelineId>,
point: WorldPoint,
flags: HitTestFlags,
--- a/gfx/webrender/src/lib.rs
+++ b/gfx/webrender/src/lib.rs
@@ -55,17 +55,17 @@ extern crate serde;
extern crate thread_profiler;
mod batch;
mod border;
mod box_shadow;
#[cfg(any(feature = "capture", feature = "replay"))]
mod capture;
mod clip;
-mod clip_scroll_node;
+mod clip_node;
mod clip_scroll_tree;
mod debug_colors;
#[cfg(feature = "debug_renderer")]
mod debug_font_data;
#[cfg(feature = "debug_renderer")]
mod debug_render;
#[cfg(feature = "debugger")]
mod debug_server;
@@ -93,16 +93,17 @@ mod record;
mod render_backend;
mod render_task;
mod renderer;
mod resource_cache;
mod scene;
mod scene_builder;
mod segment;
mod shade;
+mod spatial_node;
mod texture_allocator;
mod texture_cache;
mod tiling;
mod util;
mod shader_source {
include!(concat!(env!("OUT_DIR"), "/shaders.rs"));
}
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -1,18 +1,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{DeviceRect, FilterOp, MixBlendMode, PipelineId, PremultipliedColorF};
use api::{DeviceIntRect, DeviceIntSize, DevicePoint, LayoutPoint, LayoutRect};
use api::{DevicePixelScale, PictureIntPoint, PictureIntRect, PictureIntSize};
use box_shadow::{BLUR_SAMPLE_SCALE};
-use clip_scroll_node::ClipScrollNode;
-use clip_scroll_tree::ClipScrollNodeIndex;
+use spatial_node::SpatialNode;
+use clip_scroll_tree::SpatialNodeIndex;
use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PrimitiveRunContext};
use gpu_cache::{GpuCacheHandle};
use gpu_types::UvRectKind;
use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect};
use prim_store::{PrimitiveMetadata, ScrollNodeAndClipChain};
use render_task::{ClearMode, RenderTask, RenderTaskCacheEntryHandle};
use render_task::{RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation};
use scene::{FilterOpHelpers, SceneProperties};
@@ -145,17 +145,17 @@ pub struct PicturePrimitive {
pub is_in_3d_context: bool,
// If requested as a frame output (for rendering
// pages to a texture), this is the pipeline this
// picture is the root of.
pub frame_output_pipeline_id: Option<PipelineId>,
// The original reference frame ID for this picture.
// It is only different if this is part of a 3D
// rendering context.
- pub reference_frame_index: ClipScrollNodeIndex,
+ pub reference_frame_index: SpatialNodeIndex,
pub real_local_rect: LayoutRect,
// An optional cache handle for storing extra data
// in the GPU cache, depending on the type of
// picture.
pub extra_gpu_data_handle: GpuCacheHandle,
// Unique identifier for this picture.
pub id: PictureId,
@@ -178,17 +178,17 @@ impl PicturePrimitive {
}
}
pub fn new_image(
id: PictureId,
composite_mode: Option<PictureCompositeMode>,
is_in_3d_context: bool,
pipeline_id: PipelineId,
- reference_frame_index: ClipScrollNodeIndex,
+ reference_frame_index: SpatialNodeIndex,
frame_output_pipeline_id: Option<PipelineId>,
apply_local_clip_rect: bool,
) -> Self {
PicturePrimitive {
runs: Vec::new(),
surface: None,
secondary_render_task_id: None,
composite_mode,
@@ -585,72 +585,72 @@ impl PicturePrimitive {
}
}
}
}
// Calculate a single screen-space UV for a picture.
fn calculate_screen_uv(
local_pos: &LayoutPoint,
- clip_scroll_node: &ClipScrollNode,
+ spatial_node: &SpatialNode,
rendered_rect: &DeviceRect,
device_pixel_scale: DevicePixelScale,
) -> DevicePoint {
- let world_pos = clip_scroll_node
+ let world_pos = spatial_node
.world_content_transform
.transform_point2d(local_pos);
let mut device_pos = world_pos * device_pixel_scale;
// Apply snapping for axis-aligned scroll nodes, as per prim_shared.glsl.
- if clip_scroll_node.transform_kind == TransformedRectKind::AxisAligned {
+ if spatial_node.transform_kind == TransformedRectKind::AxisAligned {
device_pos.x = (device_pos.x + 0.5).floor();
device_pos.y = (device_pos.y + 0.5).floor();
}
DevicePoint::new(
(device_pos.x - rendered_rect.origin.x) / rendered_rect.size.width,
(device_pos.y - rendered_rect.origin.y) / rendered_rect.size.height,
)
}
// Calculate a UV rect within an image based on the screen space
// vertex positions of a picture.
fn calculate_uv_rect_kind(
local_rect: &LayoutRect,
- clip_scroll_node: &ClipScrollNode,
+ spatial_node: &SpatialNode,
rendered_rect: &DeviceIntRect,
device_pixel_scale: DevicePixelScale,
) -> UvRectKind {
let rendered_rect = rendered_rect.to_f32();
let top_left = calculate_screen_uv(
&local_rect.origin,
- clip_scroll_node,
+ spatial_node,
&rendered_rect,
device_pixel_scale,
);
let top_right = calculate_screen_uv(
&local_rect.top_right(),
- clip_scroll_node,
+ spatial_node,
&rendered_rect,
device_pixel_scale,
);
let bottom_left = calculate_screen_uv(
&local_rect.bottom_left(),
- clip_scroll_node,
+ spatial_node,
&rendered_rect,
device_pixel_scale,
);
let bottom_right = calculate_screen_uv(
&local_rect.bottom_right(),
- clip_scroll_node,
+ spatial_node,
&rendered_rect,
device_pixel_scale,
);
UvRectKind::Quad {
top_left,
top_right,
bottom_left,
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -182,69 +182,69 @@ fn new_ct_font_with_variations(cg_font:
let axes: CFArray<CFDictionary> = TCFType::wrap_under_create_rule(axes_ref);
let mut vals: Vec<(CFString, CFNumber)> = Vec::with_capacity(variations.len() as usize);
for axis in axes.iter() {
if !axis.instance_of::<CFDictionary>() {
return ct_font;
}
let tag_val = match axis.find(kCTFontVariationAxisIdentifierKey as *const _) {
Some(tag_ptr) => {
- let tag: CFNumber = TCFType::wrap_under_get_rule(tag_ptr as CFNumberRef);
+ let tag: CFNumber = TCFType::wrap_under_get_rule(*tag_ptr as CFNumberRef);
if !tag.instance_of::<CFNumber>() {
return ct_font;
}
match tag.to_i64() {
Some(val) => val,
None => return ct_font,
}
}
None => return ct_font,
};
let mut val = match variations.iter().find(|variation| (variation.tag as i64) == tag_val) {
Some(variation) => variation.value as f64,
None => continue,
};
let name: CFString = match axis.find(kCTFontVariationAxisNameKey as *const _) {
- Some(name_ptr) => TCFType::wrap_under_get_rule(name_ptr as CFStringRef),
+ Some(name_ptr) => TCFType::wrap_under_get_rule(*name_ptr as CFStringRef),
None => return ct_font,
};
if !name.instance_of::<CFString>() {
return ct_font;
}
let min_val = match axis.find(kCTFontVariationAxisMinimumValueKey as *const _) {
Some(min_ptr) => {
- let min: CFNumber = TCFType::wrap_under_get_rule(min_ptr as CFNumberRef);
+ let min: CFNumber = TCFType::wrap_under_get_rule(*min_ptr as CFNumberRef);
if !min.instance_of::<CFNumber>() {
return ct_font;
}
match min.to_f64() {
Some(val) => val,
None => return ct_font,
}
}
None => return ct_font,
};
let max_val = match axis.find(kCTFontVariationAxisMaximumValueKey as *const _) {
Some(max_ptr) => {
- let max: CFNumber = TCFType::wrap_under_get_rule(max_ptr as CFNumberRef);
+ let max: CFNumber = TCFType::wrap_under_get_rule(*max_ptr as CFNumberRef);
if !max.instance_of::<CFNumber>() {
return ct_font;
}
match max.to_f64() {
Some(val) => val,
None => return ct_font,
}
}
None => return ct_font,
};
let def_val = match axis.find(kCTFontVariationAxisDefaultValueKey as *const _) {
Some(def_ptr) => {
- let def: CFNumber = TCFType::wrap_under_get_rule(def_ptr as CFNumberRef);
+ let def: CFNumber = TCFType::wrap_under_get_rule(*def_ptr as CFNumberRef);
if !def.instance_of::<CFNumber>() {
return ct_font;
}
match def.to_f64() {
Some(val) => val,
None => return ct_font,
}
}
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -6,18 +6,17 @@ use api::{AlphaType, BorderRadius, BoxSh
use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, ExtendMode};
use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, TileOffset};
use api::{GlyphRasterSpace, LayoutPoint, LayoutRect, LayoutSize, LayoutToWorldTransform, LayoutVector2D};
use api::{PipelineId, PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat, DeviceIntSideOffsets};
use api::{BorderWidths, LayoutToWorldScale, NormalBorder};
use app_units::Au;
use border::{BorderCacheKey, BorderRenderTaskInfo};
use box_shadow::BLUR_SAMPLE_SCALE;
-use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, CoordinateSystemId};
-use clip_scroll_node::ClipScrollNode;
+use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId, SpatialNodeIndex};
use clip::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipSource};
use clip::{ClipSourcesHandle, ClipWorkItem};
use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
use frame_builder::PrimitiveRunContext;
use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT};
use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
ToGpuBlocks};
use gpu_types::BrushFlags;
@@ -26,34 +25,35 @@ use picture::{PictureCompositeMode, Pict
#[cfg(debug_assertions)]
use render_backend::FrameId;
use render_task::{BlitSource, RenderTask, RenderTaskCacheKey};
use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskCacheEntryHandle};
use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
use resource_cache::{ImageProperties, ImageRequest, ResourceCache};
use scene::SceneProperties;
use segment::SegmentBuilder;
+use spatial_node::SpatialNode;
use std::{mem, usize};
use std::sync::Arc;
-use util::{MatrixHelpers, WorldToLayoutFastTransform, calculate_screen_bounding_rect};
+use util::{MatrixHelpers, calculate_screen_bounding_rect};
use util::{pack_as_float, recycle_vec};
const MIN_BRUSH_SPLIT_AREA: f32 = 256.0 * 256.0;
pub const VECS_PER_SEGMENT: usize = 2;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ScrollNodeAndClipChain {
- pub scroll_node_id: ClipScrollNodeIndex,
+ pub scroll_node_id: SpatialNodeIndex,
pub clip_chain_index: ClipChainIndex,
}
impl ScrollNodeAndClipChain {
pub fn new(
- scroll_node_id: ClipScrollNodeIndex,
+ scroll_node_id: SpatialNodeIndex,
clip_chain_index: ClipChainIndex
) -> Self {
ScrollNodeAndClipChain { scroll_node_id, clip_chain_index }
}
}
#[derive(Debug)]
pub struct PrimitiveRun {
@@ -1237,17 +1237,17 @@ impl PrimitiveStore {
}
}
pub fn add_image_picture(
&mut self,
composite_mode: Option<PictureCompositeMode>,
is_in_3d_context: bool,
pipeline_id: PipelineId,
- reference_frame_index: ClipScrollNodeIndex,
+ reference_frame_index: SpatialNodeIndex,
frame_output_pipeline_id: Option<PipelineId>,
apply_local_clip_rect: bool,
) -> PictureIndex {
let picture = PicturePrimitive::new_image(
PictureId(self.next_picture_id),
composite_mode,
is_in_3d_context,
pipeline_id,
@@ -1989,17 +1989,16 @@ impl PrimitiveStore {
}
fn write_brush_segment_description(
brush: &mut BrushPrimitive,
metadata: &PrimitiveMetadata,
prim_run_context: &PrimitiveRunContext,
clips: &Vec<ClipWorkItem>,
has_clips_from_other_coordinate_systems: bool,
- frame_context: &FrameBuildingContext,
frame_state: &mut FrameBuildingState,
) {
match brush.segment_desc {
Some(ref segment_desc) => {
// If we already have a segment descriptor, only run through the
// clips list if we haven't already determined the mask kind.
if segment_desc.clip_mask_kind != BrushClipMaskKind::Unknown {
return;
@@ -2042,29 +2041,49 @@ impl PrimitiveStore {
// Segment the primitive on all the local-space clip sources that we can.
for clip_item in clips {
if clip_item.coordinate_system_id != prim_run_context.scroll_node.coordinate_system_id {
continue;
}
let local_clips = frame_state.clip_store.get_opt(&clip_item.clip_sources).expect("bug");
+ rect_clips_only = rect_clips_only && local_clips.only_rectangular_clips;
+
+ // TODO(gw): We can easily extend the segment builder to support these clip sources in
+ // the future, but they are rarely used.
+ // We must do this check here in case we continue early below.
+ if local_clips.has_image_or_line_decoration_clip {
+ clip_mask_kind = BrushClipMaskKind::Global;
+ }
+
+ // If this clip item is positioned by another positioning node, its relative position
+ // could change during scrolling. This means that we would need to resegment. Instead
+ // of doing that, only segment with clips that have the same positioning node.
+ // TODO(mrobinson, #2858): It may make sense to include these nodes, resegmenting only
+ // when necessary while scrolling.
+ if clip_item.transform_index != prim_run_context.transform_index {
+ // We don't need to generate a global clip mask for rectangle clips because we are
+ // in the same coordinate system and rectangular clips are handled by the local
+ // clip chain rectangle.
+ if !local_clips.only_rectangular_clips {
+ clip_mask_kind = BrushClipMaskKind::Global;
+ }
+ continue;
+ }
+
for &(ref clip, _) in &local_clips.clips {
let (local_clip_rect, radius, mode) = match *clip {
ClipSource::RoundedRectangle(rect, radii, clip_mode) => {
- rect_clips_only = false;
-
(rect, Some(radii), clip_mode)
}
ClipSource::Rectangle(rect, mode) => {
(rect, None, mode)
}
ClipSource::BoxShadow(ref info) => {
- rect_clips_only = false;
-
// For inset box shadows, we can clip out any
// pixels that are inside the shadow region
// and are beyond the inner rect, as they can't
// be affected by the blur radius.
let inner_clip_mode = match info.clip_mode {
BoxShadowClipMode::Outset => None,
BoxShadowClipMode::Inset => Some(ClipMode::ClipOut),
};
@@ -2079,45 +2098,17 @@ impl PrimitiveStore {
-0.5 * info.shadow_rect_alloc_size.width,
-0.5 * info.shadow_rect_alloc_size.height,
),
inner_clip_mode,
);
continue;
}
- ClipSource::LineDecoration(..) |
- ClipSource::Image(..) => {
- rect_clips_only = false;
-
- // TODO(gw): We can easily extend the segment builder
- // to support these clip sources in the
- // future, but they are rarely used.
- clip_mask_kind = BrushClipMaskKind::Global;
- continue;
- }
- };
-
- // If the scroll node transforms are different between the clip
- // node and the primitive, we need to get the clip rect in the
- // local space of the primitive, in order to generate correct
- // local segments.
- let local_clip_rect = if clip_item.transform_index == prim_run_context.scroll_node.transform_index {
- local_clip_rect
- } else {
- let clip_transform = frame_context
- .transforms[clip_item.transform_index.0 as usize]
- .transform;
- let prim_transform = &prim_run_context.scroll_node.world_content_transform;
- let relative_transform = prim_transform
- .inverse()
- .unwrap_or(WorldToLayoutFastTransform::identity())
- .pre_mul(&clip_transform.into());
-
- relative_transform.transform_rect(&local_clip_rect)
+ ClipSource::LineDecoration(..) | ClipSource::Image(..) => continue,
};
segment_builder.push_clip_rect(local_clip_rect, radius, mode);
}
}
if is_large || rect_clips_only {
match brush.segment_desc {
@@ -2176,17 +2167,16 @@ impl PrimitiveStore {
};
PrimitiveStore::write_brush_segment_description(
brush,
metadata,
prim_run_context,
clips,
has_clips_from_other_coordinate_systems,
- frame_context,
frame_state,
);
let segment_desc = match brush.segment_desc {
Some(ref mut description) => description,
None => return false,
};
let clip_mask_kind = segment_desc.clip_mask_kind;
@@ -2298,21 +2288,21 @@ impl PrimitiveStore {
combined_outer_rect = combined_outer_rect.and_then(|r| r.intersection(&outer));
}
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tfound extra clip with screen bounds {:?}", screen_outer_rect);
}
Arc::new(ClipChainNode {
work_item: ClipWorkItem {
- transform_index: prim_run_context.scroll_node.transform_index,
+ transform_index: prim_run_context.transform_index,
clip_sources: clip_sources.weak(),
coordinate_system_id: prim_coordinate_system_id,
},
- // The local_clip_rect a property of ClipChain nodes that are ClipScrollNodes.
+ // The local_clip_rect a property of ClipChain nodes that are ClipNodes.
// It's used to calculate a local clipping rectangle before we reach this
// point, so we can set it to zero here. It should be unused from this point
// on.
local_clip_rect: LayoutRect::zero(),
screen_inner_rect,
screen_outer_rect: screen_outer_rect.unwrap_or(prim_screen_rect),
prev: None,
})
@@ -2644,17 +2634,17 @@ impl PrimitiveStore {
println!("\tpreparing a run of length {} in pipeline {:?}",
run.count, pic_context.pipeline_id);
}
// TODO(gw): Perhaps we can restructure this to not need to create
// a new primitive context for every run (if the hash
// lookups ever show up in a profile).
let scroll_node = &frame_context
.clip_scroll_tree
- .nodes[run.clip_and_scroll.scroll_node_id.0];
+ .spatial_nodes[run.clip_and_scroll.scroll_node_id.0];
let clip_chain = frame_context
.clip_scroll_tree
.get_clip_chain(run.clip_and_scroll.clip_chain_index);
// Mark whether this picture contains any complex coordinate
// systems, due to either the scroll node or the clip-chain.
pic_state.has_non_root_coord_system |=
scroll_node.coordinate_system_id != CoordinateSystemId::root();
@@ -2680,17 +2670,17 @@ impl PrimitiveStore {
inv_parent.pre_mul(&scroll_node.world_content_transform)
});
let original_relative_transform = pic_context
.original_reference_frame_index
.and_then(|original_reference_frame_index| {
frame_context
.clip_scroll_tree
- .nodes[original_reference_frame_index.0]
+ .spatial_nodes[original_reference_frame_index.0]
.world_content_transform
.inverse()
})
.map(|inv_parent| {
inv_parent.pre_mul(&scroll_node.world_content_transform)
});
if run.is_chasing(self.chase_id) {
@@ -2717,16 +2707,17 @@ impl PrimitiveStore {
Some(rect) if rect.is_empty() => continue,
Some(rect) => rect,
None => frame_context.max_local_clip,
};
let child_prim_run_context = PrimitiveRunContext::new(
clip_chain,
scroll_node,
+ run.clip_and_scroll.scroll_node_id.transform_index(),
local_clip_chain_rect,
);
for i in 0 .. run.count {
let prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
if let Some(prim_local_rect) = self.prepare_prim_for_render(
prim_index,
@@ -2909,17 +2900,17 @@ fn convert_clip_chain_to_clip_vector(
};
Some(node.work_item.clone())
})
.collect()
}
fn get_local_clip_rect_for_nodes(
- scroll_node: &ClipScrollNode,
+ scroll_node: &SpatialNode,
clip_chain: &ClipChain,
) -> Option<LayoutRect> {
ClipChainNodeIter { current: clip_chain.nodes.clone() }
.fold(
None,
|combined_local_clip_rect: Option<LayoutRect>, node| {
if node.work_item.coordinate_system_id != scroll_node.coordinate_system_id {
return combined_local_clip_rect;
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -9,17 +9,17 @@ use api::{DeviceIntPoint, DevicePixelSca
use api::{DocumentId, DocumentLayer, ExternalScrollId, FrameMsg, HitTestFlags, HitTestResult};
use api::{IdNamespace, LayoutPoint, PipelineId, RenderNotifier, SceneMsg, ScrollClamping};
use api::{ScrollLocation, ScrollNodeState, TransactionMsg};
use api::channel::{MsgReceiver, Payload};
#[cfg(feature = "capture")]
use api::CaptureBits;
#[cfg(feature = "replay")]
use api::CapturedDocument;
-use clip_scroll_tree::{ClipScrollNodeIndex, ClipScrollTree};
+use clip_scroll_tree::{SpatialNodeIndex, ClipScrollTree};
#[cfg(feature = "debugger")]
use debug_server;
use display_list_flattener::DisplayListFlattener;
use frame_builder::{FrameBuilder, FrameBuilderConfig};
use gpu_cache::GpuCache;
use hit_test::{HitTest, HitTester};
use internal_types::{DebugOutput, FastHashMap, FastHashSet, RenderedDocument, ResultMsg};
use profiler::{BackendProfileCounters, IpcProfileCounters, ResourceProfileCounters};
@@ -84,19 +84,19 @@ struct Document {
// received from the scene builder thread.
current: SceneData,
// The scene with the latest transactions applied, not necessarily built yet.
// what we will send to the scene builder.
pending: SceneData,
view: DocumentView,
- /// The ClipScrollTree for this document which tracks both ClipScrollNodes and ClipChains.
- /// This is stored here so that we are able to preserve scrolling positions between
- /// rendered frames.
+ /// The ClipScrollTree for this document which tracks SpatialNodes, ClipNodes, and ClipChains.
+ /// This is stored here so that we are able to preserve scrolling positions between rendered
+ /// frames.
clip_scroll_tree: ClipScrollTree,
/// The id of the current frame.
frame_id: FrameId,
/// A configuration object for the FrameBuilder that we produce.
frame_builder_config: FrameBuilderConfig,
@@ -308,17 +308,17 @@ impl Document {
self.clip_scroll_tree
.discard_frame_state_for_pipeline(pipeline_id);
}
/// Returns true if any nodes actually changed position or false otherwise.
pub fn scroll_nearest_scrolling_ancestor(
&mut self,
scroll_location: ScrollLocation,
- scroll_node_index: Option<ClipScrollNodeIndex>,
+ scroll_node_index: Option<SpatialNodeIndex>,
) -> bool {
self.clip_scroll_tree.scroll_nearest_scrolling_ancestor(scroll_location, scroll_node_index)
}
/// Returns true if the node actually changed position or false otherwise.
pub fn scroll_node(
&mut self,
origin: LayoutPoint,
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/src/spatial_node.rs
@@ -0,0 +1,706 @@
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use api::{ExternalScrollId, LayoutPixel, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform};
+use api::{LayoutVector2D, PipelineId, PropertyBinding, ScrollClamping, ScrollLocation};
+use api::{ScrollSensitivity, StickyOffsetBounds};
+use clip_scroll_tree::{CoordinateSystemId, SpatialNodeIndex, TransformUpdateState};
+use euclid::SideOffsets2D;
+use gpu_types::{TransformData, TransformIndex, TransformPalette};
+use scene::SceneProperties;
+use util::{LayoutFastTransform, LayoutToWorldFastTransform, TransformedRectKind};
+
+#[derive(Clone, Debug)]
+pub enum SpatialNodeType {
+ /// A special kind of node that adjusts its position based on the position
+ /// of its parent node and a given set of sticky positioning offset bounds.
+ /// Sticky positioned is described in the CSS Positioned Layout Module Level 3 here:
+ /// https://www.w3.org/TR/css-position-3/#sticky-pos
+ StickyFrame(StickyFrameInfo),
+
+ /// Transforms it's content, but doesn't clip it. Can also be adjusted
+ /// by scroll events or setting scroll offsets.
+ ScrollFrame(ScrollFrameInfo),
+
+ /// A reference frame establishes a new coordinate space in the tree.
+ ReferenceFrame(ReferenceFrameInfo),
+
+ /// An empty node, used to pad the ClipScrollTree's array of nodes so that
+ /// we can immediately use each assigned SpatialNodeIndex. After display
+ /// list flattening this node type should never be used.
+ Empty,
+}
+
+impl SpatialNodeType {
+ fn is_reference_frame(&self) -> bool {
+ match *self {
+ SpatialNodeType::ReferenceFrame(_) => true,
+ _ => false,
+ }
+ }
+}
+
+/// Contains information common among all types of ClipScrollTree nodes.
+#[derive(Clone, Debug)]
+pub struct SpatialNode {
+ /// The transformation for this viewport in world coordinates is the transformation for
+ /// our parent reference frame, plus any accumulated scrolling offsets from nodes
+ /// between our reference frame and this node. For reference frames, we also include
+ /// whatever local transformation this reference frame provides.
+ pub world_viewport_transform: LayoutToWorldFastTransform,
+
+ /// World transform for content transformed by this node.
+ pub world_content_transform: LayoutToWorldFastTransform,
+
+ /// The current transform kind of world_content_transform.
+ pub transform_kind: TransformedRectKind,
+
+ /// Pipeline that this layer belongs to
+ pub pipeline_id: PipelineId,
+
+ /// Parent layer. If this is None, we are the root node.
+ pub parent: Option<SpatialNodeIndex>,
+
+ /// Child layers
+ pub children: Vec<SpatialNodeIndex>,
+
+ /// The type of this node and any data associated with that node type.
+ pub node_type: SpatialNodeType,
+
+ /// 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.
+ pub invertible: bool,
+
+ /// The axis-aligned coordinate system id of this node.
+ pub coordinate_system_id: CoordinateSystemId,
+
+ /// The transformation from the coordinate system which established our compatible coordinate
+ /// system (same coordinate system id) and us. This can change via scroll offsets and via new
+ /// reference frame transforms.
+ pub coordinate_system_relative_transform: LayoutFastTransform,
+}
+
+impl SpatialNode {
+ pub fn new(
+ pipeline_id: PipelineId,
+ parent_index: Option<SpatialNodeIndex>,
+ node_type: SpatialNodeType,
+ ) -> Self {
+ SpatialNode {
+ world_viewport_transform: LayoutToWorldFastTransform::identity(),
+ world_content_transform: LayoutToWorldFastTransform::identity(),
+ transform_kind: TransformedRectKind::AxisAligned,
+ parent: parent_index,
+ children: Vec::new(),
+ pipeline_id,
+ node_type,
+ invertible: true,
+ coordinate_system_id: CoordinateSystemId(0),
+ coordinate_system_relative_transform: LayoutFastTransform::identity(),
+ }
+ }
+
+ pub fn empty() -> SpatialNode {
+ Self::new(PipelineId::dummy(), None, SpatialNodeType::Empty)
+ }
+
+ pub fn new_scroll_frame(
+ pipeline_id: PipelineId,
+ parent_index: SpatialNodeIndex,
+ external_id: Option<ExternalScrollId>,
+ frame_rect: &LayoutRect,
+ content_size: &LayoutSize,
+ scroll_sensitivity: ScrollSensitivity,
+ ) -> Self {
+ let node_type = SpatialNodeType::ScrollFrame(ScrollFrameInfo::new(
+ *frame_rect,
+ scroll_sensitivity,
+ LayoutSize::new(
+ (content_size.width - frame_rect.size.width).max(0.0),
+ (content_size.height - frame_rect.size.height).max(0.0)
+ ),
+ external_id,
+ )
+ );
+
+ Self::new(pipeline_id, Some(parent_index), node_type)
+ }
+
+ pub fn new_reference_frame(
+ parent_index: Option<SpatialNodeIndex>,
+ source_transform: Option<PropertyBinding<LayoutTransform>>,
+ source_perspective: Option<LayoutTransform>,
+ origin_in_parent_reference_frame: LayoutVector2D,
+ pipeline_id: PipelineId,
+ ) -> Self {
+ let identity = LayoutTransform::identity();
+ let source_perspective = source_perspective.map_or_else(
+ LayoutFastTransform::identity, |perspective| perspective.into());
+ let info = ReferenceFrameInfo {
+ resolved_transform: LayoutFastTransform::identity(),
+ source_transform: source_transform.unwrap_or(PropertyBinding::Value(identity)),
+ source_perspective,
+ origin_in_parent_reference_frame,
+ invertible: true,
+ };
+ Self::new(pipeline_id, parent_index, SpatialNodeType:: ReferenceFrame(info))
+ }
+
+ pub fn new_sticky_frame(
+ parent_index: SpatialNodeIndex,
+ sticky_frame_info: StickyFrameInfo,
+ pipeline_id: PipelineId,
+ ) -> Self {
+ Self::new(pipeline_id, Some(parent_index), SpatialNodeType::StickyFrame(sticky_frame_info))
+ }
+
+
+ pub fn add_child(&mut self, child: SpatialNodeIndex) {
+ self.children.push(child);
+ }
+
+ pub fn apply_old_scrolling_state(&mut self, old_scroll_info: &ScrollFrameInfo) {
+ match self.node_type {
+ SpatialNodeType::ScrollFrame(ref mut scrolling) => {
+ *scrolling = scrolling.combine_with_old_scroll_info(old_scroll_info);
+ }
+ _ if old_scroll_info.offset != LayoutVector2D::zero() => {
+ warn!("Tried to scroll a non-scroll node.")
+ }
+ _ => {}
+ }
+ }
+
+ pub fn set_scroll_origin(&mut self, origin: &LayoutPoint, clamp: ScrollClamping) -> bool {
+ let scrollable_size = self.scrollable_size();
+ let scrollable_width = scrollable_size.width;
+ let scrollable_height = scrollable_size.height;
+
+ let scrolling = match self.node_type {
+ SpatialNodeType::ScrollFrame(ref mut scrolling) => scrolling,
+ _ => {
+ warn!("Tried to scroll a non-scroll node.");
+ return false;
+ }
+ };
+
+ let new_offset = match clamp {
+ ScrollClamping::ToContentBounds => {
+ if scrollable_height <= 0. && scrollable_width <= 0. {
+ return false;
+ }
+
+ let origin = LayoutPoint::new(origin.x.max(0.0), origin.y.max(0.0));
+ LayoutVector2D::new(
+ (-origin.x).max(-scrollable_width).min(0.0).round(),
+ (-origin.y).max(-scrollable_height).min(0.0).round(),
+ )
+ }
+ ScrollClamping::NoClamping => LayoutPoint::zero() - *origin,
+ };
+
+ if new_offset == scrolling.offset {
+ return false;
+ }
+
+ scrolling.offset = new_offset;
+ true
+ }
+
+ pub fn mark_uninvertible(&mut self) {
+ self.invertible = false;
+ self.world_content_transform = LayoutToWorldFastTransform::identity();
+ self.world_viewport_transform = LayoutToWorldFastTransform::identity();
+ }
+
+ pub fn push_gpu_data(
+ &mut self,
+ transform_palette: &mut TransformPalette,
+ node_index: SpatialNodeIndex,
+ ) {
+ let transform_index = TransformIndex(node_index.0 as u32);
+ if !self.invertible {
+ transform_palette.set(transform_index, TransformData::invalid());
+ return;
+ }
+
+ let inv_transform = match self.world_content_transform.inverse() {
+ Some(inverted) => inverted.to_transform(),
+ None => {
+ transform_palette.set(transform_index, TransformData::invalid());
+ return;
+ }
+ };
+
+ let data = TransformData {
+ transform: self.world_content_transform.into(),
+ inv_transform,
+ };
+
+ // Write the data that will be made available to the GPU for this node.
+ transform_palette.set(transform_index, data);
+ }
+
+ pub fn update(
+ &mut self,
+ state: &mut TransformUpdateState,
+ next_coordinate_system_id: &mut CoordinateSystemId,
+ scene_properties: &SceneProperties,
+ ) {
+ // If any of our parents was not rendered, we are not rendered either and can just
+ // quit here.
+ if !state.invertible {
+ self.mark_uninvertible();
+ return;
+ }
+
+ self.update_transform(state, next_coordinate_system_id, scene_properties);
+
+ self.transform_kind = if self.world_content_transform.preserves_2d_axis_alignment() {
+ TransformedRectKind::AxisAligned
+ } else {
+ TransformedRectKind::Complex
+ };
+
+ // 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
+ // translations which should be invertible.
+ match self.node_type {
+ SpatialNodeType::ReferenceFrame(info) if !info.invertible => {
+ self.mark_uninvertible();
+ return;
+ }
+ _ => self.invertible = true,
+ }
+ }
+
+ pub fn update_transform(
+ &mut self,
+ state: &mut TransformUpdateState,
+ next_coordinate_system_id: &mut CoordinateSystemId,
+ scene_properties: &SceneProperties,
+ ) {
+ if self.node_type.is_reference_frame() {
+ self.update_transform_for_reference_frame(
+ state,
+ next_coordinate_system_id,
+ scene_properties
+ );
+ return;
+ }
+
+ // We calculate this here to avoid a double-borrow later.
+ let sticky_offset = self.calculate_sticky_offset(
+ &state.nearest_scrolling_ancestor_offset,
+ &state.nearest_scrolling_ancestor_viewport,
+ );
+
+ // The transformation for the bounds of our viewport is the parent reference frame
+ // transform, plus any accumulated scroll offset from our parents, plus any offset
+ // provided by our own sticky positioning.
+ let accumulated_offset = state.parent_accumulated_scroll_offset + sticky_offset;
+ self.world_viewport_transform = if accumulated_offset != LayoutVector2D::zero() {
+ state.parent_reference_frame_transform.pre_translate(&accumulated_offset)
+ } else {
+ state.parent_reference_frame_transform
+ };
+
+ // The transformation for any content inside of us is the viewport transformation, plus
+ // whatever scrolling offset we supply as well.
+ let scroll_offset = self.scroll_offset();
+ self.world_content_transform = if scroll_offset != LayoutVector2D::zero() {
+ self.world_viewport_transform.pre_translate(&scroll_offset)
+ } else {
+ self.world_viewport_transform
+ };
+
+ let added_offset = state.parent_accumulated_scroll_offset + sticky_offset + scroll_offset;
+ self.coordinate_system_relative_transform =
+ state.coordinate_system_relative_transform.offset(added_offset);
+
+ if let SpatialNodeType::StickyFrame(ref mut info) = self.node_type {
+ info.current_offset = sticky_offset;
+ }
+
+ self.coordinate_system_id = state.current_coordinate_system_id;
+ }
+
+ pub fn update_transform_for_reference_frame(
+ &mut self,
+ state: &mut TransformUpdateState,
+ next_coordinate_system_id: &mut CoordinateSystemId,
+ scene_properties: &SceneProperties,
+ ) {
+ let info = match self.node_type {
+ SpatialNodeType::ReferenceFrame(ref mut info) => info,
+ _ => unreachable!("Called update_transform_for_reference_frame on non-ReferenceFrame"),
+ };
+
+ // Resolve the transform against any property bindings.
+ let source_transform = scene_properties.resolve_layout_transform(&info.source_transform);
+ info.resolved_transform =
+ LayoutFastTransform::with_vector(info.origin_in_parent_reference_frame)
+ .pre_mul(&source_transform.into())
+ .pre_mul(&info.source_perspective);
+
+ // The transformation for this viewport in world coordinates is the transformation for
+ // our parent reference frame, plus any accumulated scrolling offsets from nodes
+ // between our reference frame and this node. Finally, we also include
+ // whatever local transformation this reference frame provides.
+ let relative_transform = info.resolved_transform
+ .post_translate(state.parent_accumulated_scroll_offset)
+ .to_transform()
+ .with_destination::<LayoutPixel>();
+ self.world_viewport_transform =
+ state.parent_reference_frame_transform.pre_mul(&relative_transform.into());
+ self.world_content_transform = self.world_viewport_transform;
+
+ info.invertible = self.world_viewport_transform.is_invertible();
+ if !info.invertible {
+ return;
+ }
+
+ // Try to update our compatible coordinate system transform. If we cannot, start a new
+ // incompatible coordinate system.
+ match state.coordinate_system_relative_transform.update(relative_transform) {
+ Some(offset) => self.coordinate_system_relative_transform = offset,
+ None => {
+ self.coordinate_system_relative_transform = LayoutFastTransform::identity();
+ state.current_coordinate_system_id = *next_coordinate_system_id;
+ next_coordinate_system_id.advance();
+ }
+ }
+
+ self.coordinate_system_id = state.current_coordinate_system_id;
+ }
+
+ fn calculate_sticky_offset(
+ &self,
+ viewport_scroll_offset: &LayoutVector2D,
+ viewport_rect: &LayoutRect,
+ ) -> LayoutVector2D {
+ let info = match self.node_type {
+ SpatialNodeType::StickyFrame(ref info) => info,
+ _ => return LayoutVector2D::zero(),
+ };
+
+ if info.margins.top.is_none() && info.margins.bottom.is_none() &&
+ info.margins.left.is_none() && info.margins.right.is_none() {
+ return LayoutVector2D::zero();
+ }
+
+ // The viewport and margins of the item establishes the maximum amount that it can
+ // be offset in order to keep it on screen. Since we care about the relationship
+ // between the scrolled content and unscrolled viewport we adjust the viewport's
+ // position by the scroll offset in order to work with their relative positions on the
+ // page.
+ let sticky_rect = info.frame_rect.translate(viewport_scroll_offset);
+
+ let mut sticky_offset = LayoutVector2D::zero();
+ if let Some(margin) = info.margins.top {
+ let top_viewport_edge = viewport_rect.min_y() + margin;
+ if sticky_rect.min_y() < top_viewport_edge {
+ // If the sticky rect is positioned above the top edge of the viewport (plus margin)
+ // we move it down so that it is fully inside the viewport.
+ sticky_offset.y = top_viewport_edge - sticky_rect.min_y();
+ } else if info.previously_applied_offset.y > 0.0 &&
+ sticky_rect.min_y() > top_viewport_edge {
+ // However, if the sticky rect is positioned *below* the top edge of the viewport
+ // and there is already some offset applied to the sticky rect's position, then
+ // we need to move it up so that it remains at the correct position. This
+ // makes sticky_offset.y negative and effectively reduces the amount of the
+ // offset that was already applied. We limit the reduction so that it can, at most,
+ // cancel out the already-applied offset, but should never end up adjusting the
+ // position the other way.
+ sticky_offset.y = top_viewport_edge - sticky_rect.min_y();
+ sticky_offset.y = sticky_offset.y.max(-info.previously_applied_offset.y);
+ }
+ debug_assert!(sticky_offset.y + info.previously_applied_offset.y >= 0.0);
+ }
+
+ // If we don't have a sticky-top offset (sticky_offset.y + info.previously_applied_offset.y
+ // == 0), or if we have a previously-applied bottom offset (previously_applied_offset.y < 0)
+ // then we check for handling the bottom margin case.
+ if sticky_offset.y + info.previously_applied_offset.y <= 0.0 {
+ if let Some(margin) = info.margins.bottom {
+ // Same as the above case, but inverted for bottom-sticky items. Here
+ // we adjust items upwards, resulting in a negative sticky_offset.y,
+ // or reduce the already-present upward adjustment, resulting in a positive
+ // sticky_offset.y.
+ let bottom_viewport_edge = viewport_rect.max_y() - margin;
+ if sticky_rect.max_y() > bottom_viewport_edge {
+ sticky_offset.y = bottom_viewport_edge - sticky_rect.max_y();
+ } else if info.previously_applied_offset.y < 0.0 &&
+ sticky_rect.max_y() < bottom_viewport_edge {
+ sticky_offset.y = bottom_viewport_edge - sticky_rect.max_y();
+ sticky_offset.y = sticky_offset.y.min(-info.previously_applied_offset.y);
+ }
+ debug_assert!(sticky_offset.y + info.previously_applied_offset.y <= 0.0);
+ }
+ }
+
+ // Same as above, but for the x-axis.
+ if let Some(margin) = info.margins.left {
+ let left_viewport_edge = viewport_rect.min_x() + margin;
+ if sticky_rect.min_x() < left_viewport_edge {
+ sticky_offset.x = left_viewport_edge - sticky_rect.min_x();
+ } else if info.previously_applied_offset.x > 0.0 &&
+ sticky_rect.min_x() > left_viewport_edge {
+ sticky_offset.x = left_viewport_edge - sticky_rect.min_x();
+ sticky_offset.x = sticky_offset.x.max(-info.previously_applied_offset.x);
+ }
+ debug_assert!(sticky_offset.x + info.previously_applied_offset.x >= 0.0);
+ }
+
+ if sticky_offset.x + info.previously_applied_offset.x <= 0.0 {
+ if let Some(margin) = info.margins.right {
+ let right_viewport_edge = viewport_rect.max_x() - margin;
+ if sticky_rect.max_x() > right_viewport_edge {
+ sticky_offset.x = right_viewport_edge - sticky_rect.max_x();
+ } else if info.previously_applied_offset.x < 0.0 &&
+ sticky_rect.max_x() < right_viewport_edge {
+ sticky_offset.x = right_viewport_edge - sticky_rect.max_x();
+ sticky_offset.x = sticky_offset.x.min(-info.previously_applied_offset.x);
+ }
+ debug_assert!(sticky_offset.x + info.previously_applied_offset.x <= 0.0);
+ }
+ }
+
+ // The total "sticky offset" (which is the sum that was already applied by
+ // the calling code, stored in info.previously_applied_offset, and the extra amount we
+ // computed as a result of scrolling, stored in sticky_offset) needs to be
+ // clamped to the provided bounds.
+ let clamp_adjusted = |value: f32, adjust: f32, bounds: &StickyOffsetBounds| {
+ (value + adjust).max(bounds.min).min(bounds.max) - adjust
+ };
+ sticky_offset.y = clamp_adjusted(sticky_offset.y,
+ info.previously_applied_offset.y,
+ &info.vertical_offset_bounds);
+ sticky_offset.x = clamp_adjusted(sticky_offset.x,
+ info.previously_applied_offset.x,
+ &info.horizontal_offset_bounds);
+
+ sticky_offset
+ }
+
+ pub fn prepare_state_for_children(&self, state: &mut TransformUpdateState) {
+ if !self.invertible {
+ state.invertible = false;
+ return;
+ }
+
+ // The transformation we are passing is the transformation of the parent
+ // reference frame and the offset is the accumulated offset of all the nodes
+ // between us and the parent reference frame. If we are a reference frame,
+ // we need to reset both these values.
+ match self.node_type {
+ SpatialNodeType::StickyFrame(ref info) => {
+ // We don't translate the combined rect by the sticky offset, because sticky
+ // offsets actually adjust the node position itself, whereas scroll offsets
+ // only apply to contents inside the node.
+ state.parent_accumulated_scroll_offset =
+ info.current_offset + state.parent_accumulated_scroll_offset;
+ }
+ SpatialNodeType::ScrollFrame(ref scrolling) => {
+ state.parent_accumulated_scroll_offset =
+ scrolling.offset + state.parent_accumulated_scroll_offset;
+ state.nearest_scrolling_ancestor_offset = scrolling.offset;
+ state.nearest_scrolling_ancestor_viewport = scrolling.viewport_rect;
+ }
+ SpatialNodeType::ReferenceFrame(ref info) => {
+ state.parent_reference_frame_transform = self.world_viewport_transform;
+ state.parent_accumulated_scroll_offset = LayoutVector2D::zero();
+ state.coordinate_system_relative_transform =
+ self.coordinate_system_relative_transform.clone();
+ let translation = -info.origin_in_parent_reference_frame;
+ state.nearest_scrolling_ancestor_viewport =
+ state.nearest_scrolling_ancestor_viewport
+ .translate(&translation);
+ }
+ SpatialNodeType::Empty => unreachable!("Empty node remaining in ClipScrollTree."),
+ }
+ }
+
+ pub fn scrollable_size(&self) -> LayoutSize {
+ match self.node_type {
+ SpatialNodeType::ScrollFrame(state) => state.scrollable_size,
+ _ => LayoutSize::zero(),
+ }
+ }
+
+ pub fn scroll(&mut self, scroll_location: ScrollLocation) -> bool {
+ let scrolling = match self.node_type {
+ SpatialNodeType::ScrollFrame(ref mut scrolling) => scrolling,
+ _ => return false,
+ };
+
+ let delta = match scroll_location {
+ ScrollLocation::Delta(delta) => delta,
+ ScrollLocation::Start => {
+ if scrolling.offset.y.round() >= 0.0 {
+ // Nothing to do on this layer.
+ return false;
+ }
+
+ scrolling.offset.y = 0.0;
+ return true;
+ }
+ ScrollLocation::End => {
+ let end_pos = -scrolling.scrollable_size.height;
+ if scrolling.offset.y.round() <= end_pos {
+ // Nothing to do on this layer.
+ return false;
+ }
+
+ scrolling.offset.y = end_pos;
+ return true;
+ }
+ };
+
+ let scrollable_width = scrolling.scrollable_size.width;
+ let scrollable_height = scrolling.scrollable_size.height;
+ let original_layer_scroll_offset = scrolling.offset;
+
+ if scrollable_width > 0. {
+ scrolling.offset.x = (scrolling.offset.x + delta.x)
+ .min(0.0)
+ .max(-scrollable_width)
+ .round();
+ }
+
+ if scrollable_height > 0. {
+ scrolling.offset.y = (scrolling.offset.y + delta.y)
+ .min(0.0)
+ .max(-scrollable_height)
+ .round();
+ }
+
+ scrolling.offset != original_layer_scroll_offset
+ }
+
+ pub fn scroll_offset(&self) -> LayoutVector2D {
+ match self.node_type {
+ SpatialNodeType::ScrollFrame(ref scrolling) => scrolling.offset,
+ _ => LayoutVector2D::zero(),
+ }
+ }
+
+ pub fn matches_external_id(&self, external_id: ExternalScrollId) -> bool {
+ match self.node_type {
+ SpatialNodeType::ScrollFrame(info) if info.external_id == Some(external_id) => true,
+ _ => false,
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct ScrollFrameInfo {
+ /// The rectangle of the viewport of this scroll frame. This is important for
+ /// positioning of items inside child StickyFrames.
+ pub viewport_rect: LayoutRect,
+
+ pub offset: LayoutVector2D,
+ pub scroll_sensitivity: ScrollSensitivity,
+
+ /// Amount that this ScrollFrame can scroll in both directions.
+ pub scrollable_size: LayoutSize,
+
+ /// An external id to identify this scroll frame to API clients. This
+ /// allows setting scroll positions via the API without relying on ClipsIds
+ /// which may change between frames.
+ pub external_id: Option<ExternalScrollId>,
+
+}
+
+/// Manages scrolling offset.
+impl ScrollFrameInfo {
+ pub fn new(
+ viewport_rect: LayoutRect,
+ scroll_sensitivity: ScrollSensitivity,
+ scrollable_size: LayoutSize,
+ external_id: Option<ExternalScrollId>,
+ ) -> ScrollFrameInfo {
+ ScrollFrameInfo {
+ viewport_rect,
+ offset: LayoutVector2D::zero(),
+ scroll_sensitivity,
+ scrollable_size,
+ external_id,
+ }
+ }
+
+ pub fn sensitive_to_input_events(&self) -> bool {
+ match self.scroll_sensitivity {
+ ScrollSensitivity::ScriptAndInputEvents => true,
+ ScrollSensitivity::Script => false,
+ }
+ }
+
+ pub fn combine_with_old_scroll_info(
+ self,
+ old_scroll_info: &ScrollFrameInfo
+ ) -> ScrollFrameInfo {
+ ScrollFrameInfo {
+ viewport_rect: self.viewport_rect,
+ offset: old_scroll_info.offset,
+ scroll_sensitivity: self.scroll_sensitivity,
+ scrollable_size: self.scrollable_size,
+ external_id: self.external_id,
+ }
+ }
+}
+
+/// Contains information about reference frames.
+#[derive(Copy, Clone, Debug)]
+pub struct ReferenceFrameInfo {
+ /// The transformation that establishes this reference frame, relative to the parent
+ /// reference frame. The origin of the reference frame is included in the transformation.
+ pub resolved_transform: LayoutFastTransform,
+
+ /// The source transform and perspective matrices provided by the stacking context
+ /// that forms this reference frame. We maintain the property binding information
+ /// here so that we can resolve the animated transform and update the tree each
+ /// frame.
+ pub source_transform: PropertyBinding<LayoutTransform>,
+ pub source_perspective: LayoutFastTransform,
+
+ /// The original, not including the transform and relative to the parent reference frame,
+ /// origin of this reference frame. This is already rolled into the `transform' property, but
+ /// we also store it here to properly transform the viewport for sticky positioning.
+ pub origin_in_parent_reference_frame: LayoutVector2D,
+
+ /// True if the resolved transform is invertible.
+ pub invertible: bool,
+}
+
+#[derive(Clone, Debug)]
+pub struct StickyFrameInfo {
+ pub frame_rect: LayoutRect,
+ pub margins: SideOffsets2D<Option<f32>>,
+ pub vertical_offset_bounds: StickyOffsetBounds,
+ pub horizontal_offset_bounds: StickyOffsetBounds,
+ pub previously_applied_offset: LayoutVector2D,
+ pub current_offset: LayoutVector2D,
+}
+
+impl StickyFrameInfo {
+ pub fn new(
+ frame_rect: LayoutRect,
+ margins: SideOffsets2D<Option<f32>>,
+ vertical_offset_bounds: StickyOffsetBounds,
+ horizontal_offset_bounds: StickyOffsetBounds,
+ previously_applied_offset: LayoutVector2D
+ ) -> StickyFrameInfo {
+ StickyFrameInfo {
+ frame_rect,
+ margins,
+ vertical_offset_bounds,
+ horizontal_offset_bounds,
+ previously_applied_offset,
+ current_offset: LayoutVector2D::zero(),
+ }
+ }
+}
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -2,17 +2,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixelScale, DeviceUintPoint};
use api::{DeviceUintRect, DeviceUintSize, DocumentLayer, FilterOp, ImageFormat, LayoutRect};
use api::{MixBlendMode, PipelineId};
use batch::{AlphaBatchBuilder, AlphaBatchContainer, ClipBatcher, resolve_image};
use clip::{ClipStore};
-use clip_scroll_tree::{ClipScrollTree, ClipScrollNodeIndex};
+use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex};
use device::{FrameId, Texture};
#[cfg(feature = "pathfinder")]
use euclid::{TypedPoint2D, TypedVector2D};
use gpu_cache::{GpuCache};
use gpu_types::{BorderInstance, BlurDirection, BlurInstance, PrimitiveHeaders, TransformData, TransformPalette};
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
#[cfg(feature = "pathfinder")]
use pathfinder_partitioner::mesh::Mesh;
@@ -26,17 +26,17 @@ use std::{cmp, usize, f32, i32, mem};
use texture_allocator::GuillotineAllocator;
#[cfg(feature = "pathfinder")]
use webrender_api::{DevicePixel, FontRenderMode};
const MIN_TARGET_SIZE: u32 = 2048;
#[derive(Debug)]
pub struct ScrollbarPrimitive {
- pub scroll_frame_index: ClipScrollNodeIndex,
+ pub scroll_frame_index: SpatialNodeIndex,
pub prim_index: PrimitiveIndex,
pub frame_rect: LayoutRect,
}
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct RenderTargetIndex(pub usize);
--- a/gfx/webrender/tests/angle_shader_validation.rs
+++ b/gfx/webrender/tests/angle_shader_validation.rs
@@ -13,19 +13,21 @@ const VERTEX_SHADER: u32 = 0x8B31;
struct Shader {
name: &'static str,
features: &'static [&'static str],
}
const SHADER_PREFIX: &str = "#define WR_MAX_VERTEX_TEXTURE_WIDTH 1024\n";
+const BRUSH_FEATURES: &[&str] = &["", "ALPHA_PASS"];
const CLIP_FEATURES: &[&str] = &["TRANSFORM"];
const CACHE_FEATURES: &[&str] = &[""];
-const PRIM_FEATURES: &[&str] = &["", "TRANSFORM"];
+const GRADIENT_FEATURES: &[&str] = &[ "", "DITHERING", "ALPHA_PASS", "DITHERING,ALPHA_PASS" ];
+const PRIM_FEATURES: &[&str] = &[""];
const SHADERS: &[Shader] = &[
// Clip mask shaders
Shader {
name: "cs_clip_rectangle",
features: CLIP_FEATURES,
},
Shader {
@@ -38,59 +40,66 @@ const SHADERS: &[Shader] = &[
},
Shader {
name: "cs_clip_line",
features: CLIP_FEATURES,
},
// Cache shaders
Shader {
name: "cs_blur",
- features: CACHE_FEATURES,
+ features: &[ "ALPHA_TARGET", "COLOR_TARGET" ],
},
Shader {
name: "cs_border_segment",
features: CACHE_FEATURES,
},
// Prim shaders
Shader {
name: "ps_split_composite",
features: PRIM_FEATURES,
},
Shader {
name: "ps_text_run",
- features: PRIM_FEATURES,
+ features: &[ "", "GLYPH_TRANSFORM" ],
},
// Brush shaders
Shader {
name: "brush_yuv_image",
- features: &["", "YUV_NV12", "YUV_PLANAR", "YUV_INTERLEAVED", "YUV_NV12,TEXTURE_RECT"],
+ features: &[
+ "",
+ "YUV_NV12",
+ "YUV_PLANAR",
+ "YUV_INTERLEAVED",
+ "TEXTURE_2D,YUV_NV12",
+ "YUV_NV12,ALPHA_PASS",
+ ],
},
Shader {
name: "brush_solid",
- features: &[],
+ features: BRUSH_FEATURES,
},
Shader {
name: "brush_image",
- features: &["", "ALPHA_PASS"],
+ features: BRUSH_FEATURES,
},
Shader {
name: "brush_blend",
- features: &[],
+ features: BRUSH_FEATURES,
},
Shader {
name: "brush_mix_blend",
- features: &[],
+ features: BRUSH_FEATURES,
},
Shader {
name: "brush_radial_gradient",
- features: &[ "DITHERING" ],
+ features: GRADIENT_FEATURES,
},
Shader {
name: "brush_linear_gradient",
- features: &[],
+ features: GRADIENT_FEATURES,
},
];
const VERSION_STRING: &str = "#version 300 es\n";
#[test]
fn validate_shaders() {
mozangle::shaders::initialize().unwrap();
@@ -103,17 +112,17 @@ fn validate_shaders() {
ShaderValidator::new(FRAGMENT_SHADER, ShaderSpec::Gles3, Output::Essl, &resources).unwrap();
for shader in SHADERS {
for config in shader.features {
let mut features = String::new();
features.push_str(SHADER_PREFIX);
for feature in config.split(",") {
- features.push_str(&format!("#define WR_FEATURE_{}", feature));
+ features.push_str(&format!("#define WR_FEATURE_{}\n", feature));
}
let (vs, fs) =
webrender::build_shader_strings(VERSION_STRING, &features, shader.name, &None);
validate(&vs_validator, shader.name, vs);
validate(&fs_validator, shader.name, fs);
}
--- a/gfx/webrender_api/Cargo.toml
+++ b/gfx/webrender_api/Cargo.toml
@@ -19,13 +19,13 @@ byteorder = "1.2.1"
ipc-channel = {version = "0.10.0", optional = true}
euclid = { version = "0.17", features = ["serde"] }
serde = { version = "=1.0.66", features = ["rc"] }
serde_derive = { version = "=1.0.66", features = ["deserialize_in_place"] }
serde_bytes = "0.10"
time = "0.1"
[target.'cfg(target_os = "macos")'.dependencies]
-core-foundation = "0.5"
-core-graphics = "0.13"
+core-foundation = "0.6"
+core-graphics = "0.14"
[target.'cfg(target_os = "windows")'.dependencies]
dwrote = "0.4.1"
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -801,49 +801,51 @@ impl ComplexClipRegion {
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct ClipChainId(pub u64, pub PipelineId);
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum ClipId {
+ Spatial(usize, PipelineId),
Clip(usize, PipelineId),
ClipChain(ClipChainId),
}
const ROOT_REFERENCE_FRAME_CLIP_ID: usize = 0;
const ROOT_SCROLL_NODE_CLIP_ID: usize = 1;
impl ClipId {
pub fn root_scroll_node(pipeline_id: PipelineId) -> ClipId {
- ClipId::Clip(ROOT_SCROLL_NODE_CLIP_ID, pipeline_id)
+ ClipId::Spatial(ROOT_SCROLL_NODE_CLIP_ID, pipeline_id)
}
pub fn root_reference_frame(pipeline_id: PipelineId) -> ClipId {
- ClipId::Clip(ROOT_REFERENCE_FRAME_CLIP_ID, pipeline_id)
+ ClipId::Spatial(ROOT_REFERENCE_FRAME_CLIP_ID, pipeline_id)
}
pub fn pipeline_id(&self) -> PipelineId {
match *self {
+ ClipId::Spatial(_, pipeline_id) |
ClipId::Clip(_, pipeline_id) |
ClipId::ClipChain(ClipChainId(_, pipeline_id)) => pipeline_id,
}
}
pub fn is_root_scroll_node(&self) -> bool {
match *self {
- ClipId::Clip(1, _) => true,
+ ClipId::Spatial(ROOT_SCROLL_NODE_CLIP_ID, _) => true,
_ => false,
}
}
pub fn is_root_reference_frame(&self) -> bool {
match *self {
- ClipId::Clip(1, _) => true,
+ ClipId::Spatial(ROOT_REFERENCE_FRAME_CLIP_ID, _) => true,
_ => false,
}
}
}
/// An external identifier that uniquely identifies a scroll frame independent of its ClipId, which
/// may change from frame to frame. This should be unique within a pipeline. WebRender makes no
/// attempt to ensure uniqueness. The zero value is reserved for use by the root scroll node of
--- a/gfx/webrender_api/src/display_list.rs
+++ b/gfx/webrender_api/src/display_list.rs
@@ -26,17 +26,22 @@ use {ScrollFrameDisplayItem, ScrollSensi
use {StickyFrameDisplayItem, StickyOffsetBounds, TextDisplayItem, TransformStyle, YuvColorSpace};
use {YuvData, YuvImageDisplayItem};
// We don't want to push a long text-run. If a text-run is too long, split it into several parts.
// This needs to be set to (renderer::MAX_VERTEX_TEXTURE_WIDTH - VECS_PER_TEXT_RUN) * 2
pub const MAX_TEXT_RUN_LENGTH: usize = 2040;
// We start at 2, because the root reference is always 0 and the root scroll node is always 1.
-const FIRST_CLIP_ID: usize = 2;
+// TODO(mrobinson): It would be a good idea to eliminate the root scroll frame which is only
+// used by Servo.
+const FIRST_SPATIAL_NODE_INDEX: usize = 2;
+
+// There are no default clips, so we start at the 0 index for clips.
+const FIRST_CLIP_NODE_INDEX: usize = 0;
#[repr(C)]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct ItemRange<T> {
start: usize,
length: usize,
_boo: PhantomData<T>,
}
@@ -74,18 +79,20 @@ pub struct BuiltDisplayList {
#[derive(Copy, Clone, Default, Deserialize, Serialize)]
pub struct BuiltDisplayListDescriptor {
/// The first IPC time stamp: before any work has been done
builder_start_time: u64,
/// The second IPC time stamp: after serialization
builder_finish_time: u64,
/// The third IPC time stamp: just before sending
send_start_time: u64,
- /// The amount of clips ids assigned while building this display list.
- total_clip_ids: usize,
+ /// The amount of clipping nodes created while building this display list.
+ total_clip_nodes: usize,
+ /// The amount of spatial nodes created while building this display list.
+ total_spatial_nodes: usize,
}
pub struct BuiltDisplayListIter<'a> {
list: &'a BuiltDisplayList,
data: &'a [u8],
cur_item: DisplayItem,
cur_stops: ItemRange<GradientStop>,
cur_glyphs: ItemRange<GlyphInstance>,
@@ -141,18 +148,22 @@ impl BuiltDisplayList {
pub fn times(&self) -> (u64, u64, u64) {
(
self.descriptor.builder_start_time,
self.descriptor.builder_finish_time,
self.descriptor.send_start_time,
)
}
- pub fn total_clip_ids(&self) -> usize {
- self.descriptor.total_clip_ids
+ pub fn total_clip_nodes(&self) -> usize {
+ self.descriptor.total_clip_nodes
+ }
+
+ pub fn total_spatial_nodes(&self) -> usize {
+ self.descriptor.total_spatial_nodes
}
pub fn iter(&self) -> BuiltDisplayListIter {
BuiltDisplayListIter::new(self)
}
pub fn get<'de, T: Deserialize<'de>>(&self, range: ItemRange<T>) -> AuxIter<T> {
AuxIter::new(&self.data[range.start .. range.start + range.length])
@@ -512,36 +523,38 @@ impl<'de> Deserialize<'de> for BuiltDisp
use display_item::CompletelySpecificDisplayItem::*;
use display_item::{CompletelySpecificDisplayItem, GenericDisplayItem};
let list = Vec::<GenericDisplayItem<CompletelySpecificDisplayItem>>
::deserialize(deserializer)?;
let mut data = Vec::new();
let mut temp = Vec::new();
- let mut total_clip_ids = FIRST_CLIP_ID;
+ let mut total_clip_nodes = FIRST_CLIP_NODE_INDEX;
+ let mut total_spatial_nodes = FIRST_SPATIAL_NODE_INDEX;
for complete in list {
let item = DisplayItem {
item: match complete.item {
Clip(specific_item, complex_clips) => {
- total_clip_ids += 1;
+ total_clip_nodes += 1;
DisplayListBuilder::push_iter_impl(&mut temp, complex_clips);
SpecificDisplayItem::Clip(specific_item)
},
ClipChain(specific_item, clip_chain_ids) => {
DisplayListBuilder::push_iter_impl(&mut temp, clip_chain_ids);
SpecificDisplayItem::ClipChain(specific_item)
}
ScrollFrame(specific_item, complex_clips) => {
- total_clip_ids += 2;
+ total_spatial_nodes += 1;
+ total_clip_nodes += 1;
DisplayListBuilder::push_iter_impl(&mut temp, complex_clips);
SpecificDisplayItem::ScrollFrame(specific_item)
},
StickyFrame(specific_item) => {
- total_clip_ids += 1;
+ total_spatial_nodes += 1;
SpecificDisplayItem::StickyFrame(specific_item)
}
Rectangle(specific_item) => SpecificDisplayItem::Rectangle(specific_item),
ClearRectangle => SpecificDisplayItem::ClearRectangle,
Line(specific_item) => SpecificDisplayItem::Line(specific_item),
Text(specific_item, glyphs) => {
DisplayListBuilder::push_iter_impl(&mut temp, glyphs);
SpecificDisplayItem::Text(specific_item)
@@ -549,26 +562,26 @@ impl<'de> Deserialize<'de> for BuiltDisp
Image(specific_item) => SpecificDisplayItem::Image(specific_item),
YuvImage(specific_item) => SpecificDisplayItem::YuvImage(specific_item),
Border(specific_item) => SpecificDisplayItem::Border(specific_item),
BoxShadow(specific_item) => SpecificDisplayItem::BoxShadow(specific_item),
Gradient(specific_item) => SpecificDisplayItem::Gradient(specific_item),
RadialGradient(specific_item) =>
SpecificDisplayItem::RadialGradient(specific_item),
Iframe(specific_item) => {
- total_clip_ids += 1;
+ total_clip_nodes += 1;
SpecificDisplayItem::Iframe(specific_item)
}
PushStackingContext(specific_item, filters) => {
DisplayListBuilder::push_iter_impl(&mut temp, filters);
SpecificDisplayItem::PushStackingContext(specific_item)
},
PopStackingContext => SpecificDisplayItem::PopStackingContext,
PushReferenceFrame(specific_item) => {
- total_clip_ids += 1;
+ total_spatial_nodes += 1;
SpecificDisplayItem::PushReferenceFrame(specific_item)
}
PopReferenceFrame => SpecificDisplayItem::PopReferenceFrame,
SetGradientStops(stops) => {
DisplayListBuilder::push_iter_impl(&mut temp, stops);
SpecificDisplayItem::SetGradientStops
},
PushShadow(specific_item) => SpecificDisplayItem::PushShadow(specific_item),
@@ -583,17 +596,18 @@ impl<'de> Deserialize<'de> for BuiltDisp
}
Ok(BuiltDisplayList {
data,
descriptor: BuiltDisplayListDescriptor {
builder_start_time: 0,
builder_finish_time: 1,
send_start_time: 0,
- total_clip_ids,
+ total_clip_nodes,
+ total_spatial_nodes,
},
})
}
}
// This is a replacement for bincode::serialize_into(&vec)
// The default implementation Write for Vec will basically
// call extend_from_slice(). Serde ends up calling that for every
@@ -808,26 +822,28 @@ impl<'a, 'b> Read for UnsafeReader<'a, '
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct SaveState {
dl_len: usize,
clip_stack_len: usize,
- next_clip_id: usize,
+ next_clip_index: usize,
+ next_spatial_index: usize,
next_clip_chain_id: u64,
}
#[derive(Clone)]
pub struct DisplayListBuilder {
pub data: Vec<u8>,
pub pipeline_id: PipelineId,
clip_stack: Vec<ClipAndScrollInfo>,
- next_clip_id: usize,
+ next_clip_index: usize,
+ next_spatial_index: usize,
next_clip_chain_id: u64,
builder_start_time: u64,
/// The size of the content of this display list. This is used to allow scrolling
/// outside the bounds of the display list items themselves.
content_size: LayoutSize,
save_state: Option<SaveState>,
}
@@ -845,17 +861,18 @@ impl DisplayListBuilder {
let start_time = precise_time_ns();
DisplayListBuilder {
data: Vec::with_capacity(capacity),
pipeline_id,
clip_stack: vec![
ClipAndScrollInfo::simple(ClipId::root_scroll_node(pipeline_id)),
],
- next_clip_id: FIRST_CLIP_ID,
+ next_clip_index: FIRST_CLIP_NODE_INDEX,
+ next_spatial_index: FIRST_SPATIAL_NODE_INDEX,
next_clip_chain_id: 0,
builder_start_time: start_time,
content_size,
save_state: None,
}
}
/// Return the content size for this display list
@@ -871,28 +888,30 @@ impl DisplayListBuilder {
/// * Doesn't support nested saves.
/// * Must call `clear_save()` if the restore becomes unnecessary.
pub fn save(&mut self) {
assert!(self.save_state.is_none(), "DisplayListBuilder doesn't support nested saves");
self.save_state = Some(SaveState {
clip_stack_len: self.clip_stack.len(),
dl_len: self.data.len(),
- next_clip_id: self.next_clip_id,
+ next_clip_index: self.next_clip_index,
+ next_spatial_index: self.next_spatial_index,
next_clip_chain_id: self.next_clip_chain_id,
});
}
/// Restores the state of the builder to when `save()` was last called.
pub fn restore(&mut self) {
let state = self.save_state.take().expect("No save to restore DisplayListBuilder from");
self.clip_stack.truncate(state.clip_stack_len);
self.data.truncate(state.dl_len);
- self.next_clip_id = state.next_clip_id;
+ self.next_clip_index = state.next_clip_index;
+ self.next_spatial_index = state.next_spatial_index;
self.next_clip_chain_id = state.next_clip_chain_id;
}
/// Discards the builder's save (indicating the attempted operation was successful).
pub fn clear_save(&mut self) {
self.save_state.take().expect("No save to clear in DisplayListBuilder");
}
@@ -1283,17 +1302,17 @@ impl DisplayListBuilder {
}
pub fn push_reference_frame(
&mut self,
info: &LayoutPrimitiveInfo,
transform: Option<PropertyBinding<LayoutTransform>>,
perspective: Option<LayoutTransform>,
) -> ClipId {
- let id = self.generate_clip_id();
+ let id = self.generate_spatial_index();
let item = SpecificDisplayItem::PushReferenceFrame(PushReferenceFrameDisplayListItem {
reference_frame: ReferenceFrame {
transform,
perspective,
id,
},
});
self.push_item(item, info);
@@ -1333,19 +1352,24 @@ impl DisplayListBuilder {
pub fn push_stops(&mut self, stops: &[GradientStop]) {
if stops.is_empty() {
return;
}
self.push_new_empty_item(SpecificDisplayItem::SetGradientStops);
self.push_iter(stops);
}
- fn generate_clip_id(&mut self) -> ClipId {
- self.next_clip_id += 1;
- ClipId::Clip(self.next_clip_id - 1, self.pipeline_id)
+ fn generate_clip_index(&mut self) -> ClipId {
+ self.next_clip_index += 1;
+ ClipId::Clip(self.next_clip_index - 1, self.pipeline_id)
+ }
+
+ fn generate_spatial_index(&mut self) -> ClipId {
+ self.next_spatial_index += 1;
+ ClipId::Spatial(self.next_spatial_index - 1, self.pipeline_id)
}
fn generate_clip_chain_id(&mut self) -> ClipChainId {
self.next_clip_chain_id += 1;
ClipChainId(self.next_clip_chain_id - 1, self.pipeline_id)
}
pub fn define_scroll_frame<I>(
@@ -1381,18 +1405,18 @@ impl DisplayListBuilder {
complex_clips: I,
image_mask: Option<ImageMask>,
scroll_sensitivity: ScrollSensitivity,
) -> ClipId
where
I: IntoIterator<Item = ComplexClipRegion>,
I::IntoIter: ExactSizeIterator + Clone,
{
- let clip_id = self.generate_clip_id();
- let scroll_frame_id = self.generate_clip_id();
+ let clip_id = self.generate_clip_index();
+ let scroll_frame_id = self.generate_spatial_index();
let item = SpecificDisplayItem::ScrollFrame(ScrollFrameDisplayItem {
clip_id,
scroll_frame_id,
external_id,
image_mask,
scroll_sensitivity,
});
@@ -1446,17 +1470,17 @@ impl DisplayListBuilder {
clip_rect: LayoutRect,
complex_clips: I,
image_mask: Option<ImageMask>,
) -> ClipId
where
I: IntoIterator<Item = ComplexClipRegion>,
I::IntoIter: ExactSizeIterator + Clone,
{
- let id = self.generate_clip_id();
+ let id = self.generate_clip_index();
let item = SpecificDisplayItem::Clip(ClipDisplayItem {
id,
image_mask,
});
let info = LayoutPrimitiveInfo::new(clip_rect);
let scrollinfo = ClipAndScrollInfo::simple(parent);
@@ -1469,17 +1493,17 @@ impl DisplayListBuilder {
&mut self,
frame_rect: LayoutRect,
margins: SideOffsets2D<Option<f32>>,
vertical_offset_bounds: StickyOffsetBounds,
horizontal_offset_bounds: StickyOffsetBounds,
previously_applied_offset: LayoutVector2D,
) -> ClipId {
- let id = self.generate_clip_id();
+ let id = self.generate_spatial_index();
let item = SpecificDisplayItem::StickyFrame(StickyFrameDisplayItem {
id,
margins,
vertical_offset_bounds,
horizontal_offset_bounds,
previously_applied_offset,
});
@@ -1507,17 +1531,17 @@ impl DisplayListBuilder {
pub fn push_iframe(
&mut self,
info: &LayoutPrimitiveInfo,
pipeline_id: PipelineId,
ignore_missing_pipeline: bool
) {
let item = SpecificDisplayItem::Iframe(IframeDisplayItem {
- clip_id: self.generate_clip_id(),
+ clip_id: self.generate_clip_index(),
pipeline_id,
ignore_missing_pipeline,
});
self.push_item(item, info);
}
pub fn push_shadow(&mut self, info: &LayoutPrimitiveInfo, shadow: Shadow) {
self.push_item(SpecificDisplayItem::PushShadow(shadow), info);
@@ -1536,15 +1560,16 @@ impl DisplayListBuilder {
(
self.pipeline_id,
self.content_size,
BuiltDisplayList {
descriptor: BuiltDisplayListDescriptor {
builder_start_time: self.builder_start_time,
builder_finish_time: end_time,
send_start_time: 0,
- total_clip_ids: self.next_clip_id,
+ total_clip_nodes: self.next_clip_index,
+ total_spatial_nodes: self.next_spatial_index,
},
data: self.data,
},
)
}
}
--- a/gfx/webrender_bindings/Cargo.toml
+++ b/gfx/webrender_bindings/Cargo.toml
@@ -21,12 +21,12 @@ path = "../webrender"
version = "0.57.2"
default-features = false
features = ["capture", "serialize_program"]
[target.'cfg(target_os = "windows")'.dependencies]
dwrote = "0.4.1"
[target.'cfg(target_os = "macos")'.dependencies]
-core-foundation = "0.5"
-core-graphics = "0.13"
+core-foundation = "0.6"
+core-graphics = "0.14"
foreign-types = "0.3.0"
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-0e9563688e575cd662570f54bc9d6f849040dbf8
+e600bfe68efac6416ce2e8091d7344744771f6db
--- a/gfx/wrench/Cargo.toml
+++ b/gfx/wrench/Cargo.toml
@@ -7,40 +7,40 @@ license = "MPL-2.0"
[dependencies]
base64 = "0.6"
bincode = "1.0"
byteorder = "1.0"
env_logger = { version = "0.5", optional = true }
euclid = "0.17"
gleam = "0.5"
-glutin = "0.15"
+glutin = "0.17"
app_units = "0.6"
image = "0.19"
clap = { version = "2", features = ["yaml"] }
lazy_static = "1"
log = "0.4"
yaml-rust = { git = "https://github.com/vvuk/yaml-rust", features = ["preserve_order"] }
serde_json = "1.0"
ron = "0.1.5"
time = "0.1"
crossbeam = "0.2"
osmesa-sys = { version = "0.1.2", optional = true }
osmesa-src = { git = "https://github.com/jrmuizel/osmesa-src", optional = true, branch = "serialize" }
webrender = {path = "../webrender", features=["capture","replay","debugger","png","profiler"]}
webrender_api = {path = "../webrender_api", features=["serialize","deserialize"]}
-winit = "0.13"
+winit = "0.16"
serde = {version = "1.0", features = ["derive"] }
[target.'cfg(target_os = "macos")'.dependencies]
-core-graphics = "0.13"
-core-foundation = "0.5"
+core-graphics = "0.14"
+core-foundation = "0.6"
[features]
headless = [ "osmesa-sys", "osmesa-src" ]
pathfinder = [ "webrender/pathfinder" ]
[target.'cfg(target_os = "windows")'.dependencies]
dwrote = "0.4.1"
mozangle = {version = "0.1.5", features = ["egl"]}
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
-font-loader = "0.6"
+font-loader = "0.7"
--- a/gfx/wrench/src/angle.rs
+++ b/gfx/wrench/src/angle.rs
@@ -1,13 +1,15 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use glutin::{self, ContextBuilder, CreationError};
+#[cfg(not(windows))]
+use glutin::dpi::PhysicalSize;
use winit::{EventsLoop, Window, WindowBuilder};
#[cfg(not(windows))]
pub enum Context {}
#[cfg(windows)]
pub use ::egl::Context;
@@ -60,13 +62,13 @@ impl glutin::GlContext for Context {
fn get_api(&self) -> glutin::Api {
match *self {}
}
fn get_pixel_format(&self) -> glutin::PixelFormat {
match *self {}
}
- fn resize(&self, _: u32, _: u32) {
+ fn resize(&self, _: PhysicalSize) {
match *self {}
}
}
--- a/gfx/wrench/src/egl.rs
+++ b/gfx/wrench/src/egl.rs
@@ -10,16 +10,17 @@ use glutin::CreationError;
use glutin::GlAttributes;
use glutin::GlContext;
use glutin::GlRequest;
use glutin::PixelFormat;
use glutin::PixelFormatRequirements;
use glutin::ReleaseBehavior;
use glutin::Robustness;
use glutin::Api;
+use glutin::dpi::PhysicalSize;
use std::ffi::{CStr, CString};
use std::os::raw::c_int;
use std::{mem, ptr};
use std::cell::Cell;
use mozangle::egl::ffi as egl;
mod ffi {
@@ -201,17 +202,17 @@ impl GlContext for Context {
}
#[inline]
fn get_pixel_format(&self) -> PixelFormat {
self.pixel_format.clone()
}
#[inline]
- fn resize(&self, _: u32, _: u32) {}
+ fn resize(&self, _: PhysicalSize) {}
}
unsafe impl Send for Context {}
unsafe impl Sync for Context {}
impl Drop for Context {
fn drop(&mut self) {
unsafe {
--- a/gfx/wrench/src/main.rs
+++ b/gfx/wrench/src/main.rs
@@ -58,16 +58,17 @@ mod yaml_frame_reader;
mod yaml_frame_writer;
mod yaml_helper;
#[cfg(target_os = "macos")]
mod cgfont_to_data;
use binary_frame_reader::BinaryFrameReader;
use gleam::gl;
use glutin::GlContext;
+use glutin::dpi::{LogicalPosition, LogicalSize};
use perf::PerfHarness;
use png::save_flipped;
use rawtest::RawtestHarness;
use reftest::{ReftestHarness, ReftestOptions};
#[cfg(feature = "headless")]
use std::ffi::CString;
#[cfg(feature = "headless")]
use std::mem;
@@ -178,45 +179,49 @@ impl WindowWrapper {
WindowWrapper::Angle(_, ref context, _) => context.swap_buffers().unwrap(),
WindowWrapper::Headless(_, _) => {}
}
}
fn get_inner_size(&self) -> DeviceUintSize {
//HACK: `winit` needs to figure out its hidpi story...
#[cfg(target_os = "macos")]
- fn inner_size(window: &winit::Window) -> (u32, u32) {
- let (w, h) = window.get_inner_size().unwrap();
- let factor = window.hidpi_factor();
- ((w as f32 * factor) as _, (h as f32 * factor) as _)
+ fn inner_size(window: &winit::Window) -> LogicalSize {
+ let LogicalSize { width, height } = window.get_inner_size().unwrap();
+ let factor = window.get_hidpi_factor();
+ LogicalSize::new(width * factor, height * factor)
}
#[cfg(not(target_os = "macos"))]
- fn inner_size(window: &winit::Window) -> (u32, u32) {
+ fn inner_size(window: &winit::Window) -> LogicalSize {
window.get_inner_size().unwrap()
}
- let (w, h) = match *self {
+ let LogicalSize { width, height } = match *self {
WindowWrapper::Window(ref window, _) => inner_size(window.window()),
WindowWrapper::Angle(ref window, ..) => inner_size(window),
- WindowWrapper::Headless(ref context, _) => (context.width, context.height),
+ WindowWrapper::Headless(ref context, _) => LogicalSize::new(context.width as f64, context.height as f64),
};
- DeviceUintSize::new(w, h)
+ DeviceUintSize::new(width as u32, height as u32)
}
fn hidpi_factor(&self) -> f32 {
match *self {
- WindowWrapper::Window(ref window, _) => window.hidpi_factor(),
- WindowWrapper::Angle(ref window, ..) => window.hidpi_factor(),
+ WindowWrapper::Window(ref window, _) => window.get_hidpi_factor() as f32,
+ WindowWrapper::Angle(ref window, ..) => window.get_hidpi_factor() as f32,
WindowWrapper::Headless(_, _) => 1.0,
}
}
fn resize(&mut self, size: DeviceUintSize) {
match *self {
- WindowWrapper::Window(ref mut window, _) => window.set_inner_size(size.width, size.height),
- WindowWrapper::Angle(ref mut window, ..) => window.set_inner_size(size.width, size.height),
+ WindowWrapper::Window(ref mut window, _) => {
+ window.set_inner_size(LogicalSize::new(size.width as f64, size.height as f64))
+ },
+ WindowWrapper::Angle(ref mut window, ..) => {
+ window.set_inner_size(LogicalSize::new(size.width as f64, size.height as f64))
+ },
WindowWrapper::Headless(_, _) => unimplemented!(), // requites Glutin update
}
}
fn set_title(&mut self, title: &str) {
match *self {
WindowWrapper::Window(ref window, _) => window.set_title(title),
WindowWrapper::Angle(ref window, ..) => window.set_title(title),
@@ -254,17 +259,17 @@ fn make_window(
.with_gl(glutin::GlRequest::GlThenGles {
opengl_version: (3, 2),
opengles_version: (3, 0),
})
.with_vsync(vsync);
let window_builder = winit::WindowBuilder::new()
.with_title("WRench")
.with_multitouch()
- .with_dimensions(size.width, size.height);
+ .with_dimensions(LogicalSize::new(size.width as f64, size.height as f64));
let init = |context: &glutin::GlContext| {
unsafe {
context
.make_current()
.expect("unable to make context current!");
}
@@ -578,17 +583,17 @@ fn render<'a>(
winit::Event::WindowEvent { event, .. } => match event {
winit::WindowEvent::CloseRequested => {
return winit::ControlFlow::Break;
}
winit::WindowEvent::Refresh |
winit::WindowEvent::Focused(..) => {
do_render = true;
}
- winit::WindowEvent::CursorMoved { position: (x, y), .. } => {
+ winit::WindowEvent::CursorMoved { position: LogicalPosition { x, y }, .. } => {
cursor_position = WorldPoint::new(x as f32, y as f32);
do_render = true;
}
winit::WindowEvent::KeyboardInput {
input: winit::KeyboardInput {
state: winit::ElementState::Pressed,
virtual_keycode: Some(vk),
..