Bug 1723665 - Move coordinate mapping and snapping from scene building to display list building r=aosmond,gfx-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D121591
This commit is contained in:
Glenn Watson 2021-08-12 23:44:55 +00:00
Родитель e71325b8e1
Коммит c4ae6a4095
17 изменённых файлов: 706 добавлений и 558 удалений

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

@ -89,7 +89,6 @@ impl InternablePrimitive for Backdrop {
_key: BackdropKey,
data_handle: BackdropDataHandle,
_prim_store: &mut PrimitiveStore,
_reference_frame_relative_offset: LayoutVector2D,
) -> PrimitiveInstanceKind {
PrimitiveInstanceKind::Backdrop {
data_handle,

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

@ -167,7 +167,6 @@ impl InternablePrimitive for NormalBorderPrim {
_key: NormalBorderKey,
data_handle: NormalBorderDataHandle,
_: &mut PrimitiveStore,
_reference_frame_relative_offset: LayoutVector2D,
) -> PrimitiveInstanceKind {
PrimitiveInstanceKind::NormalBorder {
data_handle,
@ -355,7 +354,6 @@ impl InternablePrimitive for ImageBorder {
_key: ImageBorderKey,
data_handle: ImageBorderDataHandle,
_: &mut PrimitiveStore,
_reference_frame_relative_offset: LayoutVector2D,
) -> PrimitiveInstanceKind {
PrimitiveInstanceKind::ImageBorder {
data_handle

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

@ -288,7 +288,6 @@ impl InternablePrimitive for ConicGradient {
_key: ConicGradientKey,
data_handle: ConicGradientDataHandle,
_prim_store: &mut PrimitiveStore,
_reference_frame_relative_offset: LayoutVector2D,
) -> PrimitiveInstanceKind {
PrimitiveInstanceKind::ConicGradient {
data_handle,

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

@ -594,7 +594,6 @@ impl InternablePrimitive for LinearGradient {
key: LinearGradientKey,
data_handle: LinearGradientDataHandle,
_prim_store: &mut PrimitiveStore,
_reference_frame_relative_offset: LayoutVector2D,
) -> PrimitiveInstanceKind {
if key.cached {
PrimitiveInstanceKind::CachedLinearGradient {

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

@ -293,7 +293,6 @@ impl InternablePrimitive for RadialGradient {
_key: RadialGradientKey,
data_handle: RadialGradientDataHandle,
_prim_store: &mut PrimitiveStore,
_reference_frame_relative_offset: LayoutVector2D,
) -> PrimitiveInstanceKind {
PrimitiveInstanceKind::RadialGradient {
data_handle,

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

@ -441,7 +441,6 @@ impl InternablePrimitive for Image {
_key: ImageKey,
data_handle: ImageDataHandle,
prim_store: &mut PrimitiveStore,
_reference_frame_relative_offset: LayoutVector2D,
) -> PrimitiveInstanceKind {
// TODO(gw): Refactor this to not need a separate image
// instance (see ImageInstance struct).
@ -647,7 +646,6 @@ impl InternablePrimitive for YuvImage {
_key: YuvImageKey,
data_handle: YuvImageDataHandle,
_prim_store: &mut PrimitiveStore,
_reference_frame_relative_offset: LayoutVector2D,
) -> PrimitiveInstanceKind {
PrimitiveInstanceKind::YuvImage {
data_handle,

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

@ -144,7 +144,6 @@ impl InternablePrimitive for LineDecoration {
_key: LineDecorationKey,
data_handle: LineDecorationDataHandle,
_: &mut PrimitiveStore,
_reference_frame_relative_offset: LayoutVector2D,
) -> PrimitiveInstanceKind {
PrimitiveInstanceKind::LineDecoration {
data_handle,

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

@ -650,7 +650,6 @@ impl InternablePrimitive for PrimitiveKeyKind {
key: PrimitiveKey,
data_handle: PrimitiveDataHandle,
prim_store: &mut PrimitiveStore,
_reference_frame_relative_offset: LayoutVector2D,
) -> PrimitiveInstanceKind {
match key.kind {
PrimitiveKeyKind::Clear => {
@ -1453,7 +1452,6 @@ pub trait InternablePrimitive: intern::Internable<InternData = ()> + Sized {
key: Self::Key,
data_handle: intern::Handle<Self>,
prim_store: &mut PrimitiveStore,
reference_frame_relative_offset: LayoutVector2D,
) -> PrimitiveInstanceKind;
}

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

@ -6,7 +6,7 @@ use api::{
ColorU, MixBlendMode, FilterPrimitiveInput, FilterPrimitiveKind, ColorSpace,
PropertyBinding, PropertyBindingId, CompositeOperator,
};
use api::units::{Au, LayoutVector2D};
use api::units::Au;
use crate::scene_building::IsVisible;
use crate::filterdata::SFilterData;
use crate::intern::ItemUid;
@ -292,7 +292,6 @@ impl InternablePrimitive for Picture {
_key: PictureKey,
_: PictureDataHandle,
_: &mut PrimitiveStore,
_reference_frame_relative_offset: LayoutVector2D,
) -> PrimitiveInstanceKind {
// Should never be hit as this method should not be
// called for pictures.

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

@ -16,7 +16,7 @@ use crate::prim_store::{PrimitiveStore, PrimKeyCommonData, PrimTemplateCommonDat
use crate::renderer::{MAX_VERTEX_TEXTURE_WIDTH};
use crate::resource_cache::{ResourceCache};
use crate::util::{MatrixHelpers};
use crate::prim_store::{InternablePrimitive, PrimitiveInstanceKind};
use crate::prim_store::{InternablePrimitive, PrimitiveInstanceKind, VectorKey};
use crate::spatial_tree::{SpatialTree, SpatialNodeIndex, ROOT_SPATIAL_NODE_INDEX};
use crate::space::SpaceSnapper;
use crate::util::PrimaryArc;
@ -36,6 +36,7 @@ pub struct TextRunKey {
pub glyphs: PrimaryArc<Vec<GlyphInstance>>,
pub shadow: bool,
pub requested_raster_space: RasterSpace,
pub reference_frame_relative_offset: VectorKey,
}
impl TextRunKey {
@ -49,6 +50,7 @@ impl TextRunKey {
glyphs: PrimaryArc(text_run.glyphs),
shadow: text_run.shadow,
requested_raster_space: text_run.requested_raster_space,
reference_frame_relative_offset: text_run.reference_frame_relative_offset.into(),
}
}
}
@ -149,6 +151,7 @@ pub struct TextRun {
pub glyphs: Arc<Vec<GlyphInstance>>,
pub shadow: bool,
pub requested_raster_space: RasterSpace,
pub reference_frame_relative_offset: LayoutVector2D,
}
impl intern::Internable for TextRun {
@ -173,13 +176,12 @@ impl InternablePrimitive for TextRun {
key: TextRunKey,
data_handle: TextRunDataHandle,
prim_store: &mut PrimitiveStore,
reference_frame_relative_offset: LayoutVector2D,
) -> PrimitiveInstanceKind {
let run_index = prim_store.text_runs.push(TextRunPrimitive {
used_font: key.font.clone(),
glyph_keys_range: storage::Range::empty(),
reference_frame_relative_offset,
snapped_reference_frame_relative_offset: reference_frame_relative_offset,
reference_frame_relative_offset: key.reference_frame_relative_offset.into(),
snapped_reference_frame_relative_offset: key.reference_frame_relative_offset.into(),
shadow: key.shadow,
raster_scale: 1.0,
requested_raster_space: key.requested_raster_space,
@ -215,6 +217,7 @@ impl CreateShadow for TextRun {
glyphs: self.glyphs.clone(),
shadow: true,
requested_raster_space,
reference_frame_relative_offset: self.reference_frame_relative_offset,
}
}
}
@ -497,8 +500,8 @@ fn test_struct_sizes() {
// test expectations and move on.
// (b) You made a structure larger. This is not necessarily a problem, but should only
// be done with care, and after checking if talos performance regresses badly.
assert_eq!(mem::size_of::<TextRun>(), 64, "TextRun size changed");
assert_eq!(mem::size_of::<TextRun>(), 72, "TextRun size changed");
assert_eq!(mem::size_of::<TextRunTemplate>(), 80, "TextRunTemplate size changed");
assert_eq!(mem::size_of::<TextRunKey>(), 80, "TextRunKey size changed");
assert_eq!(mem::size_of::<TextRunKey>(), 88, "TextRunKey size changed");
assert_eq!(mem::size_of::<TextRunPrimitive>(), 80, "TextRunPrimitive size changed");
}

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

@ -89,110 +89,6 @@ use crate::util::{MaxRect, VecHelper};
use crate::filterdata::{SFilterDataComponent, SFilterData, SFilterDataKey};
use smallvec::SmallVec;
/// The offset stack for a given reference frame.
struct ReferenceFrameState {
/// A stack of current offsets from the current reference frame scope.
offsets: Vec<LayoutVector2D>,
}
/// Maps from stacking context layout coordinates into reference frame
/// relative coordinates.
struct ReferenceFrameMapper {
/// A stack of reference frame scopes.
frames: Vec<ReferenceFrameState>,
}
impl ReferenceFrameMapper {
fn new() -> Self {
ReferenceFrameMapper {
frames: vec![
ReferenceFrameState {
offsets: vec![
LayoutVector2D::zero(),
],
}
],
}
}
/// Push a new scope. This resets the current offset to zero, and is
/// used when a new reference frame or iframe is pushed.
fn push_scope(&mut self) {
self.frames.push(ReferenceFrameState {
offsets: vec![
LayoutVector2D::zero(),
],
});
}
/// Pop a reference frame scope off the stack.
fn pop_scope(&mut self) {
self.frames.pop().unwrap();
}
/// Push a new offset for the current scope. This is used when
/// a new stacking context is pushed.
fn push_offset(&mut self, offset: LayoutVector2D) {
let frame = self.frames.last_mut().unwrap();
let current_offset = *frame.offsets.last().unwrap();
frame.offsets.push(current_offset + offset);
}
/// Pop a local stacking context offset from the current scope.
fn pop_offset(&mut self) {
let frame = self.frames.last_mut().unwrap();
frame.offsets.pop().unwrap();
}
/// Retrieve the current offset to allow converting a stacking context
/// relative coordinate to be relative to the owing reference frame.
/// TODO(gw): We could perhaps have separate coordinate spaces for this,
/// however that's going to either mean a lot of changes to
/// public API code, or a lot of changes to internal code.
/// Before doing that, we should revisit how Gecko would
/// prefer to provide coordinates.
/// TODO(gw): For now, this includes only the reference frame relative
/// offset. Soon, we will expand this to include the initial
/// scroll offsets that are now available on scroll nodes. This
/// will allow normalizing the coordinates even between display
/// lists where APZ has scrolled the content.
fn current_offset(&self) -> LayoutVector2D {
*self.frames.last().unwrap().offsets.last().unwrap()
}
}
/// Offsets primitives (and clips) by the external scroll offset
/// supplied to scroll nodes.
pub struct ScrollOffsetMapper {
pub current_spatial_node: SpatialNodeIndex,
pub current_offset: LayoutVector2D,
}
impl ScrollOffsetMapper {
fn new() -> Self {
ScrollOffsetMapper {
current_spatial_node: SpatialNodeIndex::INVALID,
current_offset: LayoutVector2D::zero(),
}
}
/// Return the accumulated external scroll offset for a spatial
/// node. This caches the last result, which is the common case,
/// or defers to the spatial tree to build the value.
fn external_scroll_offset(
&mut self,
spatial_node_index: SpatialNodeIndex,
spatial_tree: &SpatialTree,
) -> LayoutVector2D {
if spatial_node_index != self.current_spatial_node {
self.current_spatial_node = spatial_node_index;
self.current_offset = spatial_tree.external_scroll_offset(spatial_node_index);
}
self.current_offset
}
}
/// A data structure that keeps track of mapping between API Ids for spatials and the indices
/// used internally in the SpatialTree to avoid having to do HashMap lookups for primitives
/// and clips during frame building.
@ -486,12 +382,6 @@ pub struct SceneBuilder<'a> {
/// Reference to the set of data that is interned across display lists.
interners: &'a mut Interners,
/// Helper struct to map stacking context coords <-> reference frame coords.
rf_mapper: ReferenceFrameMapper,
/// Helper struct to map spatial nodes to external scroll offsets.
external_scroll_mapper: ScrollOffsetMapper,
/// The current recursion depth of iframes encountered. Used to restrict picture
/// caching slices to only the top-level content frame.
iframe_size: Vec<LayoutSize>,
@ -567,8 +457,6 @@ impl<'a> SceneBuilder<'a> {
prim_store: PrimitiveStore::new(&stats.prim_store_stats),
clip_store: ClipStore::new(&stats.clip_store_stats),
interners,
rf_mapper: ReferenceFrameMapper::new(),
external_scroll_mapper: ScrollOffsetMapper::new(),
iframe_size: Vec::new(),
root_iframe_clip: None,
quality_settings: view.quality_settings,
@ -610,28 +498,6 @@ impl<'a> SceneBuilder<'a> {
}
}
/// Retrieve the current offset to allow converting a stacking context
/// relative coordinate to be relative to the owing reference frame,
/// also considering any external scroll offset on the provided
/// spatial node.
fn current_offset(
&mut self,
spatial_node_index: SpatialNodeIndex,
) -> LayoutVector2D {
// Get the current offset from stacking context <-> reference frame space.
let rf_offset = self.rf_mapper.current_offset();
// Get the external scroll offset, if applicable.
let scroll_offset = self
.external_scroll_mapper
.external_scroll_offset(
spatial_node_index,
&self.spatial_tree,
);
rf_offset + scroll_offset
}
fn build_all(&mut self, root_pipeline: &ScenePipeline) {
enum ContextKind<'a> {
Root,
@ -699,7 +565,6 @@ impl<'a> SceneBuilder<'a> {
bc.pipeline_id,
);
self.rf_mapper.push_offset(info.origin.to_vector());
let new_context = BuildContext {
pipeline_id: bc.pipeline_id,
kind: ContextKind::StackingContext {
@ -717,7 +582,6 @@ impl<'a> SceneBuilder<'a> {
profile_scope!("build_reference_frame");
let parent_space = self.get_space(info.parent_spatial_id);
let mut subtraversal = item.sub_iter();
let current_offset = self.current_offset(parent_space);
let transform = match info.reference_frame.transform {
ReferenceTransformBinding::Static { binding } => binding,
@ -772,10 +636,9 @@ impl<'a> SceneBuilder<'a> {
info.reference_frame.transform_style,
transform,
info.reference_frame.kind,
current_offset + info.origin.to_vector(),
info.origin.to_vector(),
);
self.rf_mapper.push_scope();
let new_context = BuildContext {
pipeline_id: bc.pipeline_id,
kind: ContextKind::ReferenceFrame,
@ -813,8 +676,6 @@ impl<'a> SceneBuilder<'a> {
assert!(self.root_iframe_clip.is_none());
self.root_iframe_clip = Some(clip_chain_id);
}
self.rf_mapper.push_scope();
self.iframe_size.push(size);
let new_context = BuildContext {
@ -836,16 +697,11 @@ impl<'a> SceneBuilder<'a> {
match bc.kind {
ContextKind::Root => {}
ContextKind::StackingContext { sc_info } => {
self.rf_mapper.pop_offset();
self.pop_stacking_context(sc_info);
}
ContextKind::ReferenceFrame => {
self.rf_mapper.pop_scope();
}
ContextKind::ReferenceFrame => {}
ContextKind::Iframe { parent_traversal } => {
self.iframe_size.pop();
self.rf_mapper.pop_scope();
self.clip_store.pop_clip_root();
if self.iframe_size.is_empty() {
assert!(self.root_iframe_clip.is_some());
@ -883,10 +739,8 @@ impl<'a> SceneBuilder<'a> {
info: &StickyFrameDisplayItem,
parent_node_index: SpatialNodeIndex,
) {
let current_offset = self.current_offset(parent_node_index);
let frame_rect = info.bounds.translate(current_offset);
let sticky_frame_info = StickyFrameInfo::new(
frame_rect,
info.bounds,
info.margins,
info.vertical_offset_bounds,
info.horizontal_offset_bounds,
@ -907,11 +761,9 @@ impl<'a> SceneBuilder<'a> {
parent_node_index: SpatialNodeIndex,
pipeline_id: PipelineId,
) {
let current_offset = self.current_offset(parent_node_index);
// This is useful when calculating scroll extents for the
// SpatialNode::scroll(..) API as well as for properly setting sticky
// positioning offsets.
let frame_rect = info.frame_rect.translate(current_offset);
let content_size = info.content_rect.size();
self.add_scroll_frame(
@ -919,7 +771,7 @@ impl<'a> SceneBuilder<'a> {
parent_node_index,
info.external_id,
pipeline_id,
&frame_rect,
&info.frame_rect,
&content_size,
info.scroll_sensitivity,
ScrollFrameKind::Explicit,
@ -941,13 +793,10 @@ impl<'a> SceneBuilder<'a> {
},
};
let current_offset = self.current_offset(spatial_node_index);
let clip_rect = info.clip_rect.translate(current_offset);
self.add_rect_clip_node(
ClipId::root(iframe_pipeline_id),
&info.space_and_clip,
&clip_rect,
&info.clip_rect,
);
self.clip_store.push_clip_root(
@ -955,11 +804,6 @@ impl<'a> SceneBuilder<'a> {
true,
);
let bounds = self.snap_rect(
&info.bounds.translate(current_offset),
spatial_node_index,
);
let spatial_node_index = self.push_reference_frame(
SpatialId::root_reference_frame(iframe_pipeline_id),
Some(spatial_node_index),
@ -970,10 +814,10 @@ impl<'a> SceneBuilder<'a> {
is_2d_scale_translation: false,
should_snap: false
},
bounds.min.to_vector(),
info.bounds.min.to_vector(),
);
let iframe_rect = LayoutRect::from_size(bounds.size());
let iframe_rect = LayoutRect::from_size(info.bounds.size());
let is_root_pipeline = self.iframe_size.is_empty();
self.add_scroll_frame(
@ -982,7 +826,7 @@ impl<'a> SceneBuilder<'a> {
ExternalScrollId(0, iframe_pipeline_id),
iframe_pipeline_id,
&iframe_rect,
&bounds.size(),
&info.bounds.size(),
ScrollSensitivity::ScriptAndInputEvents,
ScrollFrameKind::PipelineRoot {
is_root_pipeline,
@ -990,7 +834,7 @@ impl<'a> SceneBuilder<'a> {
LayoutVector2D::zero(),
);
Some((bounds.size(), pipeline.display_list.iter()))
Some((info.bounds.size(), pipeline.display_list.iter()))
}
fn get_space(
@ -1011,44 +855,24 @@ impl<'a> SceneBuilder<'a> {
&mut self,
common: &CommonItemProperties,
bounds: Option<&LayoutRect>,
) -> (LayoutPrimitiveInfo, LayoutRect, SpatialNodeIndex, ClipChainId) {
) -> (LayoutPrimitiveInfo, SpatialNodeIndex, ClipChainId) {
let spatial_node_index = self.get_space(common.spatial_id);
let clip_chain_id = self.get_clip_chain(common.clip_id);
let current_offset = self.current_offset(spatial_node_index);
let unsnapped_clip_rect = common.clip_rect.translate(current_offset);
let clip_rect = self.snap_rect(
&unsnapped_clip_rect,
spatial_node_index,
);
let unsnapped_rect = bounds.map(|bounds| {
bounds.translate(current_offset)
});
// If no bounds rect is given, default to clip rect.
let rect = unsnapped_rect.map_or(clip_rect, |bounds| {
self.snap_rect(
&bounds,
spatial_node_index,
)
});
let layout = LayoutPrimitiveInfo {
rect,
clip_rect,
rect: bounds.cloned().unwrap_or(common.clip_rect),
clip_rect: common.clip_rect,
flags: common.flags,
};
(layout, unsnapped_rect.unwrap_or(unsnapped_clip_rect), spatial_node_index, clip_chain_id)
(layout, spatial_node_index, clip_chain_id)
}
fn process_common_properties_with_bounds(
&mut self,
common: &CommonItemProperties,
bounds: &LayoutRect,
) -> (LayoutPrimitiveInfo, LayoutRect, SpatialNodeIndex, ClipChainId) {
) -> (LayoutPrimitiveInfo, SpatialNodeIndex, ClipChainId) {
self.process_common_properties(
common,
Some(bounds),
@ -1076,7 +900,7 @@ impl<'a> SceneBuilder<'a> {
DisplayItem::Image(ref info) => {
profile_scope!("image");
let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
let (layout, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
&info.common,
&info.bounds,
);
@ -1096,14 +920,14 @@ impl<'a> SceneBuilder<'a> {
DisplayItem::RepeatingImage(ref info) => {
profile_scope!("repeating_image");
let (layout, unsnapped_rect, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
let (layout, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
&info.common,
&info.bounds,
);
let stretch_size = process_repeat_size(
&layout.rect,
&unsnapped_rect,
&info.unsnapped_rect,
info.stretch_size,
);
@ -1122,7 +946,7 @@ impl<'a> SceneBuilder<'a> {
DisplayItem::YuvImage(ref info) => {
profile_scope!("yuv_image");
let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
let (layout, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
&info.common,
&info.bounds,
);
@ -1147,7 +971,7 @@ impl<'a> SceneBuilder<'a> {
// are subtle interactions between the primitive origin and the glyph offset
// which appear to be significant (presumably due to some sort of accumulated
// error throughout the layers). We should fix this at some point.
let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
let (layout, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
&info.common,
&info.bounds,
);
@ -1160,12 +984,13 @@ impl<'a> SceneBuilder<'a> {
&info.color,
item.glyphs(),
info.glyph_options,
info.reference_frame_relative_offset,
);
}
DisplayItem::Rectangle(ref info) => {
profile_scope!("rect");
let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
let (layout, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
&info.common,
&info.bounds,
);
@ -1185,7 +1010,7 @@ impl<'a> SceneBuilder<'a> {
// TODO(gw): We could skip building the clip-chain here completely, as it's not used by
// hit-test items.
let (layout, _, spatial_node_index, _) = self.process_common_properties(
let (layout, spatial_node_index, _) = self.process_common_properties(
&info.common,
None,
);
@ -1203,7 +1028,7 @@ impl<'a> SceneBuilder<'a> {
DisplayItem::ClearRectangle(ref info) => {
profile_scope!("clear");
let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
let (layout, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
&info.common,
&info.bounds,
);
@ -1217,7 +1042,7 @@ impl<'a> SceneBuilder<'a> {
DisplayItem::Line(ref info) => {
profile_scope!("line");
let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
let (layout, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
&info.common,
&info.area,
);
@ -1239,14 +1064,14 @@ impl<'a> SceneBuilder<'a> {
return;
}
let (mut layout, unsnapped_rect, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
let (mut layout, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
&info.common,
&info.bounds,
);
let mut tile_size = process_repeat_size(
&layout.rect,
&unsnapped_rect,
&info.unsnapped_rect,
info.tile_size,
);
@ -1315,7 +1140,7 @@ impl<'a> SceneBuilder<'a> {
return;
}
let (mut layout, unsnapped_rect, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
let (mut layout, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
&info.common,
&info.bounds,
);
@ -1326,7 +1151,7 @@ impl<'a> SceneBuilder<'a> {
let mut tile_size = process_repeat_size(
&layout.rect,
&unsnapped_rect,
&info.unsnapped_rect,
info.tile_size,
);
@ -1393,14 +1218,14 @@ impl<'a> SceneBuilder<'a> {
return;
}
let (mut layout, unsnapped_rect, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
let (mut layout, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
&info.common,
&info.bounds,
);
let tile_size = process_repeat_size(
&layout.rect,
&unsnapped_rect,
&info.unsnapped_rect,
info.tile_size,
);
@ -1438,7 +1263,7 @@ impl<'a> SceneBuilder<'a> {
DisplayItem::BoxShadow(ref info) => {
profile_scope!("box_shadow");
let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
let (layout, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
&info.common,
&info.box_bounds,
);
@ -1458,7 +1283,7 @@ impl<'a> SceneBuilder<'a> {
DisplayItem::Border(ref info) => {
profile_scope!("border");
let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
let (layout, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds(
&info.common,
&info.bounds,
);
@ -1474,18 +1299,10 @@ impl<'a> SceneBuilder<'a> {
DisplayItem::ImageMaskClip(ref info) => {
profile_scope!("image_clip");
let parent_space = self.get_space(info.parent_space_and_clip.spatial_id);
let current_offset = self.current_offset(parent_space);
let image_mask = ImageMask {
rect: info.image_mask.rect.translate(current_offset),
..info.image_mask
};
self.add_image_mask_clip_node(
info.id,
&info.parent_space_and_clip,
&image_mask,
&info.image_mask,
info.fill_rule,
item.points(),
);
@ -1493,27 +1310,19 @@ impl<'a> SceneBuilder<'a> {
DisplayItem::RoundedRectClip(ref info) => {
profile_scope!("rounded_clip");
let parent_space = self.get_space(info.parent_space_and_clip.spatial_id);
let current_offset = self.current_offset(parent_space);
self.add_rounded_rect_clip_node(
info.id,
&info.parent_space_and_clip,
&info.clip,
current_offset,
);
}
DisplayItem::RectClip(ref info) => {
profile_scope!("rect_clip");
let parent_space = self.get_space(info.parent_space_and_clip.spatial_id);
let current_offset = self.current_offset(parent_space);
let clip_rect = info.clip_rect.translate(current_offset);
self.add_rect_clip_node(
info.id,
&info.parent_space_and_clip,
&clip_rect,
&info.clip_rect,
);
}
DisplayItem::ClipChain(ref info) => {
@ -1556,7 +1365,7 @@ impl<'a> SceneBuilder<'a> {
DisplayItem::BackdropFilter(ref info) => {
profile_scope!("backdrop");
let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties(
let (layout, spatial_node_index, clip_chain_id) = self.process_common_properties(
&info.common,
None,
);
@ -1664,7 +1473,6 @@ impl<'a> SceneBuilder<'a> {
fn create_primitive<P>(
&mut self,
info: &LayoutPrimitiveInfo,
spatial_node_index: SpatialNodeIndex,
clip_chain_id: ClipChainId,
prim: P,
) -> PrimitiveInstance
@ -1675,7 +1483,6 @@ impl<'a> SceneBuilder<'a> {
// Build a primitive key.
let prim_key = prim.into_key(info);
let current_offset = self.current_offset(spatial_node_index);
let interner = self.interners.as_mut();
let prim_data_handle = interner
.intern(&prim_key, || ());
@ -1684,7 +1491,6 @@ impl<'a> SceneBuilder<'a> {
prim_key,
prim_data_handle,
&mut self.prim_store,
current_offset,
);
PrimitiveInstance::new(
@ -1832,7 +1638,6 @@ impl<'a> SceneBuilder<'a> {
{
let prim_instance = self.create_primitive(
info,
spatial_node_index,
clip_chain_id,
prim,
);
@ -2441,10 +2246,6 @@ impl<'a> SceneBuilder<'a> {
) {
let spatial_node_index = self.id_to_index_mapper.get_spatial_node_index(space_and_clip.spatial_id);
let snapped_mask_rect = self.snap_rect(
&image_mask.rect,
spatial_node_index,
);
let points: Vec<LayoutPoint> = points_range.iter().collect();
// If any points are provided, then intern a polygon with the points and fill rule.
@ -2460,7 +2261,7 @@ impl<'a> SceneBuilder<'a> {
}
let item = ClipItemKey {
kind: ClipItemKeyKind::image_mask(image_mask, snapped_mask_rect, polygon_handle),
kind: ClipItemKeyKind::image_mask(image_mask, image_mask.rect, polygon_handle),
};
let handle = self
@ -2493,13 +2294,8 @@ impl<'a> SceneBuilder<'a> {
) {
let spatial_node_index = self.id_to_index_mapper.get_spatial_node_index(space_and_clip.spatial_id);
let snapped_clip_rect = self.snap_rect(
clip_rect,
spatial_node_index,
);
let item = ClipItemKey {
kind: ClipItemKeyKind::rectangle(snapped_clip_rect, ClipMode::Clip),
kind: ClipItemKeyKind::rectangle(*clip_rect, ClipMode::Clip),
};
let handle = self
.interners
@ -2522,22 +2318,17 @@ impl<'a> SceneBuilder<'a> {
);
}
pub fn add_rounded_rect_clip_node(
fn add_rounded_rect_clip_node(
&mut self,
new_node_id: ClipId,
space_and_clip: &SpaceAndClipInfo,
clip: &ComplexClipRegion,
current_offset: LayoutVector2D,
) {
let spatial_node_index = self.id_to_index_mapper.get_spatial_node_index(space_and_clip.spatial_id);
let snapped_region_rect = self.snap_rect(
&clip.rect.translate(current_offset),
spatial_node_index,
);
let item = ClipItemKey {
kind: ClipItemKeyKind::rounded_rect(
snapped_region_rect,
clip.rect,
clip.radii,
clip.mode,
),
@ -2814,7 +2605,6 @@ impl<'a> SceneBuilder<'a> {
// Construct and add a primitive for the given shadow.
let shadow_prim_instance = self.create_primitive(
&info,
pending_primitive.spatial_node_index,
pending_primitive.clip_chain_id,
pending_primitive.prim.create_shadow(
&pending_shadow.shadow,
@ -3229,9 +3019,8 @@ impl<'a> SceneBuilder<'a> {
text_color: &ColorF,
glyph_range: ItemRange<GlyphInstance>,
glyph_options: Option<GlyphOptions>,
reference_frame_relative_offset: LayoutVector2D,
) {
let offset = self.current_offset(spatial_node_index);
let text_run = {
let instance_map = self.font_instances.lock().unwrap();
let font_instance = match instance_map.get(font_instance_key) {
@ -3267,20 +3056,12 @@ impl<'a> SceneBuilder<'a> {
flags,
);
// TODO(gw): It'd be nice not to have to allocate here for creating
// the primitive key, when the common case is that the
// hash will match and we won't end up creating a new
// primitive template.
let prim_offset = prim_info.rect.min.to_vector() - offset;
let glyphs = glyph_range
.iter()
.map(|glyph| {
GlyphInstance {
index: glyph.index,
point: glyph.point - prim_offset,
}
})
.collect();
// TODO(gw): Although we no longer need to remap the glyph coords here,
// we still allocate. Instead, perhaps we could have a flat
// array of glyphs inside the display list builder, and text
// runs store a glyph range? That would reduce a lot of allocations
// that we do here.
let glyphs = glyph_range.iter().collect();
// Query the current requested raster space (stack handled by push/pop
// stacking context).
@ -3294,6 +3075,7 @@ impl<'a> SceneBuilder<'a> {
font,
shadow: false,
requested_raster_space,
reference_frame_relative_offset,
}
};
@ -3415,7 +3197,6 @@ impl<'a> SceneBuilder<'a> {
// region. By makings sure to include this, the clip chain instance computes the correct clip rect,
// but we don't actually apply the filtered backdrop clip yet (this is done to the last instance in
// the filter chain below).
backdrop_spatial_node_index,
clip_chain_id,
Backdrop {
pic_index: backdrop_pic_index,

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

@ -5,7 +5,7 @@
use api::BorderRadius;
use api::units::*;
use euclid::{Point2D, Rect, Box2D, Size2D, Vector2D, point2};
use euclid::{default, Transform2D, Transform3D, Scale};
use euclid::{Transform2D, Transform3D, Scale};
use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};
use plane_split::{Clipper, Polygon};
use std::{i32, f32, fmt, ptr};
@ -15,6 +15,7 @@ use std::os::raw::c_void;
use std::sync::Arc;
use std::mem::replace;
pub use api::ScaleOffset;
// Matches the definition of SK_ScalarNearlyZero in Skia.
const NEARLY_ZERO: f32 = 1.0 / 4096.0;
@ -118,253 +119,6 @@ impl<T> VecHelper<T> for Vec<T> {
}
}
// Represents an optimized transform where there is only
// a scale and translation (which are guaranteed to maintain
// an axis align rectangle under transformation). The
// scaling is applied first, followed by the translation.
// TODO(gw): We should try and incorporate F <-> T units here,
// but it's a bit tricky to do that now with the
// way the current spatial tree works.
#[derive(Debug, Clone, Copy, MallocSizeOf)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ScaleOffset {
pub scale: default::Vector2D<f32>,
pub offset: default::Vector2D<f32>,
}
impl ScaleOffset {
pub fn identity() -> Self {
ScaleOffset {
scale: Vector2D::new(1.0, 1.0),
offset: Vector2D::zero(),
}
}
// Construct a ScaleOffset from a transform. Returns
// None if the matrix is not a pure scale / translation.
pub fn from_transform<F, T>(
m: &Transform3D<f32, F, T>,
) -> Option<ScaleOffset> {
// To check that we have a pure scale / translation:
// Every field must match an identity matrix, except:
// - Any value present in tx,ty
// - Any value present in sx,sy
if m.m12.abs() > NEARLY_ZERO ||
m.m13.abs() > NEARLY_ZERO ||
m.m14.abs() > NEARLY_ZERO ||
m.m21.abs() > NEARLY_ZERO ||
m.m23.abs() > NEARLY_ZERO ||
m.m24.abs() > NEARLY_ZERO ||
m.m31.abs() > NEARLY_ZERO ||
m.m32.abs() > NEARLY_ZERO ||
(m.m33 - 1.0).abs() > NEARLY_ZERO ||
m.m34.abs() > NEARLY_ZERO ||
m.m43.abs() > NEARLY_ZERO ||
(m.m44 - 1.0).abs() > NEARLY_ZERO {
return None;
}
Some(ScaleOffset {
scale: Vector2D::new(m.m11, m.m22),
offset: Vector2D::new(m.m41, m.m42),
})
}
pub fn from_offset(offset: default::Vector2D<f32>) -> Self {
ScaleOffset {
scale: Vector2D::new(1.0, 1.0),
offset,
}
}
pub fn from_scale(scale: default::Vector2D<f32>) -> Self {
ScaleOffset {
scale,
offset: Vector2D::new(0.0, 0.0),
}
}
pub fn inverse(&self) -> Self {
ScaleOffset {
scale: Vector2D::new(
1.0 / self.scale.x,
1.0 / self.scale.y,
),
offset: Vector2D::new(
-self.offset.x / self.scale.x,
-self.offset.y / self.scale.y,
),
}
}
pub fn offset(&self, offset: default::Vector2D<f32>) -> Self {
self.accumulate(
&ScaleOffset {
scale: Vector2D::new(1.0, 1.0),
offset,
}
)
}
pub fn scale(&self, scale: f32) -> Self {
self.accumulate(
&ScaleOffset {
scale: Vector2D::new(scale, scale),
offset: Vector2D::zero(),
}
)
}
/// Produce a ScaleOffset that includes both self and other.
/// The 'self' ScaleOffset is applied after other.
/// This is equivalent to `Transform3D::pre_transform`.
pub fn accumulate(&self, other: &ScaleOffset) -> Self {
ScaleOffset {
scale: Vector2D::new(
self.scale.x * other.scale.x,
self.scale.y * other.scale.y,
),
offset: Vector2D::new(
self.offset.x + self.scale.x * other.offset.x,
self.offset.y + self.scale.y * other.offset.y,
),
}
}
pub fn map_rect<F, T>(&self, rect: &Box2D<f32, F>) -> Box2D<f32, T> {
// TODO(gw): The logic below can return an unexpected result if the supplied
// rect is invalid (has size < 0). Since Gecko currently supplied
// invalid rects in some cases, adding a max(0) here ensures that
// mapping an invalid rect retains the property that rect.is_empty()
// will return true (the mapped rect output will have size 0 instead
// of a negative size). In future we could catch / assert / fix
// these invalid rects earlier, and assert here instead.
let w = rect.width().max(0.0);
let h = rect.height().max(0.0);
let mut x0 = rect.min.x * self.scale.x + self.offset.x;
let mut y0 = rect.min.y * self.scale.y + self.offset.y;
let mut sx = w * self.scale.x;
let mut sy = h * self.scale.y;
// Handle negative scale. Previously, branchless float math was used to find the
// min / max vertices and size. However, that sequence of operations was producind
// additional floating point accuracy on android emulator builds, causing one test
// to fail an assert. Instead, we retain the same math as previously, and adjust
// the origin / size if required.
if self.scale.x < 0.0 {
x0 += sx;
sx = -sx;
}
if self.scale.y < 0.0 {
y0 += sy;
sy = -sy;
}
Box2D::from_origin_and_size(
Point2D::new(x0, y0),
Size2D::new(sx, sy),
)
}
pub fn unmap_rect<F, T>(&self, rect: &Box2D<f32, F>) -> Box2D<f32, T> {
// TODO(gw): The logic below can return an unexpected result if the supplied
// rect is invalid (has size < 0). Since Gecko currently supplied
// invalid rects in some cases, adding a max(0) here ensures that
// mapping an invalid rect retains the property that rect.is_empty()
// will return true (the mapped rect output will have size 0 instead
// of a negative size). In future we could catch / assert / fix
// these invalid rects earlier, and assert here instead.
let w = rect.width().max(0.0);
let h = rect.height().max(0.0);
let mut x0 = (rect.min.x - self.offset.x) / self.scale.x;
let mut y0 = (rect.min.y - self.offset.y) / self.scale.y;
let mut sx = w / self.scale.x;
let mut sy = h / self.scale.y;
// Handle negative scale. Previously, branchless float math was used to find the
// min / max vertices and size. However, that sequence of operations was producind
// additional floating point accuracy on android emulator builds, causing one test
// to fail an assert. Instead, we retain the same math as previously, and adjust
// the origin / size if required.
if self.scale.x < 0.0 {
x0 += sx;
sx = -sx;
}
if self.scale.y < 0.0 {
y0 += sy;
sy = -sy;
}
Box2D::from_origin_and_size(
Point2D::new(x0, y0),
Size2D::new(sx, sy),
)
}
pub fn map_vector<F, T>(&self, vector: &Vector2D<f32, F>) -> Vector2D<f32, T> {
Vector2D::new(
vector.x * self.scale.x,
vector.y * self.scale.y,
)
}
pub fn unmap_vector<F, T>(&self, vector: &Vector2D<f32, F>) -> Vector2D<f32, T> {
Vector2D::new(
vector.x / self.scale.x,
vector.y / self.scale.y,
)
}
pub fn map_point<F, T>(&self, point: &Point2D<f32, F>) -> Point2D<f32, T> {
Point2D::new(
point.x * self.scale.x + self.offset.x,
point.y * self.scale.y + self.offset.y,
)
}
pub fn unmap_point<F, T>(&self, point: &Point2D<f32, F>) -> Point2D<f32, T> {
Point2D::new(
(point.x - self.offset.x) / self.scale.x,
(point.y - self.offset.y) / self.scale.y,
)
}
pub fn to_transform<F, T>(&self) -> Transform3D<f32, F, T> {
Transform3D::new(
self.scale.x,
0.0,
0.0,
0.0,
0.0,
self.scale.y,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
self.offset.x,
self.offset.y,
0.0,
1.0,
)
}
}
// TODO: Implement these in euclid!
pub trait MatrixHelpers<Src, Dst> {
/// A port of the preserves2dAxisAlignment function in Skia.

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

@ -391,6 +391,7 @@ pub struct TextDisplayItem {
pub font_key: font::FontInstanceKey,
pub color: ColorF,
pub glyph_options: Option<font::GlyphOptions>,
pub reference_frame_relative_offset: LayoutVector2D,
} // IMPLICIT: glyphs: Vec<font::GlyphInstance>
#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
@ -646,6 +647,9 @@ pub struct GradientDisplayItem {
/// The space between tiles of the gradient (common case: 0)
pub tile_spacing: LayoutSize,
pub gradient: Gradient,
/// The unsnapped rect is used for calculating repeats, so is replicated here
/// to maintain existing behavior.
pub unsnapped_rect: LayoutRect,
}
#[repr(C)]
@ -709,6 +713,9 @@ pub struct RadialGradientDisplayItem {
pub gradient: RadialGradient,
pub tile_size: LayoutSize,
pub tile_spacing: LayoutSize,
/// The unsnapped rect is used for calculating repeats, so is replicated here
/// to maintain existing behavior.
pub unsnapped_rect: LayoutRect,
}
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
@ -721,6 +728,9 @@ pub struct ConicGradientDisplayItem {
pub gradient: ConicGradient,
pub tile_size: LayoutSize,
pub tile_spacing: LayoutSize,
/// The unsnapped rect is used for calculating repeats, so is replicated here
/// to maintain existing behavior.
pub unsnapped_rect: LayoutRect,
}
/// Renders a filtered region of its backdrop
@ -1307,6 +1317,9 @@ pub struct RepeatingImageDisplayItem {
pub alpha_type: AlphaType,
/// A hack used by gecko to color a simple bitmap font used for tofu glyphs
pub color: ColorF,
/// The unsnapped rect is used for calculating repeats, so is replicated here
/// to maintain existing behavior.
pub unsnapped_rect: LayoutRect,
}
#[repr(u8)]

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

@ -26,6 +26,7 @@ use crate::color::ColorF;
use crate::font::{FontInstanceKey, GlyphInstance, GlyphOptions};
use crate::image::{ColorDepth, ImageKey};
use crate::units::*;
use crate::util::{RectHelpers, ScaleOffset};
// We don't want to push a long text-run. If a text-run is too long, split it into several parts.
@ -1016,6 +1017,26 @@ pub enum DisplayListSection {
Chunk,
}
/// A small portion of a normal spatial node that we store during DL construction to
/// enable snapping and reference frame <-> stacking context coord mapping. In future
/// we'll aim to remove this and have the full spatial tree available during DL build.
#[derive(Clone)]
pub struct SpatialNodeInfo {
/// The total external scroll offset applicable at this node
accumulated_external_scroll_offset: LayoutVector2D,
/// The 2d-axis-aligned snapping transform, if this node is in the root coord space
snapping_transform: Option<ScaleOffset>,
}
impl SpatialNodeInfo {
fn identity() -> Self {
SpatialNodeInfo {
accumulated_external_scroll_offset: LayoutVector2D::zero(),
snapping_transform: Some(ScaleOffset::identity()),
}
}
}
#[derive(Clone)]
pub struct DisplayListBuilder {
payload: DisplayListPayload,
@ -1033,6 +1054,12 @@ pub struct DisplayListBuilder {
cache_size: usize,
serialized_content_buffer: Option<String>,
/// Helper struct to map stacking context coords <-> reference frame coords.
rf_mapper: ReferenceFrameMapper,
/// Minimal info about encountered spatial nodes to allow snapping during DL building
spatial_nodes: Vec<SpatialNodeInfo>,
}
#[repr(C)]
@ -1078,6 +1105,9 @@ impl DisplayListBuilder {
save_state: None,
cache_size: 0,
serialized_content_buffer: None,
rf_mapper: ReferenceFrameMapper::new(),
spatial_nodes: vec![SpatialNodeInfo::identity(); FIRST_SPATIAL_NODE_INDEX + 1],
}
}
@ -1260,14 +1290,61 @@ impl DisplayListBuilder {
Self::push_iter_impl(&mut buffer, iter);
}
/// Apply snapping and coord space mapping to item props
fn process_common_props(
&mut self,
common: &di::CommonItemProperties,
) -> di::CommonItemProperties {
let current_offset = self.current_offset(common.spatial_id);
let clip_rect = self.snap_rect(
&common.clip_rect.translate(current_offset),
common.spatial_id,
);
di::CommonItemProperties {
clip_rect,
..*common
}
}
/// Apply snapping and coord space mapping to item props + prim bounds
fn process_common_props_with_bounds(
&mut self,
bounds: &LayoutRect,
common: &di::CommonItemProperties,
) -> (LayoutRect, di::CommonItemProperties) {
let current_offset = self.current_offset(common.spatial_id);
let clip_rect = self.snap_rect(
&common.clip_rect.translate(current_offset),
common.spatial_id,
);
let bounds = self.snap_rect(
&bounds.translate(current_offset),
common.spatial_id,
);
(
bounds,
di::CommonItemProperties {
clip_rect,
..*common
}
)
}
pub fn push_rect(
&mut self,
common: &di::CommonItemProperties,
bounds: LayoutRect,
color: ColorF,
) {
let (bounds, common) = self.process_common_props_with_bounds(&bounds, common);
let item = di::DisplayItem::Rectangle(di::RectangleDisplayItem {
common: *common,
common,
color: PropertyBinding::Value(color),
bounds,
});
@ -1280,8 +1357,10 @@ impl DisplayListBuilder {
bounds: LayoutRect,
color: PropertyBinding<ColorF>,
) {
let (bounds, common) = self.process_common_props_with_bounds(&bounds, common);
let item = di::DisplayItem::Rectangle(di::RectangleDisplayItem {
common: *common,
common,
color,
bounds,
});
@ -1293,8 +1372,10 @@ impl DisplayListBuilder {
common: &di::CommonItemProperties,
bounds: LayoutRect,
) {
let (bounds, common) = self.process_common_props_with_bounds(&bounds, common);
let item = di::DisplayItem::ClearRectangle(di::ClearRectangleDisplayItem {
common: *common,
common,
bounds,
});
self.push_item(&item);
@ -1305,8 +1386,10 @@ impl DisplayListBuilder {
common: &di::CommonItemProperties,
tag: di::ItemTag,
) {
let common = self.process_common_props(common);
let item = di::DisplayItem::HitTest(di::HitTestDisplayItem {
common: *common,
common,
tag,
});
self.push_item(&item);
@ -1321,9 +1404,11 @@ impl DisplayListBuilder {
color: &ColorF,
style: di::LineStyle,
) {
let (area, common) = self.process_common_props_with_bounds(area, common);
let item = di::DisplayItem::Line(di::LineDisplayItem {
common: *common,
area: *area,
common,
area,
wavy_line_thickness,
orientation,
color: *color,
@ -1342,8 +1427,10 @@ impl DisplayListBuilder {
key: ImageKey,
color: ColorF,
) {
let (bounds, common) = self.process_common_props_with_bounds(&bounds, common);
let item = di::DisplayItem::Image(di::ImageDisplayItem {
common: *common,
common,
bounds,
image_key: key,
image_rendering,
@ -1357,7 +1444,7 @@ impl DisplayListBuilder {
pub fn push_repeating_image(
&mut self,
common: &di::CommonItemProperties,
bounds: LayoutRect,
rect: LayoutRect,
stretch_size: LayoutSize,
tile_spacing: LayoutSize,
image_rendering: di::ImageRendering,
@ -1365,8 +1452,10 @@ impl DisplayListBuilder {
key: ImageKey,
color: ColorF,
) {
let (bounds, common) = self.process_common_props_with_bounds(&rect, common);
let item = di::DisplayItem::RepeatingImage(di::RepeatingImageDisplayItem {
common: *common,
common,
bounds,
image_key: key,
stretch_size,
@ -1374,6 +1463,7 @@ impl DisplayListBuilder {
image_rendering,
alpha_type,
color,
unsnapped_rect: rect,
});
self.push_item(&item);
@ -1390,8 +1480,10 @@ impl DisplayListBuilder {
color_range: di::ColorRange,
image_rendering: di::ImageRendering,
) {
let (bounds, common) = self.process_common_props_with_bounds(&bounds, common);
let item = di::DisplayItem::YuvImage(di::YuvImageDisplayItem {
common: *common,
common,
bounds,
yuv_data,
color_depth,
@ -1411,17 +1503,29 @@ impl DisplayListBuilder {
color: ColorF,
glyph_options: Option<GlyphOptions>,
) {
let (bounds, common) = self.process_common_props_with_bounds(&bounds, common);
let current_offset = self.current_offset(common.spatial_id);
let item = di::DisplayItem::Text(di::TextDisplayItem {
common: *common,
common,
bounds,
color,
font_key,
glyph_options,
reference_frame_relative_offset: self.rf_mapper.current_offset(),
});
let prim_offset = bounds.min.to_vector() - current_offset;
for split_glyphs in glyphs.chunks(MAX_TEXT_RUN_LENGTH) {
self.push_item(&item);
self.push_iter(split_glyphs);
self.push_iter(split_glyphs.iter().map(|glyph| {
GlyphInstance {
index: glyph.index,
point: glyph.point - prim_offset,
}
}));
}
}
@ -1477,8 +1581,10 @@ impl DisplayListBuilder {
widths: LayoutSideOffsets,
details: di::BorderDetails,
) {
let (bounds, common) = self.process_common_props_with_bounds(&bounds, common);
let item = di::DisplayItem::Border(di::BorderDisplayItem {
common: *common,
common,
bounds,
details,
widths,
@ -1498,8 +1604,10 @@ impl DisplayListBuilder {
border_radius: di::BorderRadius,
clip_mode: di::BoxShadowClipMode,
) {
let (box_bounds, common) = self.process_common_props_with_bounds(&box_bounds, common);
let item = di::DisplayItem::BoxShadow(di::BoxShadowDisplayItem {
common: *common,
common,
box_bounds,
offset,
color,
@ -1529,17 +1637,20 @@ impl DisplayListBuilder {
pub fn push_gradient(
&mut self,
common: &di::CommonItemProperties,
bounds: LayoutRect,
rect: LayoutRect,
gradient: di::Gradient,
tile_size: LayoutSize,
tile_spacing: LayoutSize,
) {
let (bounds, common) = self.process_common_props_with_bounds(&rect, common);
let item = di::DisplayItem::Gradient(di::GradientDisplayItem {
common: *common,
common,
bounds,
gradient,
tile_size,
tile_spacing,
unsnapped_rect: rect,
});
self.push_item(&item);
@ -1551,17 +1662,20 @@ impl DisplayListBuilder {
pub fn push_radial_gradient(
&mut self,
common: &di::CommonItemProperties,
bounds: LayoutRect,
rect: LayoutRect,
gradient: di::RadialGradient,
tile_size: LayoutSize,
tile_spacing: LayoutSize,
) {
let (bounds, common) = self.process_common_props_with_bounds(&rect, common);
let item = di::DisplayItem::RadialGradient(di::RadialGradientDisplayItem {
common: *common,
common,
bounds,
gradient,
tile_size,
tile_spacing,
unsnapped_rect: rect,
});
self.push_item(&item);
@ -1573,17 +1687,20 @@ impl DisplayListBuilder {
pub fn push_conic_gradient(
&mut self,
common: &di::CommonItemProperties,
bounds: LayoutRect,
rect: LayoutRect,
gradient: di::ConicGradient,
tile_size: LayoutSize,
tile_spacing: LayoutSize,
) {
let (bounds, common) = self.process_common_props_with_bounds(&rect, common);
let item = di::DisplayItem::ConicGradient(di::ConicGradientDisplayItem {
common: *common,
common,
bounds,
gradient,
tile_size,
tile_spacing,
unsnapped_rect: rect,
});
self.push_item(&item);
@ -1599,6 +1716,39 @@ impl DisplayListBuilder {
) -> di::SpatialId {
let id = self.generate_spatial_index();
let current_offset = self.current_offset(parent_spatial_id);
let origin = origin + current_offset;
let parent = &self.spatial_nodes[parent_spatial_id.0];
let snapping_transform = parent.snapping_transform.and_then(|parent| {
let snapping_transform = match transform {
PropertyBinding::Value(ref value) => {
// We can only get a ScaleOffset if the transform is 2d axis
// aligned.
ScaleOffset::from_transform(value).map(|scale_offset| {
ScaleOffset::from_offset(origin.to_vector().to_untyped())
.accumulate(&scale_offset)
})
}
// Assume animations start at the identity transform for snapping purposes.
// We still want to incorporate the reference frame offset however.
// TODO(aosmond): Is there a better known starting point?
PropertyBinding::Binding(..) => {
Some(ScaleOffset::from_offset(origin.to_vector().to_untyped()))
}
};
snapping_transform.map(|ref s| parent.accumulate(s))
});
self.add_spatial_node_info(
id,
LayoutVector2D::zero(),
snapping_transform,
);
let item = di::DisplayItem::PushReferenceFrame(di::ReferenceFrameDisplayListItem {
parent_spatial_id,
origin,
@ -1612,6 +1762,7 @@ impl DisplayListBuilder {
},
});
self.rf_mapper.push_scope();
self.push_item(&item);
id
}
@ -1626,6 +1777,9 @@ impl DisplayListBuilder {
) -> di::SpatialId {
let id = self.generate_spatial_index();
let current_offset = self.current_offset(parent_spatial_id);
let origin = origin + current_offset;
let item = di::DisplayItem::PushReferenceFrame(di::ReferenceFrameDisplayListItem {
parent_spatial_id,
origin,
@ -1649,6 +1803,7 @@ impl DisplayListBuilder {
}
pub fn pop_reference_frame(&mut self) {
self.rf_mapper.pop_scope();
self.push_item(&di::DisplayItem::PopReferenceFrame);
}
@ -1668,8 +1823,10 @@ impl DisplayListBuilder {
) {
self.push_filters(filters, filter_datas, filter_primitives);
let current_offset = self.current_offset(spatial_id);
let item = di::DisplayItem::PushStackingContext(di::PushStackingContextDisplayItem {
origin,
origin: origin + current_offset,
spatial_id,
prim_flags,
stacking_context: di::StackingContext {
@ -1681,6 +1838,8 @@ impl DisplayListBuilder {
},
});
self.rf_mapper.push_offset(origin.to_vector());
self.push_item(&item);
}
@ -1727,6 +1886,7 @@ impl DisplayListBuilder {
}
pub fn pop_stacking_context(&mut self) {
self.rf_mapper.pop_offset();
self.push_item(&di::DisplayItem::PopStackingContext);
}
@ -1745,10 +1905,12 @@ impl DisplayListBuilder {
filter_datas: &[di::FilterData],
filter_primitives: &[di::FilterPrimitive],
) {
let common = self.process_common_props(common);
self.push_filters(filters, filter_datas, filter_primitives);
let item = di::DisplayItem::BackdropFilter(di::BackdropFilterDisplayItem {
common: *common,
common,
});
self.push_item(&item);
}
@ -1807,6 +1969,19 @@ impl DisplayListBuilder {
external_scroll_offset: LayoutVector2D,
) -> di::SpatialId {
let scroll_frame_id = self.generate_spatial_index();
let current_offset = self.current_offset(parent_space);
let content_rect = content_rect.translate(current_offset);
let frame_rect = frame_rect.translate(current_offset);
let parent = self.spatial_nodes[parent_space.0].clone();
self.add_spatial_node_info(
scroll_frame_id,
parent.accumulated_external_scroll_offset + external_scroll_offset,
parent.snapping_transform,
);
let item = di::DisplayItem::ScrollFrame(di::ScrollFrameDisplayItem {
content_rect,
frame_rect,
@ -1840,11 +2015,18 @@ impl DisplayListBuilder {
pub fn define_clip_image_mask(
&mut self,
parent_space_and_clip: &di::SpaceAndClipInfo,
image_mask: di::ImageMask,
mut image_mask: di::ImageMask,
points: &[LayoutPoint],
fill_rule: di::FillRule,
) -> di::ClipId {
let id = self.generate_clip_index();
let current_offset = self.current_offset(parent_space_and_clip.spatial_id);
image_mask.rect = self.snap_rect(
&image_mask.rect.translate(current_offset),
parent_space_and_clip.spatial_id,
);
let item = di::DisplayItem::ImageMaskClip(di::ImageMaskClipDisplayItem {
id,
parent_space_and_clip: *parent_space_and_clip,
@ -1870,6 +2052,14 @@ impl DisplayListBuilder {
clip_rect: LayoutRect,
) -> di::ClipId {
let id = self.generate_clip_index();
let current_offset = self.current_offset(parent_space_and_clip.spatial_id);
let clip_rect = self.snap_rect(
&clip_rect.translate(current_offset),
parent_space_and_clip.spatial_id,
);
let item = di::DisplayItem::RectClip(di::RectClipDisplayItem {
id,
parent_space_and_clip: *parent_space_and_clip,
@ -1883,9 +2073,16 @@ impl DisplayListBuilder {
pub fn define_clip_rounded_rect(
&mut self,
parent_space_and_clip: &di::SpaceAndClipInfo,
clip: di::ComplexClipRegion,
mut clip: di::ComplexClipRegion,
) -> di::ClipId {
let id = self.generate_clip_index();
let current_offset = self.current_offset(parent_space_and_clip.spatial_id);
clip.rect = self.snap_rect(
&clip.rect.translate(current_offset),
parent_space_and_clip.spatial_id,
);
let item = di::DisplayItem::RoundedRectClip(di::RoundedRectClipDisplayItem {
id,
parent_space_and_clip: *parent_space_and_clip,
@ -1906,6 +2103,18 @@ impl DisplayListBuilder {
previously_applied_offset: LayoutVector2D,
) -> di::SpatialId {
let id = self.generate_spatial_index();
let current_offset = self.current_offset(parent_spatial_id);
let frame_rect = frame_rect.translate(current_offset);
let parent = self.spatial_nodes[parent_spatial_id.0].clone();
self.add_spatial_node_info(
id,
parent.accumulated_external_scroll_offset,
parent.snapping_transform,
);
let item = di::DisplayItem::StickyFrame(di::StickyFrameDisplayItem {
parent_spatial_id,
id,
@ -1928,6 +2137,18 @@ impl DisplayListBuilder {
pipeline_id: PipelineId,
ignore_missing_pipeline: bool
) {
let current_offset = self.current_offset(space_and_clip.spatial_id);
let bounds = self.snap_rect(
&bounds.translate(current_offset),
space_and_clip.spatial_id,
);
let clip_rect = self.snap_rect(
&clip_rect.translate(current_offset),
space_and_clip.spatial_id,
);
let item = di::DisplayItem::Iframe(di::IframeDisplayItem {
bounds,
clip_rect,
@ -2047,4 +2268,124 @@ impl DisplayListBuilder {
},
)
}
/// Retrieve the current offset to allow converting a stacking context
/// relative coordinate to be relative to the owing reference frame,
/// also considering any external scroll offset on the provided
/// spatial node.
fn current_offset(
&mut self,
spatial_id: di::SpatialId,
) -> LayoutVector2D {
// Get the current offset from stacking context <-> reference frame space.
let rf_offset = self.rf_mapper.current_offset();
// Get the external scroll offset, if applicable.
let scroll_offset = self.spatial_nodes[spatial_id.0].accumulated_external_scroll_offset;
rf_offset + scroll_offset
}
/// Add info about a spatial node that is needed during DL building.
fn add_spatial_node_info(
&mut self,
id: di::SpatialId,
accumulated_external_scroll_offset: LayoutVector2D,
snapping_transform: Option<ScaleOffset>,
) {
self.spatial_nodes.resize(id.0 + 1, SpatialNodeInfo::identity());
let info = &mut self.spatial_nodes[id.0];
info.accumulated_external_scroll_offset = accumulated_external_scroll_offset;
info.snapping_transform = snapping_transform;
}
/// Snap a local rect, if applicable
fn snap_rect(
&self,
rect: &LayoutRect,
spatial_id: di::SpatialId,
) -> LayoutRect {
match self.spatial_nodes[spatial_id.0].snapping_transform {
Some(ref scale_offset) => {
let snapped_device_rect: LayoutRect = scale_offset.map_rect(rect).snap();
scale_offset.unmap_rect(&snapped_device_rect)
}
None => *rect,
}
}
}
/// The offset stack for a given reference frame.
#[derive(Clone)]
struct ReferenceFrameState {
/// A stack of current offsets from the current reference frame scope.
offsets: Vec<LayoutVector2D>,
}
/// Maps from stacking context layout coordinates into reference frame
/// relative coordinates.
#[derive(Clone)]
struct ReferenceFrameMapper {
/// A stack of reference frame scopes.
frames: Vec<ReferenceFrameState>,
}
impl ReferenceFrameMapper {
fn new() -> Self {
ReferenceFrameMapper {
frames: vec![
ReferenceFrameState {
offsets: vec![
LayoutVector2D::zero(),
],
}
],
}
}
/// Push a new scope. This resets the current offset to zero, and is
/// used when a new reference frame or iframe is pushed.
fn push_scope(&mut self) {
self.frames.push(ReferenceFrameState {
offsets: vec![
LayoutVector2D::zero(),
],
});
}
/// Pop a reference frame scope off the stack.
fn pop_scope(&mut self) {
self.frames.pop().unwrap();
}
/// Push a new offset for the current scope. This is used when
/// a new stacking context is pushed.
fn push_offset(&mut self, offset: LayoutVector2D) {
let frame = self.frames.last_mut().unwrap();
let current_offset = *frame.offsets.last().unwrap();
frame.offsets.push(current_offset + offset);
}
/// Pop a local stacking context offset from the current scope.
fn pop_offset(&mut self) {
let frame = self.frames.last_mut().unwrap();
frame.offsets.pop().unwrap();
}
/// Retrieve the current offset to allow converting a stacking context
/// relative coordinate to be relative to the owing reference frame.
/// TODO(gw): We could perhaps have separate coordinate spaces for this,
/// however that's going to either mean a lot of changes to
/// public API code, or a lot of changes to internal code.
/// Before doing that, we should revisit how Gecko would
/// prefer to provide coordinates.
/// TODO(gw): For now, this includes only the reference frame relative
/// offset. Soon, we will expand this to include the initial
/// scroll offsets that are now available on scroll nodes. This
/// will allow normalizing the coordinates even between display
/// lists where APZ has scrolled the content.
fn current_offset(&self) -> LayoutVector2D {
*self.frames.last().unwrap().offsets.last().unwrap()
}
}

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

@ -48,6 +48,7 @@ mod font;
mod gradient_builder;
mod image;
pub mod units;
mod util;
pub use crate::color::*;
pub use crate::display_item::*;
@ -56,6 +57,7 @@ pub use crate::display_list::*;
pub use crate::font::*;
pub use crate::gradient_builder::*;
pub use crate::image::*;
pub use crate::util::ScaleOffset;
use crate::units::*;
use crate::channel::Receiver;

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

@ -0,0 +1,266 @@
/* 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 euclid::{Point2D, Box2D, Size2D, Vector2D};
use euclid::{default, Transform3D};
// Matches the definition of SK_ScalarNearlyZero in Skia.
const NEARLY_ZERO: f32 = 1.0 / 4096.0;
// Represents an optimized transform where there is only
// a scale and translation (which are guaranteed to maintain
// an axis align rectangle under transformation). The
// scaling is applied first, followed by the translation.
// TODO(gw): We should try and incorporate F <-> T units here,
// but it's a bit tricky to do that now with the
// way the current spatial tree works.
#[derive(Debug, Clone, Copy, MallocSizeOf)]
#[derive(Serialize, Deserialize)]
pub struct ScaleOffset {
pub scale: default::Vector2D<f32>,
pub offset: default::Vector2D<f32>,
}
impl ScaleOffset {
pub fn identity() -> Self {
ScaleOffset {
scale: Vector2D::new(1.0, 1.0),
offset: Vector2D::zero(),
}
}
// Construct a ScaleOffset from a transform. Returns
// None if the matrix is not a pure scale / translation.
pub fn from_transform<F, T>(
m: &Transform3D<f32, F, T>,
) -> Option<ScaleOffset> {
// To check that we have a pure scale / translation:
// Every field must match an identity matrix, except:
// - Any value present in tx,ty
// - Any value present in sx,sy
if m.m12.abs() > NEARLY_ZERO ||
m.m13.abs() > NEARLY_ZERO ||
m.m14.abs() > NEARLY_ZERO ||
m.m21.abs() > NEARLY_ZERO ||
m.m23.abs() > NEARLY_ZERO ||
m.m24.abs() > NEARLY_ZERO ||
m.m31.abs() > NEARLY_ZERO ||
m.m32.abs() > NEARLY_ZERO ||
(m.m33 - 1.0).abs() > NEARLY_ZERO ||
m.m34.abs() > NEARLY_ZERO ||
m.m43.abs() > NEARLY_ZERO ||
(m.m44 - 1.0).abs() > NEARLY_ZERO {
return None;
}
Some(ScaleOffset {
scale: Vector2D::new(m.m11, m.m22),
offset: Vector2D::new(m.m41, m.m42),
})
}
pub fn from_offset(offset: default::Vector2D<f32>) -> Self {
ScaleOffset {
scale: Vector2D::new(1.0, 1.0),
offset,
}
}
pub fn from_scale(scale: default::Vector2D<f32>) -> Self {
ScaleOffset {
scale,
offset: Vector2D::new(0.0, 0.0),
}
}
pub fn inverse(&self) -> Self {
ScaleOffset {
scale: Vector2D::new(
1.0 / self.scale.x,
1.0 / self.scale.y,
),
offset: Vector2D::new(
-self.offset.x / self.scale.x,
-self.offset.y / self.scale.y,
),
}
}
pub fn offset(&self, offset: default::Vector2D<f32>) -> Self {
self.accumulate(
&ScaleOffset {
scale: Vector2D::new(1.0, 1.0),
offset,
}
)
}
pub fn scale(&self, scale: f32) -> Self {
self.accumulate(
&ScaleOffset {
scale: Vector2D::new(scale, scale),
offset: Vector2D::zero(),
}
)
}
/// Produce a ScaleOffset that includes both self and other.
/// The 'self' ScaleOffset is applied after other.
/// This is equivalent to `Transform3D::pre_transform`.
pub fn accumulate(&self, other: &ScaleOffset) -> Self {
ScaleOffset {
scale: Vector2D::new(
self.scale.x * other.scale.x,
self.scale.y * other.scale.y,
),
offset: Vector2D::new(
self.offset.x + self.scale.x * other.offset.x,
self.offset.y + self.scale.y * other.offset.y,
),
}
}
pub fn map_rect<F, T>(&self, rect: &Box2D<f32, F>) -> Box2D<f32, T> {
// TODO(gw): The logic below can return an unexpected result if the supplied
// rect is invalid (has size < 0). Since Gecko currently supplied
// invalid rects in some cases, adding a max(0) here ensures that
// mapping an invalid rect retains the property that rect.is_empty()
// will return true (the mapped rect output will have size 0 instead
// of a negative size). In future we could catch / assert / fix
// these invalid rects earlier, and assert here instead.
let w = rect.width().max(0.0);
let h = rect.height().max(0.0);
let mut x0 = rect.min.x * self.scale.x + self.offset.x;
let mut y0 = rect.min.y * self.scale.y + self.offset.y;
let mut sx = w * self.scale.x;
let mut sy = h * self.scale.y;
// Handle negative scale. Previously, branchless float math was used to find the
// min / max vertices and size. However, that sequence of operations was producind
// additional floating point accuracy on android emulator builds, causing one test
// to fail an assert. Instead, we retain the same math as previously, and adjust
// the origin / size if required.
if self.scale.x < 0.0 {
x0 += sx;
sx = -sx;
}
if self.scale.y < 0.0 {
y0 += sy;
sy = -sy;
}
Box2D::from_origin_and_size(
Point2D::new(x0, y0),
Size2D::new(sx, sy),
)
}
pub fn unmap_rect<F, T>(&self, rect: &Box2D<f32, F>) -> Box2D<f32, T> {
// TODO(gw): The logic below can return an unexpected result if the supplied
// rect is invalid (has size < 0). Since Gecko currently supplied
// invalid rects in some cases, adding a max(0) here ensures that
// mapping an invalid rect retains the property that rect.is_empty()
// will return true (the mapped rect output will have size 0 instead
// of a negative size). In future we could catch / assert / fix
// these invalid rects earlier, and assert here instead.
let w = rect.width().max(0.0);
let h = rect.height().max(0.0);
let mut x0 = (rect.min.x - self.offset.x) / self.scale.x;
let mut y0 = (rect.min.y - self.offset.y) / self.scale.y;
let mut sx = w / self.scale.x;
let mut sy = h / self.scale.y;
// Handle negative scale. Previously, branchless float math was used to find the
// min / max vertices and size. However, that sequence of operations was producind
// additional floating point accuracy on android emulator builds, causing one test
// to fail an assert. Instead, we retain the same math as previously, and adjust
// the origin / size if required.
if self.scale.x < 0.0 {
x0 += sx;
sx = -sx;
}
if self.scale.y < 0.0 {
y0 += sy;
sy = -sy;
}
Box2D::from_origin_and_size(
Point2D::new(x0, y0),
Size2D::new(sx, sy),
)
}
pub fn map_vector<F, T>(&self, vector: &Vector2D<f32, F>) -> Vector2D<f32, T> {
Vector2D::new(
vector.x * self.scale.x,
vector.y * self.scale.y,
)
}
pub fn unmap_vector<F, T>(&self, vector: &Vector2D<f32, F>) -> Vector2D<f32, T> {
Vector2D::new(
vector.x / self.scale.x,
vector.y / self.scale.y,
)
}
pub fn map_point<F, T>(&self, point: &Point2D<f32, F>) -> Point2D<f32, T> {
Point2D::new(
point.x * self.scale.x + self.offset.x,
point.y * self.scale.y + self.offset.y,
)
}
pub fn unmap_point<F, T>(&self, point: &Point2D<f32, F>) -> Point2D<f32, T> {
Point2D::new(
(point.x - self.offset.x) / self.scale.x,
(point.y - self.offset.y) / self.scale.y,
)
}
pub fn to_transform<F, T>(&self) -> Transform3D<f32, F, T> {
Transform3D::new(
self.scale.x,
0.0,
0.0,
0.0,
0.0,
self.scale.y,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
self.offset.x,
self.offset.y,
0.0,
1.0,
)
}
}
pub trait RectHelpers<U>
where
Self: Sized,
{
fn snap(&self) -> Self;
}
impl<U> RectHelpers<U> for Box2D<f32, U> {
fn snap(&self) -> Self {
self.round()
}
}

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

@ -76,7 +76,7 @@ fuzzy(1,7) == tiling-conic-3.yaml tiling-conic-3-ref.yaml
== radial-zero-size-2.yaml radial-zero-size-ref.yaml
== radial-zero-size-3.yaml radial-zero-size-ref.yaml
== linear-adjust-tile-size.yaml linear-adjust-tile-size-ref.yaml
fuzzy(2,34) == linear-adjust-tile-size.yaml linear-adjust-tile-size-ref.yaml
== linear-repeat-clip.yaml linear-repeat-clip-ref.yaml
platform(linux,mac) == linear-aligned-border-radius.yaml linear-aligned-border-radius.png