--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -97,17 +97,17 @@ use util::{extract_inner_rect_safe, proj
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Clone, Copy, Debug)]
pub struct ClipDataMarker;
pub type ClipDataStore = intern::DataStore<ClipItemKey, ClipNode, ClipDataMarker>;
pub type ClipDataHandle = intern::Handle<ClipDataMarker>;
pub type ClipDataUpdateList = intern::UpdateList<ClipItemKey>;
-pub type ClipDataInterner = intern::Interner<ClipItemKey, ClipDataMarker>;
+pub type ClipDataInterner = intern::Interner<ClipItemKey, ClipItemSceneData, ClipDataMarker>;
// Result of comparing a clip node instance against a local rect.
#[derive(Debug)]
enum ClipResult {
// The clip does not affect the region at all.
Accept,
// The clip prevents the region from being drawn.
Reject,
@@ -747,16 +747,30 @@ impl ClipRegion<Option<ComplexClipRegion
ClipRegion {
main: local_clip.translate(reference_frame_relative_offset),
image_mask: None,
complex_clips: None,
}
}
}
+/// The information about an interned clip item that
+/// is stored and available in the scene builder
+/// thread.
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct ClipItemSceneData {
+ // TODO(gw): We will store the local clip rect of
+ // the clip item here. This will allow
+ // calculation of the local clip rect
+ // for a primitive and its clip chain
+ // during scene building, rather than
+ // frame building.
+}
+
// The ClipItemKey is a hashable representation of the contents
// of a clip item. It is used during interning to de-duplicate
// clip nodes between frames and display lists. This allows quick
// comparison of clip node equality by handle, and also allows
// the uploaded GPU cache handle to be retained between display lists.
// TODO(gw): Maybe we should consider constructing these directly
// in the DL builder?
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -8,29 +8,29 @@ use api::{ClipId, ColorF, ComplexClipReg
use api::{DisplayItemRef, ExtendMode, ExternalScrollId};
use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, RasterSpace, GradientStop};
use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, LayoutPoint, ColorDepth};
use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
use api::{LineOrientation, LineStyle, NinePatchBorderSource, PipelineId};
use api::{PropertyBinding, ReferenceFrame, RepeatMode, ScrollFrameDisplayItem, ScrollSensitivity};
use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect};
use api::{ClipMode, TransformStyle, YuvColorSpace, YuvData};
-use clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore};
+use clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore, ClipItemSceneData};
use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex};
use euclid::vec2;
use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig};
use glyph_rasterizer::FontInstance;
use gpu_cache::GpuCacheHandle;
use gpu_types::BrushFlags;
use hit_test::{HitTestingItem, HitTestingRun};
use image::simplify_repeated_primitive;
use internal_types::{FastHashMap, FastHashSet};
use picture::{Picture3DContext, PictureCompositeMode, PictureIdGenerator, PicturePrimitive};
use prim_store::{BrushKind, BrushPrimitive, BrushSegmentDescriptor, PrimitiveInstance};
-use prim_store::{EdgeAaSegmentMask, ImageSource, PrimitiveOpacity, PrimitiveKey};
+use prim_store::{EdgeAaSegmentMask, ImageSource, PrimitiveOpacity, PrimitiveKey, PrimitiveSceneData};
use prim_store::{BorderSource, BrushSegment, BrushSegmentVec, PrimitiveContainer, PrimitiveDataHandle, PrimitiveStore};
use prim_store::{OpacityBinding, ScrollNodeAndClipChain, TextRunPrimitive, PictureIndex};
use render_backend::{DocumentView};
use resource_cache::{FontInstanceMap, ImageRequest};
use scene::{Scene, ScenePipeline, StackingContextHelpers};
use scene_builder::DocumentResources;
use spatial_node::{StickyFrameInfo};
use std::{f32, mem};
@@ -794,17 +794,22 @@ impl<'a> DisplayListFlattener<'a> {
if clip_items.is_empty() {
parent_clip_chain_id
} else {
let mut clip_chain_id = parent_clip_chain_id;
for item in clip_items {
// Intern this clip item, and store the handle
// in the clip chain node.
- let handle = self.resources.clip_interner.intern(&item);
+ let handle = self.resources
+ .clip_interner
+ .intern(&item, || {
+ ClipItemSceneData {
+ }
+ });
clip_chain_id = self.clip_store
.add_clip_chain_node(
handle,
spatial_node_index,
clip_chain_id,
);
}
@@ -820,17 +825,22 @@ impl<'a> DisplayListFlattener<'a> {
&mut self,
info: &LayoutPrimitiveInfo,
clip_chain_id: ClipChainId,
spatial_node_index: SpatialNodeIndex,
container: PrimitiveContainer,
) -> PrimitiveInstance {
let prim_key = PrimitiveKey::new(info.is_backface_visible);
- let prim_data_handle = self.resources.prim_interner.intern(&prim_key);
+ let prim_data_handle = self.resources
+ .prim_interner
+ .intern(&prim_key, || {
+ PrimitiveSceneData {
+ }
+ });
let prim_index = self.prim_store.add_primitive(
&info.rect,
&info.clip_rect,
container,
);
PrimitiveInstance::new(
@@ -998,17 +1008,23 @@ impl<'a> DisplayListFlattener<'a> {
// Force an intermediate surface if the stacking context
// has a clip node. In the future, we may decide during
// prepare step to skip the intermediate surface if the
// clip node doesn't affect the stacking context rect.
let should_isolate = clipping_node.is_some();
let prim_key = PrimitiveKey::new(is_backface_visible);
- let primitive_data_handle = self.resources.prim_interner.intern(&prim_key);
+ let primitive_data_handle = self.resources
+ .prim_interner
+ .intern(&prim_key, || {
+ PrimitiveSceneData {
+ }
+ }
+ );
// Push the SC onto the stack, so we know how to handle things in
// pop_stacking_context.
self.sc_stack.push(FlattenedStackingContext {
primitives: Vec::new(),
pipeline_id,
primitive_data_handle,
requested_raster_space,
@@ -1321,48 +1337,57 @@ impl<'a> DisplayListFlattener<'a> {
// handle to a clip chain node, parented to form a chain.
// TODO(gw): We could re-structure this to share some of the
// interning and chaining code.
// Build the clip sources from the supplied region.
let handle = self
.resources
.clip_interner
- .intern(&ClipItemKey::rectangle(clip_region.main, ClipMode::Clip));
+ .intern(&ClipItemKey::rectangle(clip_region.main, ClipMode::Clip), || {
+ ClipItemSceneData {
+ }
+ });
parent_clip_chain_index = self
.clip_store
.add_clip_chain_node(
handle,
spatial_node,
parent_clip_chain_index,
);
clip_count += 1;
if let Some(ref image_mask) = clip_region.image_mask {
let handle = self
.resources
.clip_interner
- .intern(&ClipItemKey::image_mask(image_mask));
+ .intern(&ClipItemKey::image_mask(image_mask), || {
+ ClipItemSceneData {
+ }
+ });
parent_clip_chain_index = self
.clip_store
.add_clip_chain_node(
handle,
spatial_node,
parent_clip_chain_index,
);
clip_count += 1;
}
for region in clip_region.complex_clips {
let handle = self
.resources
.clip_interner
- .intern(&ClipItemKey::rounded_rect(region.rect, region.radii, region.mode));
+ .intern(&ClipItemKey::rounded_rect(region.rect, region.radii, region.mode), || {
+ ClipItemSceneData {
+ }
+ });
parent_clip_chain_index = self
.clip_store
.add_clip_chain_node(
handle,
spatial_node,
parent_clip_chain_index,
);
@@ -1509,17 +1534,23 @@ impl<'a> DisplayListFlattener<'a> {
let shadow_prim = BrushPrimitive::new_picture(shadow_pic_index);
let shadow_prim_index = self.prim_store.add_primitive(
&LayoutRect::zero(),
&max_clip,
PrimitiveContainer::Brush(shadow_prim),
);
let shadow_prim_key = PrimitiveKey::new(true);
- let shadow_prim_data_handle = self.resources.prim_interner.intern(&shadow_prim_key);
+ let shadow_prim_data_handle = self.resources
+ .prim_interner
+ .intern(&shadow_prim_key, || {
+ PrimitiveSceneData {
+ }
+ }
+ );
let shadow_prim_instance = PrimitiveInstance::new(
shadow_prim_index,
shadow_prim_data_handle,
pending_shadow.clip_and_scroll.clip_chain_id,
pending_shadow.clip_and_scroll.spatial_node_index,
);
--- a/gfx/webrender/src/intern.rs
+++ b/gfx/webrender/src/intern.rs
@@ -168,77 +168,79 @@ impl<S, T, M> ops::IndexMut<Handle<M>> f
/// The main interning data structure. This lives in the
/// scene builder thread, and handles hashing and interning
/// unique data structures. It also manages a free-list for
/// the items in the data store, which is synchronized via
/// an update list of additions / removals.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
-pub struct Interner<S : Eq + Hash + Clone + Debug, M> {
+pub struct Interner<S : Eq + Hash + Clone + Debug, D, M> {
/// Uniquely map an interning key to a handle
map: FastHashMap<S, Handle<M>>,
/// List of free slots in the data store for re-use.
free_list: Vec<usize>,
- /// The next index to append items to if free-list is empty.
- next_index: usize,
/// Pending list of updates that need to be applied.
updates: Vec<Update<S>>,
/// The current epoch for the interner.
current_epoch: Epoch,
+ /// The information associated with each interned
+ /// item that can be accessed by the interner.
+ local_data: Vec<Item<D>>,
}
-impl<S, M> Interner<S, M> where S: Eq + Hash + Clone + Debug, M: Copy + Debug {
+impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + Debug {
/// Construct a new interner
pub fn new() -> Self {
Interner {
map: FastHashMap::default(),
free_list: Vec::new(),
- next_index: 0,
updates: Vec::new(),
current_epoch: Epoch(1),
+ local_data: Vec::new(),
}
}
/// Intern a data structure, and return a handle to
/// that data. The handle can then be stored in the
/// frame builder, and safely accessed via the data
/// store that lives in the frame builder thread.
- pub fn intern(
+ /// The provided closure is invoked to build the
+ /// local data about an interned structure if the
+ /// key isn't already interned.
+ pub fn intern<F>(
&mut self,
data: &S,
- ) -> Handle<M> {
+ f: F,
+ ) -> Handle<M> where F: FnOnce() -> D {
// Use get_mut rather than entry here to avoid
// cloning the (sometimes large) key in the common
// case, where the data already exists in the interner.
if let Some(handle) = self.map.get_mut(data) {
// Update the epoch in the data store. This
// is not strictly needed for correctness, but
// is used to ensure items are only accessed
// via valid handles.
if handle.epoch != self.current_epoch {
self.updates.push(Update {
index: handle.index,
kind: UpdateKind::UpdateEpoch,
- })
+ });
+ self.local_data[handle.index].epoch = self.current_epoch;
}
handle.epoch = self.current_epoch;
return *handle;
}
// We need to intern a new data item. First, find out
// if there is a spare slot in the free-list that we
// can use. Otherwise, append to the end of the list.
let index = match self.free_list.pop() {
Some(index) => index,
- None => {
- let index = self.next_index;
- self.next_index += 1;
- index
- }
+ None => self.local_data.len(),
};
// Add a pending update to insert the new data.
self.updates.push(Update {
index,
kind: UpdateKind::Insert(data.clone()),
});
@@ -248,16 +250,28 @@ impl<S, M> Interner<S, M> where S: Eq +
epoch: self.current_epoch,
_marker: PhantomData,
};
// Store this handle so the next time it is
// interned, it gets re-used.
self.map.insert(data.clone(), handle);
+ // Create the local data for this item that is
+ // being interned.
+ let local_item = Item {
+ epoch: self.current_epoch,
+ data: f(),
+ };
+ if self.local_data.len() == index {
+ self.local_data.push(local_item);
+ } else {
+ self.local_data[index] = local_item;
+ }
+
handle
}
/// Retrieve the pending list of updates for an interner
/// that need to be applied to the data store. Also run
/// a GC step that removes old entries.
pub fn end_frame_and_get_pending_updates(&mut self) -> UpdateList<S> {
let mut updates = mem::replace(&mut self.updates, Vec::new());
@@ -294,8 +308,18 @@ impl<S, M> Interner<S, M> where S: Eq +
};
// Begin the next epoch
self.current_epoch = Epoch(self.current_epoch.0 + 1);
updates
}
}
+
+/// Retrieve the local data for an item from the interner via handle
+impl<S, D, M> ops::Index<Handle<M>> for Interner<S, D, M> where S: Eq + Clone + Hash + Debug, M: Copy + Debug {
+ type Output = D;
+ fn index(&self, handle: Handle<M>) -> &D {
+ let item = &self.local_data[handle.index];
+ assert_eq!(item.epoch, handle.epoch);
+ &item.data
+ }
+}
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -270,16 +270,29 @@ impl GpuCacheAddress {
pub fn as_int(&self) -> i32 {
// TODO(gw): Temporarily encode GPU Cache addresses as a single int.
// In the future, we can change the PrimitiveInstanceData struct
// to use 2x u16 for the vertex attribute instead of an i32.
self.v as i32 * MAX_VERTEX_TEXTURE_WIDTH as i32 + self.u as i32
}
}
+/// The information about an interned primitive that
+/// is stored and available in the scene builder
+/// thread.
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct PrimitiveSceneData {
+ // TODO(gw): We will store the local clip rect of
+ // the primitive here. This will allow
+ // fast calculation of the tight local
+ // bounding rect of a primitive during
+ // picture traversal.
+}
+
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct PrimitiveKey {
pub is_backface_visible: bool,
}
impl PrimitiveKey {
@@ -310,17 +323,17 @@ impl From<PrimitiveKey> for PrimitiveTem
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Clone, Copy, Debug)]
pub struct PrimitiveDataMarker;
pub type PrimitiveDataStore = intern::DataStore<PrimitiveKey, PrimitiveTemplate, PrimitiveDataMarker>;
pub type PrimitiveDataHandle = intern::Handle<PrimitiveDataMarker>;
pub type PrimitiveDataUpdateList = intern::UpdateList<PrimitiveKey>;
-pub type PrimitiveDataInterner = intern::Interner<PrimitiveKey, PrimitiveDataMarker>;
+pub type PrimitiveDataInterner = intern::Interner<PrimitiveKey, PrimitiveSceneData, PrimitiveDataMarker>;
// Maintains a list of opacity bindings that have been collapsed into
// the color of a single primitive. This is an important optimization
// that avoids allocating an intermediate surface for most common
// uses of opacity filters.
#[derive(Debug)]
pub struct OpacityBinding {
bindings: Vec<PropertyBinding<f32>>,
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-a8817b943a2fd0038307a7432fdf5cbccf4a943e
+a7052abfe8e41bcc8904cb5b3add99735fedcd1f