From 8c3bf9c5e01b5114b7eac2856a00c68eda607cd0 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Mon, 19 Feb 2018 19:16:17 +0100 Subject: [PATCH] Use FastTransform almost everywhere FastTransform used to be called TransformOrOffset and this change uses it in a lot of places that we used to use TypedTransform3D directly. This helps to avoid the cost of expensive matrix math and allows us to more liberally call things like `inverse()` on our transformations, since the worst case is that we will be retrieving the already-cached inverted matrix. This decreases the amount of time that FrameBuilder::build takes in my local CPU profiles. --- webrender/src/batch.rs | 8 +- webrender/src/clip.rs | 8 +- webrender/src/clip_scroll_node.rs | 71 ++++----- webrender/src/clip_scroll_tree.rs | 18 +-- webrender/src/frame_builder.rs | 21 ++- webrender/src/hit_test.rs | 8 +- webrender/src/prim_store.rs | 27 ++-- webrender/src/util.rs | 236 ++++++++++++++++++++++++------ 8 files changed, 274 insertions(+), 123 deletions(-) diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index ddb939477b..98d0ad868d 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -811,7 +811,7 @@ impl AlphaBatchBuilder { let font_transform = if is_shadow { None } else { - Some(&scroll_node.transform) + Some(scroll_node.transform) }; let font = text_cpu.get_font( @@ -977,8 +977,10 @@ impl AlphaBatchBuilder { if is_in_3d_context { // Push into parent plane splitter. - let real_xf = &ctx.clip_scroll_tree.nodes[&reference_frame_id].world_content_transform; - + let real_xf = &ctx.clip_scroll_tree + .nodes[&reference_frame_id] + .world_content_transform + .into(); let polygon = make_polygon( real_local_rect, &real_xf, diff --git a/webrender/src/clip.rs b/webrender/src/clip.rs index 59ed948ac2..f14474f0b6 100644 --- a/webrender/src/clip.rs +++ b/webrender/src/clip.rs @@ -3,8 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BorderRadius, ClipMode, ComplexClipRegion, DeviceIntRect, DevicePixelScale, ImageMask}; -use api::{ImageRendering, LayerRect, LayerToWorldTransform, LayoutPoint, LayoutVector2D}; -use api::LocalClip; +use api::{ImageRendering, LayerRect, LayoutPoint, LayoutVector2D, LocalClip}; use border::{BorderCornerClipSource, ensure_no_corner_overlap}; use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId}; use ellipse::Ellipse; @@ -13,7 +12,8 @@ use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks}; use gpu_types::ClipScrollNodeIndex; use prim_store::{ClipData, ImageMaskData}; use resource_cache::{ImageRequest, ResourceCache}; -use util::{MaxRect, MatrixHelpers, calculate_screen_bounding_rect, extract_inner_rect_safe}; +use util::{LayerToWorldFastTransform, MaxRect, calculate_screen_bounding_rect}; +use util::extract_inner_rect_safe; use std::rc::Rc; pub type ClipStore = FreeList; @@ -252,7 +252,7 @@ impl ClipSources { pub fn get_screen_bounds( &self, - transform: &LayerToWorldTransform, + transform: &LayerToWorldFastTransform, device_pixel_scale: DevicePixelScale, ) -> (DeviceIntRect, Option) { // If this translation isn't axis aligned or has a perspective component, don't try to diff --git a/webrender/src/clip_scroll_node.rs b/webrender/src/clip_scroll_node.rs index c699a7912f..1a5755c6b3 100644 --- a/webrender/src/clip_scroll_node.rs +++ b/webrender/src/clip_scroll_node.rs @@ -3,9 +3,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{ClipId, DevicePixelScale, ExternalScrollId, LayerPixel, LayerPoint, LayerRect}; -use api::{LayerSize, LayerToWorldTransform, LayerTransform, LayerVector2D, LayoutTransform}; -use api::{LayoutVector2D, PipelineId, PropertyBinding, ScrollClamping, ScrollEventPhase}; -use api::{ScrollLocation, ScrollSensitivity, StickyOffsetBounds, WorldPoint}; +use api::{LayerSize, LayerVector2D, LayoutTransform, LayoutVector2D, PipelineId, PropertyBinding}; +use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollSensitivity, StickyOffsetBounds}; +use api::WorldPoint; use clip::{ClipChain, ClipSourcesHandle, ClipStore, ClipWorkItem}; use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId, TransformUpdateState}; use euclid::SideOffsets2D; @@ -15,7 +15,8 @@ use gpu_types::{ClipScrollNodeIndex, ClipScrollNodeData}; use resource_cache::ResourceCache; use scene::SceneProperties; use spring::{DAMPING, STIFFNESS, Spring}; -use util::{MatrixHelpers, TransformOrOffset, TransformedRectKind}; +use util::{LayerToWorldFastTransform, LayerFastTransform, LayoutFastTransform}; +use util::{TransformedRectKind}; #[cfg(target_os = "macos")] const CAN_OVERSCROLL: bool = true; @@ -91,10 +92,10 @@ pub struct ClipScrollNode { /// between our reference frame and this node. For reference frames, we also include /// whatever local transformation this reference frame provides. This can be combined /// with the local_viewport_rect to get its position in world space. - pub world_viewport_transform: LayerToWorldTransform, + pub world_viewport_transform: LayerToWorldFastTransform, /// World transform for content transformed by this node. - pub world_content_transform: LayerToWorldTransform, + pub world_content_transform: LayerToWorldFastTransform, /// Pipeline that this layer belongs to pub pipeline_id: PipelineId, @@ -119,7 +120,7 @@ pub struct ClipScrollNode { /// 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: TransformOrOffset, + pub coordinate_system_relative_transform: LayerFastTransform, /// A linear ID / index of this clip-scroll node. Used as a reference to /// pass to shaders, to allow them to fetch a given clip-scroll node. @@ -135,15 +136,15 @@ impl ClipScrollNode { ) -> Self { ClipScrollNode { local_viewport_rect: *rect, - world_viewport_transform: LayerToWorldTransform::identity(), - world_content_transform: LayerToWorldTransform::identity(), + world_viewport_transform: LayerToWorldFastTransform::identity(), + world_content_transform: LayerToWorldFastTransform::identity(), parent: parent_id, children: Vec::new(), pipeline_id, node_type: node_type, invertible: true, coordinate_system_id: CoordinateSystemId(0), - coordinate_system_relative_transform: TransformOrOffset::zero(), + coordinate_system_relative_transform: LayerFastTransform::identity(), node_data_index: ClipScrollNodeIndex(0), } } @@ -177,10 +178,12 @@ impl ClipScrollNode { 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: LayerTransform::identity(), + resolved_transform: LayerFastTransform::identity(), source_transform: source_transform.unwrap_or(PropertyBinding::Value(identity)), - source_perspective: source_perspective.unwrap_or(identity), + source_perspective: source_perspective, origin_in_parent_reference_frame, invertible: true, }; @@ -258,8 +261,8 @@ impl ClipScrollNode { pub fn mark_uninvertible(&mut self) { self.invertible = false; - self.world_content_transform = LayerToWorldTransform::identity(); - self.world_viewport_transform = LayerToWorldTransform::identity(); + self.world_content_transform = LayerToWorldFastTransform::identity(); + self.world_viewport_transform = LayerToWorldFastTransform::identity(); } pub fn push_gpu_node_data(&mut self, node_data: &mut Vec) { @@ -274,7 +277,7 @@ impl ClipScrollNode { TransformedRectKind::Complex }; let data = ClipScrollNodeData { - transform: self.world_content_transform, + transform: self.world_content_transform.into(), transform_kind: transform_kind as u32 as f32, padding: [0.0; 3], }; @@ -303,9 +306,9 @@ impl ClipScrollNode { self.update_transform(state, next_coordinate_system_id, scene_properties); - // If this node is a reference frame, we check if the determinant is 0, which means it - // has a non-invertible matrix. For non-reference-frames we assume that they will - // produce only additional translations which should be invertible. + // 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::ReferenceFrame(info) if !info.invertible => { self.mark_uninvertible(); @@ -362,7 +365,7 @@ impl ClipScrollNode { let mut clip_chain = clip_chains[state.parent_clip_chain_index.0].new_with_added_node( work_item, - self.coordinate_system_relative_transform.apply(&local_outer_rect), + self.coordinate_system_relative_transform.transform_rect(&local_outer_rect), screen_outer_rect, screen_inner_rect, ); @@ -398,7 +401,7 @@ impl ClipScrollNode { // provided by our own sticky positioning. let accumulated_offset = state.parent_accumulated_scroll_offset + sticky_offset; self.world_viewport_transform = if accumulated_offset != LayerVector2D::zero() { - state.parent_reference_frame_transform.pre_translate(accumulated_offset.to_3d()) + state.parent_reference_frame_transform.pre_translate(&accumulated_offset) } else { state.parent_reference_frame_transform }; @@ -407,7 +410,7 @@ impl ClipScrollNode { // whatever scrolling offset we supply as well. let scroll_offset = self.scroll_offset(); self.world_content_transform = if scroll_offset != LayerVector2D::zero() { - self.world_viewport_transform.pre_translate(scroll_offset.to_3d()) + self.world_viewport_transform.pre_translate(&scroll_offset) } else { self.world_viewport_transform }; @@ -437,12 +440,10 @@ impl ClipScrollNode { // Resolve the transform against any property bindings. let source_transform = scene_properties.resolve_layout_transform(&info.source_transform); - info.resolved_transform = LayerTransform::create_translation( - info.origin_in_parent_reference_frame.x, - info.origin_in_parent_reference_frame.y, - 0.0 - ).pre_mul(&source_transform) - .pre_mul(&info.source_perspective); + info.resolved_transform = + LayerFastTransform::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 @@ -450,12 +451,14 @@ impl ClipScrollNode { // whatever local transformation this reference frame provides. This can be combined // with the local_viewport_rect to get its position in world space. let relative_transform = info.resolved_transform - .post_translate(state.parent_accumulated_scroll_offset.to_3d()); - self.world_viewport_transform = state.parent_reference_frame_transform - .pre_mul(&relative_transform.with_destination::()); + .post_translate(state.parent_accumulated_scroll_offset) + .to_transform() + .with_destination::(); + self.world_viewport_transform = + state.parent_reference_frame_transform.pre_mul(&relative_transform.into()); self.world_content_transform = self.world_viewport_transform; - info.invertible = relative_transform.determinant() != 0.0; + info.invertible = self.world_viewport_transform.is_invertible(); if !info.invertible { return; } @@ -465,7 +468,7 @@ impl ClipScrollNode { match state.coordinate_system_relative_transform.update(relative_transform) { Some(offset) => self.coordinate_system_relative_transform = offset, None => { - self.coordinate_system_relative_transform = TransformOrOffset::zero(); + self.coordinate_system_relative_transform = LayerFastTransform::identity(); state.current_coordinate_system_id = *next_coordinate_system_id; next_coordinate_system_id.advance(); } @@ -844,14 +847,14 @@ impl ScrollFrameInfo { 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: LayerTransform, + pub resolved_transform: LayerFastTransform, /// 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, - pub source_perspective: 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 diff --git a/webrender/src/clip_scroll_tree.rs b/webrender/src/clip_scroll_tree.rs index c3adee1b30..b456959233 100644 --- a/webrender/src/clip_scroll_tree.rs +++ b/webrender/src/clip_scroll_tree.rs @@ -3,8 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{ClipId, DeviceIntRect, DevicePixelScale, ExternalScrollId, LayerPoint, LayerRect}; -use api::{LayerToWorldTransform, LayerVector2D, PipelineId, ScrollClamping, ScrollEventPhase}; -use api::{ScrollLocation, ScrollNodeState, WorldPoint}; +use api::{LayerVector2D, PipelineId, ScrollClamping, ScrollEventPhase, ScrollLocation}; +use api::{ScrollNodeState, WorldPoint}; use clip::{ClipChain, ClipSourcesHandle, ClipStore}; use clip_scroll_node::{ClipScrollNode, NodeType, ScrollFrameInfo, StickyFrameInfo}; use gpu_cache::GpuCache; @@ -13,7 +13,7 @@ use internal_types::{FastHashMap, FastHashSet}; use print_tree::{PrintTree, PrintTreePrinter}; use resource_cache::ResourceCache; use scene::SceneProperties; -use util::TransformOrOffset; +use util::{LayerFastTransform, LayerToWorldFastTransform}; pub type ScrollStates = FastHashMap; @@ -88,7 +88,7 @@ pub struct ClipScrollTree { #[derive(Clone)] pub struct TransformUpdateState { - pub parent_reference_frame_transform: LayerToWorldTransform, + pub parent_reference_frame_transform: LayerToWorldFastTransform, pub parent_accumulated_scroll_offset: LayerVector2D, pub nearest_scrolling_ancestor_offset: LayerVector2D, pub nearest_scrolling_ancestor_viewport: LayerRect, @@ -103,7 +103,7 @@ pub struct TransformUpdateState { pub current_coordinate_system_id: CoordinateSystemId, /// Transform from the coordinate system that started this compatible coordinate system. - pub coordinate_system_relative_transform: TransformOrOffset, + pub coordinate_system_relative_transform: LayerFastTransform, /// 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 @@ -329,17 +329,13 @@ impl ClipScrollTree { let root_reference_frame_id = self.root_reference_frame_id(); let mut state = TransformUpdateState { - parent_reference_frame_transform: LayerToWorldTransform::create_translation( - pan.x, - pan.y, - 0.0, - ), + parent_reference_frame_transform: LayerVector2D::new(pan.x, pan.y).into(), parent_accumulated_scroll_offset: LayerVector2D::zero(), nearest_scrolling_ancestor_offset: LayerVector2D::zero(), nearest_scrolling_ancestor_viewport: LayerRect::zero(), parent_clip_chain_index: ClipChainIndex(0), current_coordinate_system_id: CoordinateSystemId::root(), - coordinate_system_relative_transform: TransformOrOffset::zero(), + coordinate_system_relative_transform: LayerFastTransform::identity(), invertible: true, }; let mut next_coordinate_system_id = state.current_coordinate_system_id.next(); diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index b856b74100..7f6d7fcea8 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -6,11 +6,10 @@ use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayList, ClipId, use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixelScale, DeviceUintPoint}; use api::{DeviceUintRect, DeviceUintSize, DocumentLayer, Epoch, ExtendMode, ExternalScrollId}; use api::{FontRenderMode, GlyphInstance, GlyphOptions, GradientStop, ImageKey, ImageRendering}; -use api::{ItemRange, LayerPoint, LayerPrimitiveInfo, LayerRect, LayerSize, LayerTransform}; -use api::{LayerVector2D, LayoutTransform, LayoutVector2D, LineOrientation, LineStyle, LocalClip}; -use api::{PipelineId, PremultipliedColorF, PropertyBinding, RepeatMode, ScrollSensitivity, Shadow}; -use api::{TexelRect, TileOffset, TransformStyle, WorldPoint, WorldToLayerTransform, YuvColorSpace}; -use api::YuvData; +use api::{ItemRange, LayerPoint, LayerPrimitiveInfo, LayerRect, LayerSize, LayerVector2D}; +use api::{LayoutTransform, LayoutVector2D, LineOrientation, LineStyle, LocalClip, PipelineId}; +use api::{PremultipliedColorF, PropertyBinding, RepeatMode, ScrollSensitivity, Shadow, TexelRect}; +use api::{TileOffset, TransformStyle, WorldPoint, YuvColorSpace, YuvData}; use app_units::Au; use border::ImageBorderSegment; use clip::{ClipChain, ClipRegion, ClipSource, ClipSources, ClipStore}; @@ -35,7 +34,8 @@ use scene::{ScenePipeline, SceneProperties}; use std::{mem, usize, f32}; use tiling::{CompositeOps, Frame, RenderPass, RenderTargetKind}; use tiling::{RenderPassKind, RenderTargetContext, ScrollbarPrimitive}; -use util::{self, MaxRect, pack_as_float, RectHelpers, recycle_vec}; +use util::{self, MaxRect, RectHelpers, WorldToLayerFastTransform, pack_as_float, recycle_vec}; + #[derive(Debug)] pub struct ScrollbarInfo(pub ClipId, pub LayerRect); @@ -133,7 +133,7 @@ pub struct PictureContext<'a> { pub original_reference_frame_id: Option, pub display_list: &'a BuiltDisplayList, pub draw_text_transformed: bool, - pub inv_world_transform: Option, + pub inv_world_transform: Option, } pub struct PictureState { @@ -673,11 +673,8 @@ impl FrameBuilder { let root_id = clip_scroll_tree.root_reference_frame_id(); if let Some(root_node) = clip_scroll_tree.nodes.get_mut(&root_id) { if let NodeType::ReferenceFrame(ref mut info) = root_node.node_type { - info.resolved_transform = LayerTransform::create_translation( - viewport_offset.x, - viewport_offset.y, - 0.0, - ); + info.resolved_transform = + LayerVector2D::new(viewport_offset.x, viewport_offset.y).into(); } } } diff --git a/webrender/src/hit_test.rs b/webrender/src/hit_test.rs index d4b7b2b689..7dac3b28c4 100644 --- a/webrender/src/hit_test.rs +++ b/webrender/src/hit_test.rs @@ -3,13 +3,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BorderRadius, ClipId, ClipMode, HitTestFlags, HitTestItem, HitTestResult, ItemTag}; -use api::{LayerPoint, LayerPrimitiveInfo, LayerRect, LayerToWorldTransform, LocalClip, PipelineId}; -use api::WorldPoint; +use api::{LayerPoint, LayerPrimitiveInfo, LayerRect, LocalClip, PipelineId, WorldPoint}; use clip::{ClipSource, ClipStore, Contains, rounded_rectangle_contains_point}; use clip_scroll_node::{ClipScrollNode, NodeType}; use clip_scroll_tree::{ClipChainIndex, ClipScrollTree}; use internal_types::FastHashMap; use prim_store::ScrollNodeAndClipChain; +use util::LayerToWorldFastTransform; /// 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, @@ -20,10 +20,10 @@ pub struct HitTestClipScrollNode { regions: Vec, /// World transform for content transformed by this node. - world_content_transform: LayerToWorldTransform, + world_content_transform: LayerToWorldFastTransform, /// World viewport transform for content transformed by this node. - world_viewport_transform: LayerToWorldTransform, + world_viewport_transform: LayerToWorldFastTransform, /// Origin of the viewport of the node, used to calculate node-relative positions. node_origin: LayerPoint, diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 263b079d41..a2fd82cf8d 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -6,7 +6,7 @@ use api::{AlphaType, BorderRadius, BuiltDisplayList, ClipId, ClipMode, ColorF, C use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode, FontRenderMode}; use api::{GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag}; use api::{LayerPoint, LayerRect, LayerSize, LayerToWorldTransform, LayerVector2D, LineOrientation}; -use api::{LineStyle, PremultipliedColorF, WorldToLayerTransform, YuvColorSpace, YuvFormat}; +use api::{LineStyle, PremultipliedColorF, YuvColorSpace, YuvFormat}; use border::{BorderCornerInstance, BorderEdgeKind}; use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId}; use clip_scroll_node::ClipScrollNode; @@ -25,8 +25,8 @@ use resource_cache::{CacheItem, ImageProperties, ImageRequest, ResourceCache}; use segment::SegmentBuilder; use std::{mem, usize}; use std::rc::Rc; -use util::{MatrixHelpers, calculate_screen_bounding_rect, pack_as_float}; -use util::recycle_vec; +use util::{MatrixHelpers, WorldToLayerFastTransform, calculate_screen_bounding_rect}; +use util::{pack_as_float, recycle_vec}; const MIN_BRUSH_SPLIT_AREA: f32 = 128.0 * 128.0; @@ -670,7 +670,7 @@ impl TextRunPrimitiveCpu { pub fn get_font( &self, device_pixel_scale: DevicePixelScale, - transform: Option<&LayerToWorldTransform>, + transform: Option, ) -> FontInstance { let mut font = self.font.clone(); font.size = font.size.scale_by(device_pixel_scale.0); @@ -678,7 +678,7 @@ impl TextRunPrimitiveCpu { if transform.has_perspective_component() || !transform.has_2d_inverse() { font.render_mode = font.render_mode.limit_by(FontRenderMode::Alpha); } else { - font.transform = FontTransform::from(transform).quantize(); + font.transform = FontTransform::from(&transform).quantize(); } } font @@ -688,7 +688,7 @@ impl TextRunPrimitiveCpu { &mut self, resource_cache: &mut ResourceCache, device_pixel_scale: DevicePixelScale, - transform: Option<&LayerToWorldTransform>, + transform: Option, display_list: &BuiltDisplayList, gpu_cache: &mut GpuCache, ) { @@ -1146,7 +1146,7 @@ impl PrimitiveStore { let text = &mut self.cpu_text_runs[metadata.cpu_prim_index.0]; // The transform only makes sense for screen space rasterization let transform = if pic_context.draw_text_transformed { - Some(&prim_run_context.scroll_node.world_content_transform) + Some(prim_run_context.scroll_node.world_content_transform.into()) } else { None }; @@ -1471,14 +1471,14 @@ impl PrimitiveStore { let local_clip_rect = if clip_item.scroll_node_data_index == prim_run_context.scroll_node.node_data_index { local_clip_rect } else { - let clip_transform_data = &frame_context - .node_data[clip_item.scroll_node_data_index.0 as usize]; + let clip_transform = frame_context + .node_data[clip_item.scroll_node_data_index.0 as usize] + .transform; let prim_transform = &prim_run_context.scroll_node.world_content_transform; - let relative_transform = prim_transform .inverse() - .unwrap_or(WorldToLayerTransform::identity()) - .pre_mul(&clip_transform_data.transform); + .unwrap_or(WorldToLayerFastTransform::identity()) + .pre_mul(&clip_transform.into()); relative_transform.transform_rect(&local_clip_rect) }; @@ -2061,8 +2061,7 @@ fn get_local_clip_rect_for_nodes( ); match local_rect { - Some(local_rect) => - Some(scroll_node.coordinate_system_relative_transform.unapply(&local_rect)), + Some(local_rect) => scroll_node.coordinate_system_relative_transform.unapply(&local_rect), None => None, } } diff --git a/webrender/src/util.rs b/webrender/src/util.rs index 34ced28237..780703a45d 100644 --- a/webrender/src/util.rs +++ b/webrender/src/util.rs @@ -3,10 +3,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BorderRadius, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixelScale}; -use api::{DevicePoint, DeviceRect, DeviceSize, LayerPoint, LayerRect, LayerSize}; -use api::{LayerToWorldTransform, LayerTransform, LayerVector2D, WorldRect}; -use euclid::{Point2D, Rect, Size2D, TypedPoint2D, TypedRect, TypedSize2D, TypedTransform2D}; -use euclid::TypedTransform3D; +use api::{DevicePoint, DeviceRect, DeviceSize, LayerPixel, LayerPoint, LayerRect, LayerSize}; +use api::{LayoutPixel, WorldPixel, WorldRect}; +use euclid::{Point2D, Rect, Size2D, TypedPoint2D, TypedPoint3D, TypedRect, TypedSize2D}; +use euclid::{TypedTransform2D, TypedTransform3D, TypedVector2D}; use num_traits::Zero; use std::{i32, f32}; @@ -22,6 +22,7 @@ pub trait MatrixHelpers { fn inverse_rect_footprint(&self, rect: &TypedRect) -> TypedRect; fn transform_kind(&self) -> TransformedRectKind; fn is_simple_translation(&self) -> bool; + fn is_simple_2d_translation(&self) -> bool; } impl MatrixHelpers for TypedTransform3D { @@ -105,6 +106,14 @@ impl MatrixHelpers for TypedTransform3D { self.m31.abs() < NEARLY_ZERO && self.m32.abs() < NEARLY_ZERO && self.m34.abs() < NEARLY_ZERO } + + fn is_simple_2d_translation(&self) -> bool { + if !self.is_simple_translation() { + return false; + } + + self.m43.abs() < NEARLY_ZERO + } } pub trait RectHelpers @@ -145,7 +154,7 @@ pub fn lerp(a: f32, b: f32, t: f32) -> f32 { } pub fn calculate_screen_bounding_rect( - transform: &LayerToWorldTransform, + transform: &LayerToWorldFastTransform, rect: &LayerRect, device_pixel_scale: DevicePixelScale, ) -> DeviceIntRect { @@ -334,67 +343,189 @@ impl MaxRect for DeviceRect { /// An enum that tries to avoid expensive transformation matrix calculations /// when possible when dealing with non-perspective axis-aligned transformations. -#[derive(Debug, Clone)] -pub enum TransformOrOffset { +#[derive(Debug, Clone, Copy)] +pub enum FastTransform { /// A simple offset, which can be used without doing any matrix math. - Offset(LayerVector2D), + Offset(TypedVector2D), - /// A transformation with an inverse. If the inverse isn't present, this isn't a 2D - /// transformation, which means we need to fall back to using inverse_rect_footprint. - /// Since this operation is so expensive, we avoid it for the 2D case. + /// A 2D transformation with an inverse. Transform { - transform: LayerTransform, - inverse: Option, - } + transform: TypedTransform3D, + inverse: Option>, + is_2d: bool, + }, } -impl TransformOrOffset { - pub fn zero() -> TransformOrOffset { - TransformOrOffset::Offset(LayerVector2D::zero()) +impl FastTransform { + pub fn identity() -> Self { + FastTransform::Offset(TypedVector2D::zero()) + } + + pub fn with_vector(offset: TypedVector2D) -> Self { + FastTransform::Offset(offset) } - fn new_transform(transform: LayerTransform) -> TransformOrOffset { - if transform.is_2d() { - TransformOrOffset::Transform { - transform, - inverse: Some(transform.inverse().expect("Expected invertible matrix.")) + #[inline(always)] + pub fn with_transform(transform: TypedTransform3D) -> Self { + if transform.is_simple_2d_translation() { + return FastTransform::Offset(TypedVector2D::new(transform.m41, transform.m42)); + } + let inverse = transform.inverse(); + let is_2d = transform.is_2d(); + FastTransform::Transform { transform, inverse, is_2d} + } + + pub fn to_transform(&self) -> TypedTransform3D { + match *self { + FastTransform::Offset(offset) => + TypedTransform3D::create_translation(offset.x, offset.y, 0.0), + FastTransform::Transform { transform, .. } => transform + } + } + + pub fn is_invertible(&self) -> bool { + match *self { + FastTransform::Offset(..) => true, + FastTransform::Transform { ref inverse, .. } => inverse.is_some(), + } + } + + #[inline(always)] + pub fn pre_mul( + &self, + other: &FastTransform + ) -> FastTransform { + match (self, other) { + (&FastTransform::Offset(ref offset), &FastTransform::Offset(ref other_offset)) => { + let offset = TypedVector2D::from_untyped(&offset.to_untyped()); + FastTransform::Offset((offset + *other_offset)) } - } else { - TransformOrOffset::Transform { transform, inverse: None } + _ => { + let new_transform = self.to_transform().pre_mul(&other.to_transform()); + FastTransform::with_transform(new_transform) + } + } + } + + #[inline(always)] + pub fn pre_translate(&self, other_offset: &TypedVector2D) -> Self { + match self { + &FastTransform::Offset(ref offset) => + return FastTransform::Offset(*offset + *other_offset), + &FastTransform::Transform { transform, .. } => + FastTransform::with_transform(transform.pre_translate(other_offset.to_3d())) + } + } + + #[inline(always)] + pub fn preserves_2d_axis_alignment(&self) -> bool { + match *self { + FastTransform::Offset(..) => true, + FastTransform::Transform { ref transform, .. } => + transform.preserves_2d_axis_alignment(), + } + } + + #[inline(always)] + pub fn has_perspective_component(&self) -> bool { + match *self { + FastTransform::Offset(..) => false, + FastTransform::Transform { ref transform, .. } => transform.has_perspective_component(), + } + } + + #[inline(always)] + pub fn is_backface_visible(&self) -> bool { + match *self { + FastTransform::Offset(..) => false, + FastTransform::Transform { ref transform, .. } => transform.is_backface_visible(), } } - pub fn apply(&self, rect: &LayerRect) -> LayerRect { + #[inline(always)] + pub fn transform_point2d(&self, point: &TypedPoint2D) -> TypedPoint2D { match *self { - TransformOrOffset::Offset(offset) => rect.translate(&offset), - TransformOrOffset::Transform {transform, .. } => transform.transform_rect(&rect), + FastTransform::Offset(offset) => { + let new_point = *point + offset; + TypedPoint2D::from_untyped(&new_point.to_untyped()) + } + FastTransform::Transform { ref transform, .. } => transform.transform_point2d(point), } } - pub fn unapply(&self, rect: &LayerRect) -> LayerRect { + #[inline(always)] + pub fn transform_point3d(&self, point: &TypedPoint3D) -> TypedPoint3D { match *self { - TransformOrOffset::Offset(offset) => rect.translate(&-offset), - TransformOrOffset::Transform { inverse: Some(inverse), .. } => - inverse.transform_rect(&rect), - TransformOrOffset::Transform { transform, inverse: None } => - transform.inverse_rect_footprint(rect), + FastTransform::Offset(offset) => + TypedPoint3D::new(point.x + offset.x, point.y + offset.y, point.z), + FastTransform::Transform { ref transform, .. } => transform.transform_point3d(point), } } - pub fn offset(&self, new_offset: LayerVector2D) -> TransformOrOffset { + #[inline(always)] + pub fn transform_rect(&self, rect: &TypedRect) -> TypedRect { match *self { - TransformOrOffset::Offset(offset) => TransformOrOffset::Offset(offset + new_offset), - TransformOrOffset::Transform { transform, .. } => { + FastTransform::Offset(offset) => + TypedRect::from_untyped(&rect.to_untyped().translate(&offset.to_untyped())), + FastTransform::Transform { ref transform, .. } => transform.transform_rect(rect), + } + } + + pub fn unapply(&self, rect: &TypedRect) -> Option> { + match *self { + FastTransform::Offset(offset) => + Some(TypedRect::from_untyped(&rect.to_untyped().translate(&-offset.to_untyped()))), + FastTransform::Transform { inverse: Some(ref inverse), is_2d: true, .. } => + Some(inverse.transform_rect(&rect)), + FastTransform::Transform { ref transform, is_2d: false, .. } => + Some(transform.inverse_rect_footprint(rect)), + FastTransform::Transform { inverse: None, .. } => None, + } + } + + #[inline(always)] + pub fn offset(&self, new_offset: TypedVector2D) -> Self { + match *self { + FastTransform::Offset(offset) => FastTransform::Offset(offset + new_offset), + FastTransform::Transform { ref transform, .. } => { let transform = transform.pre_translate(new_offset.to_3d()); - TransformOrOffset::new_transform(transform) + FastTransform::with_transform(transform) } } } - pub fn update(&self, transform: LayerTransform) -> Option { - if transform.is_simple_translation() { - let offset = LayerVector2D::new(transform.m41, transform.m42); - Some(self.offset(offset)) + pub fn post_translate(&self, new_offset: TypedVector2D) -> Self { + match *self { + FastTransform::Offset(offset) => { + let offset = offset.to_untyped() + new_offset.to_untyped(); + FastTransform::Offset(TypedVector2D::from_untyped(&offset)) + } + FastTransform::Transform { ref transform, .. } => { + let transform = transform.post_translate(new_offset.to_3d()); + FastTransform::with_transform(transform) + } + } + } + + #[inline(always)] + pub fn inverse(&self) -> Option> { + match *self { + FastTransform::Offset(offset) => + Some(FastTransform::Offset(TypedVector2D::new(-offset.x, -offset.y))), + FastTransform::Transform { transform, inverse: Some(inverse), is_2d, } => + Some(FastTransform::Transform { + transform: inverse, + inverse: Some(transform), + is_2d + }), + FastTransform::Transform { inverse: None, .. } => None, + + } + } + + pub fn update(&self, transform: TypedTransform3D) -> Option { + if transform.is_simple_2d_translation() { + Some(self.offset(TypedVector2D::new(transform.m41, transform.m42))) } else { // If we break 2D axis alignment or have a perspective component, we need to start a // new incompatible coordinate system with which we cannot share clips without masking. @@ -402,3 +533,26 @@ impl TransformOrOffset { } } } + +impl From> for FastTransform { + fn from(transform: TypedTransform3D) -> FastTransform { + FastTransform::with_transform(transform) + } +} + +impl Into> for FastTransform { + fn into(self) -> TypedTransform3D { + self.to_transform() + } +} + +impl From> for FastTransform { + fn from(vector: TypedVector2D) -> FastTransform { + FastTransform::with_vector(vector) + } +} + +pub type LayoutFastTransform = FastTransform; +pub type LayerFastTransform = FastTransform; +pub type LayerToWorldFastTransform = FastTransform; +pub type WorldToLayerFastTransform = FastTransform;