--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -492,16 +492,19 @@ impl AlphaBatchBuilder {
prim_headers,
);
}
// Flush the accumulated plane splits onto the task tree.
// Z axis is directed at the screen, `sort` is ascending, and we need back-to-front order.
for poly in splitter.sort(vec3(0.0, 0.0, 1.0)) {
let prim_index = PrimitiveIndex(poly.anchor);
+ if cfg!(debug_assertions) && ctx.prim_store.chase_id == Some(prim_index) {
+ println!("\t\tsplit polygon {:?}", poly.points);
+ }
debug!("process sorted poly {:?} {:?}", prim_index, poly.points);
let pp = &poly.points;
let gpu_blocks = [
[pp[0].x as f32, pp[0].y as f32, pp[0].z as f32, pp[1].x as f32].into(),
[pp[1].y as f32, pp[1].z as f32, pp[2].x as f32, pp[2].y as f32].into(),
[pp[2].z as f32, pp[3].x as f32, pp[3].y as f32, pp[3].z as f32].into(),
];
let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
@@ -649,24 +652,28 @@ impl AlphaBatchBuilder {
local_rect: prim_metadata.local_rect,
local_clip_rect: prim_metadata.combined_local_clip_rect,
task_address,
specific_prim_address: prim_cache_address,
clip_task_address,
transform_id,
};
+ if cfg!(debug_assertions) && ctx.prim_store.chase_id == Some(prim_index) {
+ println!("\ttask target {:?}", self.target_rect);
+ println!("\t{:?}", prim_header);
+ }
+
match prim_metadata.prim_kind {
PrimitiveKind::Brush => {
let brush = &ctx.prim_store.cpu_brushes[prim_metadata.cpu_prim_index.0];
match brush.kind {
BrushKind::Picture { pic_index, .. } => {
- let picture =
- &ctx.prim_store.pictures[pic_index.0];
+ let picture = &ctx.prim_store.pictures[pic_index.0];
// If this picture is participating in a 3D rendering context,
// then don't add it to any batches here. Instead, create a polygon
// for it and add it to the current plane splitter.
if picture.is_in_3d_context {
// Push into parent plane splitter.
debug_assert!(picture.surface.is_some());
@@ -1051,18 +1058,23 @@ impl AlphaBatchBuilder {
prim_headers,
);
}
_ => {
if let Some((batch_kind, textures, user_data)) = brush.get_batch_params(
ctx.resource_cache,
gpu_cache,
deferred_resolves,
+ ctx.prim_store.chase_id == Some(prim_index),
) {
let prim_header_index = prim_headers.push(&prim_header, user_data);
+ if cfg!(debug_assertions) && ctx.prim_store.chase_id == Some(prim_index) {
+ println!("\t{:?} {:?}, task relative bounds {:?}",
+ batch_kind, prim_header_index, task_relative_bounding_rect);
+ }
self.add_brush_to_batch(
brush,
prim_metadata,
batch_kind,
specified_blend_mode,
non_segmented_blend_mode,
textures,
@@ -1380,16 +1392,17 @@ impl BrushPrimitive {
}
}
fn get_batch_params(
&self,
resource_cache: &ResourceCache,
gpu_cache: &mut GpuCache,
deferred_resolves: &mut Vec<DeferredResolve>,
+ is_chased: bool,
) -> Option<(BrushBatchKind, BatchTextures, [i32; 3])> {
match self.kind {
BrushKind::Image { request, ref source, .. } => {
let cache_item = match *source {
ImageSource::Default => {
resolve_image(
request,
resource_cache,
@@ -1401,16 +1414,19 @@ impl BrushPrimitive {
let rt_handle = handle
.as_ref()
.expect("bug: render task handle not allocated");
let rt_cache_entry = resource_cache
.get_cached_render_task(rt_handle);
resource_cache.get_texture_cache_item(&rt_cache_entry.handle)
}
};
+ if cfg!(debug_assertions) && is_chased {
+ println!("\tsource {:?}", cache_item);
+ }
if cache_item.texture_id == SourceTexture::Invalid {
None
} else {
let textures = BatchTextures::color(cache_item.texture_id);
Some((
BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)),
@@ -1746,17 +1762,17 @@ impl ClipBatcher {
pub fn add_clip_region(
&mut self,
task_address: RenderTaskAddress,
clip_data_address: GpuCacheAddress,
) {
let instance = ClipMaskInstance {
render_task_address: task_address,
- transform_id: TransformPaletteId::identity(),
+ transform_id: TransformPaletteId::IDENTITY,
segment: 0,
clip_data_address,
resource_address: GpuCacheAddress::invalid(),
};
self.rectangles.push(instance);
}
@@ -1767,26 +1783,24 @@ impl ClipBatcher {
coordinate_system_id: CoordinateSystemId,
resource_cache: &ResourceCache,
gpu_cache: &GpuCache,
clip_store: &ClipStore,
transforms: &TransformPalette,
) {
let mut coordinate_system_id = coordinate_system_id;
for work_item in clips.iter() {
+ let info = clip_store.get(work_item.clip_sources_index);
let instance = ClipMaskInstance {
render_task_address: task_address,
- transform_id: transforms.get_id(work_item.spatial_node_index),
+ transform_id: transforms.get_id(info.spatial_node_index),
segment: 0,
clip_data_address: GpuCacheAddress::invalid(),
resource_address: GpuCacheAddress::invalid(),
};
- let info = clip_store
- .get_opt(&work_item.clip_sources)
- .expect("bug: clip handle should be valid");
for &(ref source, ref handle) in &info.clips {
let gpu_address = gpu_cache.get_address(handle);
match *source {
ClipSource::Image(ref mask) => {
if let Ok(cache_item) = resource_cache.get_cached_image(
ImageRequest {
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -4,32 +4,61 @@
use api::{BorderRadius, ClipMode, ComplexClipRegion, DeviceIntRect, DevicePixelScale, ImageMask};
use api::{ImageRendering, LayoutRect, LayoutSize, LayoutPoint, LayoutVector2D, LocalClip};
use api::{BoxShadowClipMode, LayoutToWorldScale, LineOrientation, LineStyle};
use border::{ensure_no_corner_overlap};
use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey};
use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId, SpatialNodeIndex};
use ellipse::Ellipse;
-use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
use gpu_types::BoxShadowStretchMode;
use prim_store::{ClipData, ImageMaskData};
use render_task::to_cache_size;
use resource_cache::{ImageRequest, ResourceCache};
use util::{LayoutToWorldFastTransform, MaxRect, calculate_screen_bounding_rect};
-use util::{extract_inner_rect_safe, pack_as_float};
+use util::{extract_inner_rect_safe, pack_as_float, recycle_vec};
use std::sync::Arc;
-#[derive(Debug)]
-pub enum ClipStoreMarker {}
+#[derive(Debug, Copy, Clone)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct ClipSourcesIndex(usize);
+
+pub struct ClipStore {
+ clip_sources: Vec<ClipSources>,
+}
+
+impl ClipStore {
+ pub fn new() -> ClipStore {
+ ClipStore {
+ clip_sources: Vec::new(),
+ }
+ }
-pub type ClipStore = FreeList<ClipSources, ClipStoreMarker>;
-pub type ClipSourcesHandle = FreeListHandle<ClipStoreMarker>;
-pub type ClipSourcesWeakHandle = WeakFreeListHandle<ClipStoreMarker>;
+ pub fn recycle(self) -> ClipStore {
+ ClipStore {
+ clip_sources: recycle_vec(self.clip_sources),
+ }
+ }
+
+ pub fn insert(&mut self, clip_sources: ClipSources) -> ClipSourcesIndex {
+ let index = ClipSourcesIndex(self.clip_sources.len());
+ self.clip_sources.push(clip_sources);
+ index
+ }
+
+ pub fn get(&self, index: ClipSourcesIndex) -> &ClipSources {
+ &self.clip_sources[index.0]
+ }
+
+ pub fn get_mut(&mut self, index: ClipSourcesIndex) -> &mut ClipSources {
+ &mut self.clip_sources[index.0]
+ }
+}
#[derive(Debug)]
pub struct LineDecorationClipSource {
rect: LayoutRect,
style: LineStyle,
orientation: LineOrientation,
wavy_line_thickness: f32,
}
@@ -86,38 +115,16 @@ impl ClipRegion {
pub enum ClipSource {
Rectangle(LayoutRect, ClipMode),
RoundedRectangle(LayoutRect, BorderRadius, ClipMode),
Image(ImageMask),
BoxShadow(BoxShadowClipSource),
LineDecoration(LineDecorationClipSource),
}
-impl From<ClipRegion> for ClipSources {
- fn from(region: ClipRegion) -> ClipSources {
- let mut clips = Vec::new();
-
- if let Some(info) = region.image_mask {
- clips.push(ClipSource::Image(info));
- }
-
- clips.push(ClipSource::Rectangle(region.main, ClipMode::Clip));
-
- for complex in region.complex_clips {
- clips.push(ClipSource::new_rounded_rect(
- complex.rect,
- complex.radii,
- complex.mode,
- ));
- }
-
- ClipSources::new(clips)
- }
-}
-
impl ClipSource {
pub fn new_rounded_rect(
rect: LayoutRect,
mut radii: BorderRadius,
clip_mode: ClipMode
) -> ClipSource {
if radii.is_zero() {
ClipSource::Rectangle(rect, clip_mode)
@@ -275,20 +282,24 @@ impl ClipSource {
#[derive(Debug)]
pub struct ClipSources {
pub clips: Vec<(ClipSource, GpuCacheHandle)>,
pub local_inner_rect: LayoutRect,
pub local_outer_rect: Option<LayoutRect>,
pub only_rectangular_clips: bool,
pub has_image_or_line_decoration_clip: bool,
+ pub spatial_node_index: SpatialNodeIndex,
}
impl ClipSources {
- pub fn new(clips: Vec<ClipSource>) -> Self {
+ pub fn new(
+ clips: Vec<ClipSource>,
+ spatial_node_index: SpatialNodeIndex,
+ ) -> Self {
let (local_inner_rect, local_outer_rect) = Self::calculate_inner_and_outer_rects(&clips);
let has_image_or_line_decoration_clip =
clips.iter().any(|clip| clip.is_image_or_line_decoration_clip());
let only_rectangular_clips =
!has_image_or_line_decoration_clip && clips.iter().all(|clip| clip.is_rect());
let clips = clips
.into_iter()
@@ -296,19 +307,43 @@ impl ClipSources {
.collect();
ClipSources {
clips,
local_inner_rect,
local_outer_rect,
only_rectangular_clips,
has_image_or_line_decoration_clip,
+ spatial_node_index,
}
}
+ pub fn from_region(
+ region: ClipRegion,
+ spatial_node_index: SpatialNodeIndex,
+ ) -> ClipSources {
+ let mut clips = Vec::new();
+
+ if let Some(info) = region.image_mask {
+ clips.push(ClipSource::Image(info));
+ }
+
+ clips.push(ClipSource::Rectangle(region.main, ClipMode::Clip));
+
+ for complex in region.complex_clips {
+ clips.push(ClipSource::new_rounded_rect(
+ complex.rect,
+ complex.radii,
+ complex.mode,
+ ));
+ }
+
+ ClipSources::new(clips, spatial_node_index)
+ }
+
pub fn clips(&self) -> &[(ClipSource, GpuCacheHandle)] {
&self.clips
}
fn calculate_inner_and_outer_rects(clips: &Vec<ClipSource>) -> (LayoutRect, Option<LayoutRect>) {
if clips.is_empty() {
return (LayoutRect::zero(), None);
}
@@ -642,13 +677,12 @@ impl Iterator for ClipChainNodeIter {
previous
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ClipWorkItem {
- pub spatial_node_index: SpatialNodeIndex,
- pub clip_sources: ClipSourcesWeakHandle,
+ pub clip_sources_index: ClipSourcesIndex,
pub coordinate_system_id: CoordinateSystemId,
}
--- a/gfx/webrender/src/clip_node.rs
+++ b/gfx/webrender/src/clip_node.rs
@@ -1,70 +1,50 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::DevicePixelScale;
-use clip::{ClipChain, ClipChainNode, ClipSourcesHandle, ClipStore, ClipWorkItem};
-use clip_scroll_tree::{ClipChainIndex, SpatialNodeIndex};
+use clip::{ClipChain, ClipChainNode, ClipSourcesIndex, ClipStore, ClipWorkItem};
+use clip_scroll_tree::{ClipChainIndex};
use gpu_cache::GpuCache;
use resource_cache::ResourceCache;
use spatial_node::SpatialNode;
#[derive(Debug)]
pub struct ClipNode {
- /// The node that determines how this clip node is positioned.
- pub spatial_node: SpatialNodeIndex,
-
/// A handle to this clip nodes clips in the ClipStore.
- pub handle: Option<ClipSourcesHandle>,
+ pub clip_sources_index: ClipSourcesIndex,
/// An index to a ClipChain defined by this ClipNode's hiearchy in the display
/// list.
pub clip_chain_index: ClipChainIndex,
/// The index of the parent ClipChain of this node's hiearchical ClipChain.
pub parent_clip_chain_index: ClipChainIndex,
/// A copy of the ClipChainNode this node would produce. We need to keep a copy,
/// because the ClipChain may not contain our node if is optimized out, but API
/// defined ClipChains will still need to access it.
pub clip_chain_node: Option<ClipChainNode>,
}
impl ClipNode {
- const EMPTY: ClipNode = ClipNode {
- spatial_node: SpatialNodeIndex(0),
- handle: None,
- clip_chain_index: ClipChainIndex::NO_CLIP,
- parent_clip_chain_index: ClipChainIndex::NO_CLIP,
- clip_chain_node: None,
- };
-
- pub fn empty() -> ClipNode {
- ClipNode::EMPTY
- }
-
pub fn update(
&mut self,
- spatial_node: &SpatialNode,
device_pixel_scale: DevicePixelScale,
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
clip_chains: &mut [ClipChain],
+ spatial_nodes: &[SpatialNode],
) {
- let (clip_sources, weak_handle) = match self.handle {
- Some(ref handle) => (clip_store.get_mut(handle), handle.weak()),
- None => {
- warn!("Tried to process an empty clip node");
- return;
- }
- };
+ let clip_sources = clip_store.get_mut(self.clip_sources_index);
clip_sources.update(gpu_cache, resource_cache, device_pixel_scale);
+ let spatial_node = &spatial_nodes[clip_sources.spatial_node_index.0];
let (screen_inner_rect, screen_outer_rect) = clip_sources.get_screen_bounds(
&spatial_node.world_content_transform,
device_pixel_scale,
None,
);
// All clipping SpatialNodes should have outer rectangles, because they never
@@ -72,18 +52,17 @@ impl ClipNode {
// Rectangle ClipSource.
let screen_outer_rect = screen_outer_rect
.expect("Clipping node didn't have outer rect.");
let local_outer_rect = clip_sources.local_outer_rect
.expect("Clipping node didn't have outer rect.");
let new_node = ClipChainNode {
work_item: ClipWorkItem {
- spatial_node_index: self.spatial_node,
- clip_sources: weak_handle,
+ clip_sources_index: self.clip_sources_index,
coordinate_system_id: spatial_node.coordinate_system_id,
},
local_clip_rect: spatial_node
.coordinate_system_relative_transform
.transform_rect(&local_outer_rect)
.expect("clip node transform is not valid"),
screen_outer_rect,
screen_inner_rect,
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -1,16 +1,16 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{DeviceIntRect, DevicePixelScale, ExternalScrollId, LayoutPoint, LayoutRect, LayoutVector2D};
use api::{PipelineId, ScrollClamping, ScrollLocation, ScrollNodeState};
use api::{LayoutSize, LayoutTransform, PropertyBinding, ScrollSensitivity, WorldPoint};
-use clip::{ClipChain, ClipSourcesHandle, ClipStore};
+use clip::{ClipChain, ClipSourcesIndex, ClipStore};
use clip_node::ClipNode;
use gpu_cache::GpuCache;
use gpu_types::TransformPalette;
use internal_types::{FastHashMap, FastHashSet};
use print_tree::{PrintTree, PrintTreePrinter};
use resource_cache::ResourceCache;
use scene::SceneProperties;
use spatial_node::{ScrollFrameInfo, SpatialNode, SpatialNodeType, StickyFrameInfo};
@@ -251,24 +251,23 @@ impl ClipScrollTree {
root_reference_frame_index,
&mut state,
&mut next_coordinate_system_id,
&mut transform_palette,
scene_properties,
);
for clip_node in &mut self.clip_nodes {
- let spatial_node = &self.spatial_nodes[clip_node.spatial_node.0];
clip_node.update(
- spatial_node,
device_pixel_scale,
clip_store,
resource_cache,
gpu_cache,
&mut self.clip_chains,
+ &self.spatial_nodes,
);
}
self.build_clip_chains(screen_rect);
transform_palette
}
fn update_node(
@@ -350,139 +349,108 @@ impl ClipScrollTree {
if let Some((offset, clamping)) = self.pending_scroll_offsets.remove(&external_id) {
node.set_scroll_origin(&offset, clamping);
}
}
}
pub fn add_clip_node(
&mut self,
- index: ClipNodeIndex,
parent_clip_chain_index: ClipChainIndex,
- spatial_node: SpatialNodeIndex,
- handle: ClipSourcesHandle,
- ) -> ClipChainIndex {
+ clip_sources_index: ClipSourcesIndex,
+ ) -> (ClipNodeIndex, ClipChainIndex) {
let clip_chain_index = self.allocate_clip_chain();
let node = ClipNode {
parent_clip_chain_index,
- spatial_node,
- handle: Some(handle),
+ clip_sources_index,
clip_chain_index,
clip_chain_node: None,
};
- self.push_clip_node(node, index);
- clip_chain_index
+ let node_index = self.push_clip_node(node);
+ (node_index, clip_chain_index)
}
pub fn add_scroll_frame(
&mut self,
- index: SpatialNodeIndex,
parent_index: SpatialNodeIndex,
external_id: Option<ExternalScrollId>,
pipeline_id: PipelineId,
frame_rect: &LayoutRect,
content_size: &LayoutSize,
scroll_sensitivity: ScrollSensitivity,
- ) {
+ ) -> SpatialNodeIndex {
let node = SpatialNode::new_scroll_frame(
pipeline_id,
parent_index,
external_id,
frame_rect,
content_size,
scroll_sensitivity,
);
- self.add_spatial_node(node, index);
+ self.add_spatial_node(node)
}
pub fn add_reference_frame(
&mut self,
- index: SpatialNodeIndex,
parent_index: Option<SpatialNodeIndex>,
source_transform: Option<PropertyBinding<LayoutTransform>>,
source_perspective: Option<LayoutTransform>,
origin_in_parent_reference_frame: LayoutVector2D,
pipeline_id: PipelineId,
- ) {
+ ) -> SpatialNodeIndex {
let node = SpatialNode::new_reference_frame(
parent_index,
source_transform,
source_perspective,
origin_in_parent_reference_frame,
pipeline_id,
);
- self.add_spatial_node(node, index);
+ self.add_spatial_node(node)
}
pub fn add_sticky_frame(
&mut self,
- index: SpatialNodeIndex,
parent_index: SpatialNodeIndex,
sticky_frame_info: StickyFrameInfo,
pipeline_id: PipelineId,
- ) {
+ ) -> SpatialNodeIndex {
let node = SpatialNode::new_sticky_frame(
parent_index,
sticky_frame_info,
pipeline_id,
);
- self.add_spatial_node(node, index);
+ self.add_spatial_node(node)
}
pub fn add_clip_chain_descriptor(
&mut self,
parent: Option<ClipChainIndex>,
clips: Vec<ClipNodeIndex>
) -> ClipChainIndex {
let index = self.allocate_clip_chain();
self.clip_chains_descriptors.push(ClipChainDescriptor { index, parent, clips });
index
}
- pub fn push_clip_node(&mut self, node: ClipNode, index: ClipNodeIndex) {
- if index.0 == self.clip_nodes.len() {
- self.clip_nodes.push(node);
- return;
- }
-
- if let Some(empty_node) = self.clip_nodes.get_mut(index.0) {
- *empty_node = node;
- return
- }
-
- let length_to_reserve = index.0 + 1 - self.clip_nodes.len();
- self.clip_nodes.reserve_exact(length_to_reserve);
-
- // We would like to use `Vec::resize` here, but the Clone trait is not supported
- // for ClipNodes. We can fix this either when support is added for something like
- // `Vec::resize_default`.
- let length_to_extend = self.clip_nodes.len() .. index.0;
- self.clip_nodes.extend(length_to_extend.map(|_| ClipNode::empty()));
+ pub fn push_clip_node(&mut self, node: ClipNode) -> ClipNodeIndex {
+ let index = ClipNodeIndex(self.clip_nodes.len());
self.clip_nodes.push(node);
+ index
}
- pub fn add_spatial_node(&mut self, node: SpatialNode, index: SpatialNodeIndex) {
+ pub fn add_spatial_node(&mut self, node: SpatialNode) -> SpatialNodeIndex {
+ let index = SpatialNodeIndex(self.spatial_nodes.len());
+
// When the parent node is None this means we are adding the root.
if let Some(parent_index) = node.parent {
self.spatial_nodes[parent_index.0].add_child(index);
}
- if index.0 == self.spatial_nodes.len() {
- self.spatial_nodes.push(node);
- return;
- }
-
- if let Some(empty_node) = self.spatial_nodes.get_mut(index.0) {
- *empty_node = node;
- return
- }
-
- debug_assert!(index.0 > self.spatial_nodes.len() - 1);
- self.spatial_nodes.resize(index.0, SpatialNode::empty());
self.spatial_nodes.push(node);
+ index
}
pub fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
self.pipelines_to_discard.insert(pipeline_id);
}
fn print_node<T: PrintTreePrinter>(
&self,
@@ -503,17 +471,16 @@ impl ClipScrollTree {
pt.add_item(format!("viewport: {:?}", scrolling_info.viewport_rect));
pt.add_item(format!("scrollable_size: {:?}", scrolling_info.scrollable_size));
pt.add_item(format!("scroll offset: {:?}", scrolling_info.offset));
}
SpatialNodeType::ReferenceFrame(ref info) => {
pt.new_level(format!("ReferenceFrame {:?}", info.resolved_transform));
pt.add_item(format!("index: {:?}", index));
}
- SpatialNodeType::Empty => unreachable!("Empty node remaining in ClipScrollTree."),
}
pt.add_item(format!("world_viewport_transform: {:?}", node.world_viewport_transform));
pt.add_item(format!("world_content_transform: {:?}", node.world_content_transform));
pt.add_item(format!("coordinate_system_id: {:?}", node.coordinate_system_id));
for child_index in &node.children {
self.print_node(*child_index, pt, clip_store);
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -8,17 +8,17 @@ use api::{ClipId, ColorF, ComplexClipReg
use api::{DevicePixelScale, DeviceUintRect, DisplayItemRef, ExtendMode, ExternalScrollId};
use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, GlyphRasterSpace, GradientStop};
use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, LayoutPoint};
use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
use api::{LineOrientation, LineStyle, LocalClip, NinePatchBorderSource, PipelineId};
use api::{PropertyBinding, ReferenceFrame, RepeatMode, ScrollFrameDisplayItem, ScrollSensitivity};
use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect};
use api::{TransformStyle, YuvColorSpace, YuvData};
-use clip::{ClipRegion, ClipSource, ClipSources, ClipStore};
+use clip::{ClipRegion, ClipSource, ClipSources, ClipSourcesIndex, ClipStore};
use clip_scroll_tree::{ClipChainIndex, ClipNodeIndex, ClipScrollTree, SpatialNodeIndex};
use euclid::vec2;
use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig};
use glyph_rasterizer::FontInstance;
use gpu_cache::GpuCacheHandle;
use gpu_types::BrushFlags;
use hit_test::{HitTestingItem, HitTestingRun};
use image::simplify_repeated_primitive;
@@ -28,137 +28,86 @@ use prim_store::{BrushClipMaskKind, Brus
use prim_store::{EdgeAaSegmentMask, ImageSource};
use prim_store::{BorderSource, BrushSegment, PictureIndex, PrimitiveContainer, PrimitiveIndex, PrimitiveStore};
use prim_store::{OpacityBinding, ScrollNodeAndClipChain, TextRunPrimitiveCpu};
use render_backend::{DocumentView};
use resource_cache::{FontInstanceMap, ImageRequest};
use scene::{Scene, ScenePipeline, StackingContextHelpers};
use scene_builder::{BuiltScene, SceneRequest};
use spatial_node::{SpatialNodeType, StickyFrameInfo};
-use std::{f32, mem, usize};
+use std::{f32, mem};
use tiling::{CompositeOps, ScrollbarPrimitive};
use util::{MaxRect, RectHelpers, recycle_vec};
static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF {
r: 0.3,
g: 0.3,
b: 0.3,
a: 0.6,
};
-#[derive(Clone, Copy)]
-pub struct PipelineOffset {
- pipeline: PipelineId,
- spatial_offset: usize,
- clip_offset: usize,
-}
-
/// A data structure that keeps track of mapping between API ClipIds and the indices used
/// internally in the ClipScrollTree to avoid having to do HashMap lookups. ClipIdToIndexMapper is
-/// responsible for mapping both ClipId to ClipChainIndex and ClipId to SpatialNodeIndex. We
-/// also include two small LRU caches. Currently the caches are small (1 entry), but in the future
-/// we could use uluru here to do something more involved.
+/// responsible for mapping both ClipId to ClipChainIndex and ClipId to SpatialNodeIndex.
#[derive(Default)]
pub struct ClipIdToIndexMapper {
- /// A map which converts a ClipId for a clipping node or an API-defined ClipChain into
- /// a ClipChainIndex, which is the index used internally in the ClipScrollTree to
- /// identify ClipChains.
clip_chain_map: FastHashMap<ClipId, ClipChainIndex>,
-
- /// The last mapped ClipChainIndex, used to avoid having to do lots of consecutive
- /// HashMap lookups.
- cached_clip_chain_index: Option<(ClipId, ClipChainIndex)>,
-
- /// The offset in the ClipScrollTree's array of SpatialNodes and ClipNodes for a particular
- /// pipeline. This is used to convert ClipIds into SpatialNodeIndex or ClipNodeIndex.
- pipeline_offsets: FastHashMap<PipelineId, PipelineOffset>,
-
- /// The last mapped pipeline offset for this mapper. This is used to avoid having to
- /// consult `pipeline_offsets` repeatedly when flattening the display list.
- cached_pipeline_offset: Option<PipelineOffset>,
-
- /// The next available pipeline offset for ClipNodeIndex. When we encounter a pipeline
- /// we will use this value and increment it by the total number of clip nodes in the
- /// pipeline's display list.
- next_available_clip_offset: usize,
-
- /// The next available pipeline offset for SpatialNodeIndex. When we encounter a pipeline
- /// we will use this value and increment it by the total number of spatial nodes in the
- /// pipeline's display list.
- next_available_spatial_offset: usize,
+ clip_node_map: FastHashMap<ClipId, ClipNodeIndex>,
+ spatial_node_map: FastHashMap<ClipId, SpatialNodeIndex>,
}
impl ClipIdToIndexMapper {
pub fn add_clip_chain(&mut self, id: ClipId, index: ClipChainIndex) {
- debug_assert!(!self.clip_chain_map.contains_key(&id));
- self.clip_chain_map.insert(id, index);
+ let _old_value = self.clip_chain_map.insert(id, index);
+ debug_assert!(_old_value.is_none());
}
pub fn map_to_parent_clip_chain(&mut self, id: ClipId, parent_id: &ClipId) {
let parent_chain_index = self.get_clip_chain_index(parent_id);
self.add_clip_chain(id, parent_chain_index);
}
- pub fn get_clip_chain_index(&mut self, id: &ClipId) -> ClipChainIndex {
- match self.cached_clip_chain_index {
- Some((cached_id, cached_clip_chain_index)) if cached_id == *id =>
- return cached_clip_chain_index,
- _ => {}
- }
+ pub fn map_spatial_node(&mut self, id: ClipId, index: SpatialNodeIndex) {
+ let _old_value = self.spatial_node_map.insert(id, index);
+ debug_assert!(_old_value.is_none());
+ }
+ pub fn map_clip_node(&mut self, id: ClipId, index: ClipNodeIndex) {
+ let _old_value = self.clip_node_map.insert(id, index);
+ debug_assert!(_old_value.is_none());
+ }
+
+ pub fn get_clip_chain_index(&self, id: &ClipId) -> ClipChainIndex {
self.clip_chain_map[id]
}
- pub fn get_clip_chain_index_and_cache_result(&mut self, id: &ClipId) -> ClipChainIndex {
- let index = self.get_clip_chain_index(id);
- self.cached_clip_chain_index = Some((*id, index));
- index
- }
-
- pub fn initialize_for_pipeline(&mut self, pipeline: &ScenePipeline) {
- debug_assert!(!self.pipeline_offsets.contains_key(&pipeline.pipeline_id));
- self.pipeline_offsets.insert(
- pipeline.pipeline_id,
- PipelineOffset {
- pipeline: pipeline.pipeline_id,
- spatial_offset: self.next_available_spatial_offset,
- clip_offset: self.next_available_clip_offset,
- }
- );
-
- self.next_available_clip_offset += pipeline.display_list.total_clip_nodes();
- self.next_available_spatial_offset += pipeline.display_list.total_spatial_nodes();
- }
-
- pub fn get_pipeline_offet<'a>(&'a mut self, id: PipelineId) -> &'a PipelineOffset {
- match self.cached_pipeline_offset {
- Some(ref offset) if offset.pipeline == id => offset,
- _ => {
- let offset = &self.pipeline_offsets[&id];
- self.cached_pipeline_offset = Some(*offset);
- offset
- }
- }
- }
-
- pub fn get_clip_node_index(&mut self, id: ClipId) -> ClipNodeIndex {
+ pub fn get_clip_node_index(&self, id: ClipId) -> ClipNodeIndex {
match id {
- ClipId::Clip(index, pipeline_id) => {
- let pipeline_offset = self.get_pipeline_offet(pipeline_id);
- ClipNodeIndex(pipeline_offset.clip_offset + index)
+ ClipId::Clip(..) => {
+ self.clip_node_map[&id]
}
ClipId::Spatial(..) => {
// We could theoretically map back to the containing clip node with the current
// design, but we will eventually fully separate out clipping from spatial nodes
// in the display list. We don't ever need to do this anyway.
panic!("Tried to use positioning node as clip node.");
}
ClipId::ClipChain(_) => panic!("Tried to use ClipChain as scroll node."),
}
}
+
+ pub fn get_spatial_node_index(&self, id: ClipId) -> SpatialNodeIndex {
+ match id {
+ ClipId::Clip(..) |
+ ClipId::Spatial(..) => {
+ self.spatial_node_map[&id]
+ }
+ ClipId::ClipChain(_) => panic!("Tried to use ClipChain as scroll node."),
+ }
+ }
}
/// A structure that converts a serialized display list into a form that WebRender
/// can use to later build a frame. This structure produces a FrameBuilder. Public
/// members are typically those that are destructured into the FrameBuilder.
pub struct DisplayListFlattener<'a> {
/// The scene that we are currently flattening.
scene: &'a Scene,
@@ -243,17 +192,16 @@ impl<'a> DisplayListFlattener<'a> {
picture_stack: Vec::new(),
shadow_stack: Vec::new(),
sc_stack: Vec::new(),
pipeline_clip_chain_stack: Vec::new(),
prim_store: old_builder.prim_store.recycle(),
clip_store: old_builder.clip_store.recycle(),
};
- flattener.id_to_index_mapper.initialize_for_pipeline(root_pipeline);
flattener.push_root(
root_pipeline_id,
&root_pipeline.viewport_size,
&root_pipeline.content_size,
);
flattener.setup_viewport_offset(view.inner_rect, view.accumulated_scale_factor());
flattener.flatten_root(root_pipeline, &root_pipeline.viewport_size);
@@ -396,23 +344,22 @@ impl<'a> DisplayListFlattener<'a> {
let sticky_frame_info = StickyFrameInfo::new(
frame_rect,
info.margins,
info.vertical_offset_bounds,
info.horizontal_offset_bounds,
info.previously_applied_offset,
);
- let index = self.get_spatial_node_index_for_clip_id(info.id);
- self.clip_scroll_tree.add_sticky_frame(
- index,
+ let index = self.clip_scroll_tree.add_sticky_frame(
clip_and_scroll.spatial_node_index, /* parent id */
sticky_frame_info,
info.id.pipeline_id(),
);
+ self.id_to_index_mapper.map_spatial_node(info.id, index);
self.id_to_index_mapper.map_to_parent_clip_chain(info.id, parent_id);
}
fn flatten_scroll_frame(
&mut self,
item: &DisplayItemRef,
info: &ScrollFrameDisplayItem,
pipeline_id: PipelineId,
@@ -527,18 +474,16 @@ impl<'a> DisplayListFlattener<'a> {
let pipeline = match self.scene.pipelines.get(&iframe_pipeline_id) {
Some(pipeline) => pipeline,
None => {
debug_assert!(info.ignore_missing_pipeline);
return
},
};
- self.id_to_index_mapper.initialize_for_pipeline(pipeline);
-
//TODO: use or assert on `clip_and_scroll_ids.clip_node_id` ?
let clip_chain_index = self.add_clip_node(
info.clip_id,
clip_and_scroll_ids.scroll_node_id,
ClipRegion::create_for_clip_node_with_local_clip(
&LocalClip::from(*item.clip_rect()),
reference_frame_relative_offset
),
@@ -786,33 +731,39 @@ impl<'a> DisplayListFlattener<'a> {
}
SpecificDisplayItem::PopAllShadows => {
self.pop_all_shadows();
}
}
None
}
+ fn add_clip_sources(
+ &mut self,
+ clip_sources: Vec<ClipSource>,
+ spatial_node_index: SpatialNodeIndex,
+ ) -> Option<ClipSourcesIndex> {
+ if clip_sources.is_empty() {
+ None
+ } else {
+ Some(self.clip_store.insert(ClipSources::new(clip_sources, spatial_node_index)))
+ }
+ }
+
/// Create a primitive and add it to the prim store. This method doesn't
/// add the primitive to the draw list, so can be used for creating
/// sub-primitives.
pub fn create_primitive(
&mut self,
info: &LayoutPrimitiveInfo,
- clip_sources: Vec<ClipSource>,
+ clip_sources: Option<ClipSourcesIndex>,
container: PrimitiveContainer,
) -> PrimitiveIndex {
let stacking_context = self.sc_stack.last().expect("bug: no stacking context!");
- let clip_sources = if clip_sources.is_empty() {
- None
- } else {
- Some(self.clip_store.insert(ClipSources::new(clip_sources)))
- };
-
self.prim_store.add_primitive(
&info.rect,
&info.clip_rect,
info.is_backface_visible && stacking_context.is_backface_visible,
clip_sources,
info.tag,
container,
)
@@ -872,32 +823,40 @@ impl<'a> DisplayListFlattener<'a> {
info.rect = info.rect.translate(&shadow.offset);
info.clip_rect = info.clip_rect.translate(&shadow.offset);
// Offset any local clip sources by the shadow offset.
let clip_sources: Vec<ClipSource> = clip_sources
.iter()
.map(|cs| cs.offset(&shadow.offset))
.collect();
+ let clip_sources = self.add_clip_sources(
+ clip_sources,
+ clip_and_scroll.spatial_node_index,
+ );
// Construct and add a primitive for the given shadow.
let shadow_prim_index = self.create_primitive(
&info,
clip_sources,
container.create_shadow(shadow),
);
// Add the new primitive to the shadow picture.
let shadow_pic = &mut self.prim_store.pictures[shadow_pic_index.0];
shadow_pic.add_primitive(shadow_prim_index, clip_and_scroll);
}
self.shadow_stack = shadow_stack;
}
if container.is_visible() {
+ let clip_sources = self.add_clip_sources(
+ clip_sources,
+ clip_and_scroll.spatial_node_index,
+ );
let prim_index = self.create_primitive(info, clip_sources, container);
if cfg!(debug_assertions) && ChasePrimitive::LocalRect(info.rect) == self.config.chase_primitive {
println!("Chasing {:?}", prim_index);
self.prim_store.chase_id = Some(prim_index);
}
self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
}
@@ -914,17 +873,17 @@ impl<'a> DisplayListFlattener<'a> {
clipping_node: Option<ClipId>,
glyph_raster_space: GlyphRasterSpace,
) {
let clip_chain_id = match clipping_node {
Some(ref clipping_node) => self.id_to_index_mapper.get_clip_chain_index(clipping_node),
None => ClipChainIndex::NO_CLIP,
};
let clip_and_scroll = ScrollNodeAndClipChain::new(
- self.get_spatial_node_index_for_clip_id(spatial_node),
+ self.id_to_index_mapper.get_spatial_node_index(spatial_node),
clip_chain_id
);
// Construct the necessary set of Picture primitives
// to draw this stacking context.
let current_reference_frame_index = self.current_reference_frame_index();
// An arbitrary large clip rect. For now, we don't
@@ -1214,27 +1173,26 @@ impl<'a> DisplayListFlattener<'a> {
&mut self,
reference_frame_id: ClipId,
parent_id: Option<ClipId>,
pipeline_id: PipelineId,
source_transform: Option<PropertyBinding<LayoutTransform>>,
source_perspective: Option<LayoutTransform>,
origin_in_parent_reference_frame: LayoutVector2D,
) -> SpatialNodeIndex {
- let index = self.get_spatial_node_index_for_clip_id(reference_frame_id);
- let parent_index = parent_id.map(|id| self.get_spatial_node_index_for_clip_id(id));
- self.clip_scroll_tree.add_reference_frame(
- index,
+ let parent_index = parent_id.map(|id| self.id_to_index_mapper.get_spatial_node_index(id));
+ let index = self.clip_scroll_tree.add_reference_frame(
parent_index,
source_transform,
source_perspective,
origin_in_parent_reference_frame,
pipeline_id,
);
self.reference_frame_stack.push((reference_frame_id, index));
+ self.id_to_index_mapper.map_spatial_node(reference_frame_id, index);
match parent_id {
Some(ref parent_id) =>
self.id_to_index_mapper.map_to_parent_clip_chain(reference_frame_id, parent_id),
_ => self.id_to_index_mapper.add_clip_chain(reference_frame_id, ClipChainIndex::NO_CLIP),
}
index
}
@@ -1284,54 +1242,52 @@ impl<'a> DisplayListFlattener<'a> {
}
pub fn add_clip_node(
&mut self,
new_node_id: ClipId,
parent_id: ClipId,
clip_region: ClipRegion,
) -> ClipChainIndex {
- let clip_sources = ClipSources::from(clip_region);
+ let parent_clip_chain_index = self.id_to_index_mapper.get_clip_chain_index(&parent_id);
+ let spatial_node = self.id_to_index_mapper.get_spatial_node_index(parent_id);
+
+ let clip_sources = ClipSources::from_region(clip_region, spatial_node);
let handle = self.clip_store.insert(clip_sources);
- let node_index = self.id_to_index_mapper.get_clip_node_index(new_node_id);
- let parent_clip_chain_index =
- self.id_to_index_mapper.get_clip_chain_index_and_cache_result(&parent_id);
- let spatial_node = self.get_spatial_node_index_for_clip_id(parent_id);
- let clip_chain_index = self.clip_scroll_tree.add_clip_node(
- node_index,
+ let (node_index, clip_chain_index) = self.clip_scroll_tree.add_clip_node(
parent_clip_chain_index,
- spatial_node,
handle,
);
+ self.id_to_index_mapper.map_spatial_node(new_node_id, spatial_node);
+ self.id_to_index_mapper.map_clip_node(new_node_id, node_index);
self.id_to_index_mapper.add_clip_chain(new_node_id, clip_chain_index);
clip_chain_index
}
pub fn add_scroll_frame(
&mut self,
new_node_id: ClipId,
parent_id: ClipId,
external_id: Option<ExternalScrollId>,
pipeline_id: PipelineId,
frame_rect: &LayoutRect,
content_size: &LayoutSize,
scroll_sensitivity: ScrollSensitivity,
) -> SpatialNodeIndex {
- let node_index = self.get_spatial_node_index_for_clip_id(new_node_id);
- let parent_node_index = self.get_spatial_node_index_for_clip_id(parent_id);
- self.clip_scroll_tree.add_scroll_frame(
- node_index,
+ let parent_node_index = self.id_to_index_mapper.get_spatial_node_index(parent_id);
+ let node_index = self.clip_scroll_tree.add_scroll_frame(
parent_node_index,
external_id,
pipeline_id,
frame_rect,
content_size,
scroll_sensitivity,
);
+ self.id_to_index_mapper.map_spatial_node(new_node_id, node_index);
self.id_to_index_mapper.map_to_parent_clip_chain(new_node_id, &parent_id);
node_index
}
pub fn pop_reference_frame(&mut self) {
self.reference_frame_stack.pop();
}
@@ -1450,17 +1406,17 @@ impl<'a> DisplayListFlattener<'a> {
let prim = BrushPrimitive::new(
BrushKind::new_solid(color),
None,
);
let prim_index = self.create_primitive(
info,
- Vec::new(),
+ None,
PrimitiveContainer::Brush(prim),
);
self.add_primitive_to_draw_list(
prim_index,
clip_and_scroll,
);
@@ -1962,38 +1918,24 @@ impl<'a> DisplayListFlattener<'a> {
info,
Vec::new(),
PrimitiveContainer::Brush(prim),
);
}
pub fn map_clip_and_scroll(&mut self, info: &ClipAndScrollInfo) -> ScrollNodeAndClipChain {
ScrollNodeAndClipChain::new(
- self.get_spatial_node_index_for_clip_id(info.scroll_node_id),
- self.id_to_index_mapper.get_clip_chain_index_and_cache_result(&info.clip_node_id())
+ self.id_to_index_mapper.get_spatial_node_index(info.scroll_node_id),
+ self.id_to_index_mapper.get_clip_chain_index(&info.clip_node_id())
)
}
pub fn simple_scroll_and_clip_chain(&mut self, id: &ClipId) -> ScrollNodeAndClipChain {
self.map_clip_and_scroll(&ClipAndScrollInfo::simple(*id))
}
-
- pub fn get_spatial_node_index_for_clip_id(&mut self, id: ClipId,) -> SpatialNodeIndex {
- match id {
- ClipId::Spatial(index, pipeline_id) => {
- let pipeline_offset = self.id_to_index_mapper.get_pipeline_offet(pipeline_id);
- SpatialNodeIndex(pipeline_offset.spatial_offset + index)
- }
- ClipId::Clip(..) => {
- let clip_node_index = self.id_to_index_mapper.get_clip_node_index(id);
- self.clip_scroll_tree.clip_nodes[clip_node_index.0].spatial_node
- }
- ClipId::ClipChain(_) => panic!("Tried to use ClipChain as scroll node."),
- }
- }
}
pub fn build_scene(config: &FrameBuilderConfig, request: SceneRequest) -> BuiltScene {
let mut clip_scroll_tree = ClipScrollTree::new();
let mut new_scene = Scene::new();
let frame_builder = DisplayListFlattener::create_frame_builder(
--- a/gfx/webrender/src/freelist.rs
+++ b/gfx/webrender/src/freelist.rs
@@ -1,14 +1,13 @@
/* 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 std::marker::PhantomData;
-use util::recycle_vec;
// TODO(gw): Add an occupied list head, for fast
// iteration of the occupied list to implement
// retain() style functionality.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
@@ -81,25 +80,16 @@ impl<T, M> FreeList<T, M> {
FreeList {
slots: Vec::new(),
free_list_head: None,
active_count: 0,
_marker: PhantomData,
}
}
- pub fn recycle(self) -> FreeList<T, M> {
- FreeList {
- slots: recycle_vec(self.slots),
- free_list_head: None,
- active_count: 0,
- _marker: PhantomData,
- }
- }
-
pub fn clear(&mut self) {
self.slots.clear();
self.free_list_head = None;
self.active_count = 0;
}
#[allow(dead_code)]
pub fn get(&self, id: &FreeListHandle<M>) -> &T {
--- a/gfx/webrender/src/gpu_types.rs
+++ b/gfx/webrender/src/gpu_types.rs
@@ -190,16 +190,17 @@ impl PrimitiveHeaders {
});
PrimitiveHeaderIndex(id as i32)
}
}
// This is a convenience type used to make it easier to pass
// the common parts around during batching.
+#[derive(Debug)]
pub struct PrimitiveHeader {
pub local_rect: LayoutRect,
pub local_clip_rect: LayoutRect,
pub task_address: RenderTaskAddress,
pub specific_prim_address: GpuCacheAddress,
pub clip_task_address: RenderTaskAddress,
pub transform_id: TransformPaletteId,
}
@@ -344,22 +345,25 @@ impl From<BrushInstance> for PrimitiveIn
// pixel snapping applied).
#[derive(Copy, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[repr(C)]
pub struct TransformPaletteId(pub u32);
impl TransformPaletteId {
- // Get the palette ID for an identity transform.
- pub fn identity() -> TransformPaletteId {
- TransformPaletteId(0)
+ /// Identity transform ID.
+ pub const IDENTITY: Self = TransformPaletteId(0);
+
+ /// Extract the spatial node index from the id.
+ pub fn _spatial_node_index(&self) -> SpatialNodeIndex {
+ SpatialNodeIndex(self.0 as usize & 0xFFFFFF)
}
- // Extract the transform kind from the id.
+ /// Extract the transform kind from the id.
pub fn transform_kind(&self) -> TransformedRectKind {
if (self.0 >> 24) == 0 {
TransformedRectKind::AxisAligned
} else {
TransformedRectKind::Complex
}
}
}
--- a/gfx/webrender/src/hit_test.rs
+++ b/gfx/webrender/src/hit_test.rs
@@ -29,16 +29,39 @@ pub struct HitTestClipNode {
/// The positioning node for this clip node.
spatial_node: SpatialNodeIndex,
/// A particular point must be inside all of these regions to be considered clipped in
/// for the purposes of a hit test.
regions: Vec<HitTestRegion>,
}
+impl HitTestClipNode {
+ fn new(node: &ClipNode, clip_store: &ClipStore) -> Self {
+ let clips = clip_store.get(node.clip_sources_index);
+ let regions = clips.clips().iter().map(|source| {
+ match source.0 {
+ ClipSource::Rectangle(ref rect, mode) => HitTestRegion::Rectangle(*rect, mode),
+ ClipSource::RoundedRectangle(ref rect, ref radii, ref mode) =>
+ HitTestRegion::RoundedRectangle(*rect, *radii, *mode),
+ ClipSource::Image(ref mask) => HitTestRegion::Rectangle(mask.rect, ClipMode::Clip),
+ ClipSource::LineDecoration(_) |
+ ClipSource::BoxShadow(_) => {
+ unreachable!("Didn't expect to hit test against BorderCorner / BoxShadow / LineDecoration");
+ }
+ }
+ }).collect();
+
+ HitTestClipNode {
+ spatial_node: clips.spatial_node_index,
+ regions,
+ }
+ }
+}
+
/// A description of a clip chain in the HitTester. This is used to describe
/// hierarchical clip scroll nodes as well as ClipChains, so that they can be
/// handled the same way during hit testing. Once we represent all ClipChains
/// using ClipChainDescriptors, we can get rid of this and just use the
/// ClipChainDescriptor here.
#[derive(Clone)]
struct HitTestClipChainDescriptor {
parent: Option<ClipChainIndex>,
@@ -143,25 +166,21 @@ impl HitTester {
self.spatial_nodes.push(HitTestSpatialNode {
pipeline_id: node.pipeline_id,
world_content_transform: node.world_content_transform,
world_viewport_transform: node.world_viewport_transform,
});
}
for (index, node) in clip_scroll_tree.clip_nodes.iter().enumerate() {
- self.clip_nodes.push(HitTestClipNode {
- spatial_node: node.spatial_node,
- regions: get_regions_for_clip_node(node, clip_store),
- });
-
- let clip_chain = self.clip_chains.get_mut(node.clip_chain_index.0).unwrap();
- clip_chain.parent =
- clip_scroll_tree.get_clip_chain(node.clip_chain_index).parent_index;
- clip_chain.clips = vec![ClipNodeIndex(index)];
+ self.clip_nodes.push(HitTestClipNode::new(node, clip_store));
+ let clip_chain = self.clip_chains.get_mut(node.clip_chain_index.0).unwrap();
+ clip_chain.parent =
+ clip_scroll_tree.get_clip_chain(node.clip_chain_index).parent_index;
+ clip_chain.clips = vec![ClipNodeIndex(index)];
}
for descriptor in &clip_scroll_tree.clip_chains_descriptors {
let clip_chain = self.clip_chains.get_mut(descriptor.index.0).unwrap();
clip_chain.parent = clip_scroll_tree.get_clip_chain(descriptor.index).parent_index;
clip_chain.clips = descriptor.clips.clone();
}
}
@@ -341,43 +360,16 @@ impl HitTester {
result
}
pub fn get_pipeline_root(&self, pipeline_id: PipelineId) -> &HitTestSpatialNode {
&self.spatial_nodes[self.pipeline_root_nodes[&pipeline_id].0]
}
}
-fn get_regions_for_clip_node(
- node: &ClipNode,
- clip_store: &ClipStore
-) -> Vec<HitTestRegion> {
- let handle = match node.handle.as_ref() {
- Some(handle) => handle,
- None => {
- warn!("Encountered an empty clip node unexpectedly.");
- return Vec::new()
- }
- };
-
- let clips = clip_store.get(handle).clips();
- clips.iter().map(|source| {
- match source.0 {
- ClipSource::Rectangle(ref rect, mode) => HitTestRegion::Rectangle(*rect, mode),
- ClipSource::RoundedRectangle(ref rect, ref radii, ref mode) =>
- HitTestRegion::RoundedRectangle(*rect, *radii, *mode),
- ClipSource::Image(ref mask) => HitTestRegion::Rectangle(mask.rect, ClipMode::Clip),
- ClipSource::LineDecoration(_) |
- ClipSource::BoxShadow(_) => {
- unreachable!("Didn't expect to hit test against BorderCorner / BoxShadow / LineDecoration");
- }
- }
- }).collect()
-}
-
#[derive(Clone, Copy, PartialEq)]
enum ClippedIn {
ClippedIn,
NotClippedIn,
}
pub struct HitTest {
pipeline_id: Option<PipelineId>,
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -8,17 +8,17 @@ use api::{FilterOp, GlyphInstance, Gradi
use api::{GlyphRasterSpace, LayoutPoint, LayoutRect, LayoutSize, LayoutToWorldTransform, LayoutVector2D};
use api::{PipelineId, PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat, DeviceIntSideOffsets};
use api::{BorderWidths, LayoutToWorldScale, NormalBorder};
use app_units::Au;
use border::{BorderCacheKey, BorderRenderTaskInfo};
use box_shadow::BLUR_SAMPLE_SCALE;
use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId, SpatialNodeIndex};
use clip::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipSource};
-use clip::{ClipSourcesHandle, ClipWorkItem};
+use clip::{ClipSourcesIndex, ClipWorkItem};
use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
use frame_builder::PrimitiveRunContext;
use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT};
use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
ToGpuBlocks};
use gpu_types::BrushFlags;
use image::{for_each_tile, for_each_repetition};
use picture::{PictureCompositeMode, PictureId, PicturePrimitive};
@@ -183,17 +183,17 @@ pub struct ScreenRect {
pub clipped: DeviceIntRect,
pub unclipped: DeviceIntRect,
}
// TODO(gw): Pack the fields here better!
#[derive(Debug)]
pub struct PrimitiveMetadata {
pub opacity: PrimitiveOpacity,
- pub clip_sources: Option<ClipSourcesHandle>,
+ pub clip_sources_index: Option<ClipSourcesIndex>,
pub prim_kind: PrimitiveKind,
pub cpu_prim_index: SpecificPrimitiveIndex,
pub gpu_location: GpuCacheHandle,
pub clip_task_id: Option<RenderTaskId>,
// TODO(gw): In the future, we should just pull these
// directly from the DL item, instead of
// storing them here.
@@ -1276,24 +1276,24 @@ impl PrimitiveStore {
picture_index
}
pub fn add_primitive(
&mut self,
local_rect: &LayoutRect,
local_clip_rect: &LayoutRect,
is_backface_visible: bool,
- clip_sources: Option<ClipSourcesHandle>,
+ clip_sources_index: Option<ClipSourcesIndex>,
tag: Option<ItemTag>,
container: PrimitiveContainer,
) -> PrimitiveIndex {
let prim_index = self.cpu_metadata.len();
let base_metadata = PrimitiveMetadata {
- clip_sources,
+ clip_sources_index,
gpu_location: GpuCacheHandle::new(),
clip_task_id: None,
local_rect: *local_rect,
local_clip_rect: *local_clip_rect,
combined_local_clip_rect: *local_clip_rect,
is_backface_visible,
screen_rect: None,
tag,
@@ -1978,16 +1978,19 @@ impl PrimitiveStore {
}
PrimitiveKind::Brush => {
let brush = &self.cpu_brushes[metadata.cpu_prim_index.0];
brush.write_gpu_blocks(&mut request, metadata.local_rect);
match brush.segment_desc {
Some(ref segment_desc) => {
for segment in &segment_desc.segments {
+ if cfg!(debug_assertions) && self.chase_id == Some(prim_index) {
+ println!("\t\t{:?}", segment);
+ }
// has to match VECS_PER_SEGMENT
request.write_segment(
segment.local_rect,
segment.extra_data,
);
}
}
None => {
@@ -2054,32 +2057,32 @@ impl PrimitiveStore {
};
// Segment the primitive on all the local-space clip sources that we can.
for clip_item in clips {
if clip_item.coordinate_system_id != prim_run_context.scroll_node.coordinate_system_id {
continue;
}
- let local_clips = frame_state.clip_store.get_opt(&clip_item.clip_sources).expect("bug");
+ let local_clips = frame_state.clip_store.get(clip_item.clip_sources_index);
rect_clips_only = rect_clips_only && local_clips.only_rectangular_clips;
// TODO(gw): We can easily extend the segment builder to support these clip sources in
// the future, but they are rarely used.
// We must do this check here in case we continue early below.
if local_clips.has_image_or_line_decoration_clip {
clip_mask_kind = BrushClipMaskKind::Global;
}
// If this clip item is positioned by another positioning node, its relative position
// could change during scrolling. This means that we would need to resegment. Instead
// of doing that, only segment with clips that have the same positioning node.
// TODO(mrobinson, #2858): It may make sense to include these nodes, resegmenting only
// when necessary while scrolling.
- if clip_item.spatial_node_index != prim_run_context.spatial_node_index {
+ if local_clips.spatial_node_index != prim_run_context.spatial_node_index {
// We don't need to generate a global clip mask for rectangle clips because we are
// in the same coordinate system and rectangular clips are handled by the local
// clip chain rectangle.
if !local_clips.only_rectangular_clips {
clip_mask_kind = BrushClipMaskKind::Global;
}
continue;
}
@@ -2280,18 +2283,18 @@ impl PrimitiveStore {
println!("\tbase screen {:?}, combined clip chain {:?}",
prim_screen_rect, prim_run_context.clip_chain.combined_outer_screen_rect);
}
let prim_coordinate_system_id = prim_run_context.scroll_node.coordinate_system_id;
let transform = &prim_run_context.scroll_node.world_content_transform;
let extra_clip = {
let metadata = &self.cpu_metadata[prim_index.0];
- metadata.clip_sources.as_ref().map(|clip_sources| {
- let prim_clips = frame_state.clip_store.get_mut(clip_sources);
+ metadata.clip_sources_index.map(|clip_sources_index| {
+ let prim_clips = frame_state.clip_store.get_mut(clip_sources_index);
prim_clips.update(
frame_state.gpu_cache,
frame_state.resource_cache,
frame_context.device_pixel_scale,
);
let (screen_inner_rect, screen_outer_rect) = prim_clips.get_screen_bounds(
transform,
frame_context.device_pixel_scale,
@@ -2302,18 +2305,17 @@ impl PrimitiveStore {
combined_outer_rect = combined_outer_rect.and_then(|r| r.intersection(&outer));
}
if cfg!(debug_assertions) && Some(prim_index) == self.chase_id {
println!("\tfound extra clip with screen bounds {:?}", screen_outer_rect);
}
Arc::new(ClipChainNode {
work_item: ClipWorkItem {
- spatial_node_index: prim_run_context.spatial_node_index,
- clip_sources: clip_sources.weak(),
+ clip_sources_index,
coordinate_system_id: prim_coordinate_system_id,
},
// The local_clip_rect a property of ClipChain nodes that are ClipNodes.
// It's used to calculate a local clipping rectangle before we reach this
// point, so we can set it to zero here. It should be unused from this point
// on.
local_clip_rect: LayoutRect::zero(),
screen_inner_rect,
@@ -2638,29 +2640,32 @@ impl PrimitiveStore {
frame_state: &mut FrameBuildingState,
) -> PrimitiveRunLocalRect {
let mut result = PrimitiveRunLocalRect {
local_rect_in_actual_parent_space: LayoutRect::zero(),
local_rect_in_original_parent_space: LayoutRect::zero(),
};
for run in &pic_context.prim_runs {
- if run.is_chasing(self.chase_id) {
- println!("\tpreparing a run of length {} in pipeline {:?}",
- run.count, pic_context.pipeline_id);
- }
// TODO(gw): Perhaps we can restructure this to not need to create
// a new primitive context for every run (if the hash
// lookups ever show up in a profile).
let scroll_node = &frame_context
.clip_scroll_tree
.spatial_nodes[run.clip_and_scroll.spatial_node_index.0];
let clip_chain = &frame_context
.clip_chains[run.clip_and_scroll.clip_chain_index.0];
+ if run.is_chasing(self.chase_id) {
+ println!("\tpreparing a run of length {} in pipeline {:?}",
+ run.count, pic_context.pipeline_id);
+ println!("\trun {:?}", run.clip_and_scroll);
+ println!("\ttransform {:?}", scroll_node.world_content_transform.to_transform());
+ }
+
// Mark whether this picture contains any complex coordinate
// systems, due to either the scroll node or the clip-chain.
pic_state.has_non_root_coord_system |=
scroll_node.coordinate_system_id != CoordinateSystemId::root();
pic_state.has_non_root_coord_system |= clip_chain.has_non_root_coord_system;
if !scroll_node.invertible {
if run.is_chasing(self.chase_id) {
@@ -2711,17 +2716,22 @@ impl PrimitiveStore {
let clip_chain_rect = if pic_context.apply_local_clip_rect {
get_local_clip_rect_for_nodes(scroll_node, clip_chain)
} else {
None
};
let local_clip_chain_rect = match clip_chain_rect {
- Some(rect) if rect.is_empty() => continue,
+ Some(rect) if rect.is_empty() => {
+ if run.is_chasing(self.chase_id) {
+ println!("\tculled by empty chain rect");
+ }
+ continue
+ },
Some(rect) => rect,
None => frame_context.max_local_clip,
};
let transform = frame_context
.transforms
.get_transform(run.clip_and_scroll.spatial_node_index);
@@ -2744,17 +2754,22 @@ impl PrimitiveStore {
frame_context,
frame_state,
) {
frame_state.profile_counters.visible_primitives.inc();
let clipped_rect = match clip_chain_rect {
Some(ref chain_rect) => match prim_local_rect.intersection(chain_rect) {
Some(rect) => rect,
- None => continue,
+ None => {
+ if cfg!(debug_assertions) && self.chase_id == Some(prim_index) {
+ println!("\tculled by chain rect {:?}", chain_rect);
+ }
+ continue
+ },
},
None => prim_local_rect,
};
if let Some(ref matrix) = parent_relative_transform {
match matrix.transform_rect(&clipped_rect) {
Some(bounds) => {
result.local_rect_in_actual_parent_space =
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -164,16 +164,107 @@ impl Document {
}
fn can_render(&self) -> bool { self.frame_builder.is_some() }
fn has_pixels(&self) -> bool {
!self.view.window_size.is_empty_or_negative()
}
+ fn process_frame_msg(
+ &mut self,
+ message: FrameMsg,
+ ) -> DocumentOps {
+ match message {
+ FrameMsg::UpdateEpoch(pipeline_id, epoch) => {
+ self.current.scene.update_epoch(pipeline_id, epoch);
+
+ DocumentOps::nop()
+ }
+ FrameMsg::EnableFrameOutput(pipeline_id, enable) => {
+ if enable {
+ self.output_pipelines.insert(pipeline_id);
+ } else {
+ self.output_pipelines.remove(&pipeline_id);
+ }
+ DocumentOps::nop()
+ }
+ FrameMsg::Scroll(delta, cursor) => {
+ profile_scope!("Scroll");
+
+ let mut should_render = true;
+ let node_index = match self.hit_tester {
+ Some(ref hit_tester) => {
+ // Ideally we would call self.scroll_nearest_scrolling_ancestor here, but
+ // we need have to avoid a double-borrow.
+ let test = HitTest::new(None, cursor, HitTestFlags::empty());
+ hit_tester.find_node_under_point(test)
+ }
+ None => {
+ should_render = false;
+ None
+ }
+ };
+
+ let should_render =
+ should_render &&
+ self.scroll_nearest_scrolling_ancestor(delta, node_index) &&
+ self.render_on_scroll == Some(true);
+ DocumentOps {
+ scroll: true,
+ render: should_render,
+ composite: should_render,
+ ..DocumentOps::nop()
+ }
+ }
+ FrameMsg::HitTest(pipeline_id, point, flags, tx) => {
+
+ let result = match self.hit_tester {
+ Some(ref hit_tester) => {
+ hit_tester.hit_test(HitTest::new(pipeline_id, point, flags))
+ }
+ None => HitTestResult { items: Vec::new() },
+ };
+
+ tx.send(result).unwrap();
+ DocumentOps::nop()
+ }
+ FrameMsg::SetPan(pan) => {
+ self.view.pan = pan;
+ DocumentOps::nop()
+ }
+ FrameMsg::ScrollNodeWithId(origin, id, clamp) => {
+ profile_scope!("ScrollNodeWithScrollId");
+
+ let should_render = self.scroll_node(origin, id, clamp)
+ && self.render_on_scroll == Some(true);
+
+ DocumentOps {
+ scroll: true,
+ render: should_render,
+ composite: should_render,
+ ..DocumentOps::nop()
+ }
+ }
+ FrameMsg::GetScrollNodeState(tx) => {
+ profile_scope!("GetScrollNodeState");
+ tx.send(self.get_scroll_node_state()).unwrap();
+ DocumentOps::nop()
+ }
+ FrameMsg::UpdateDynamicProperties(property_bindings) => {
+ self.dynamic_properties.set_properties(property_bindings);
+ DocumentOps::nop()
+ }
+ FrameMsg::AppendDynamicProperties(property_bindings) => {
+ self.dynamic_properties.add_properties(property_bindings);
+ DocumentOps::nop()
+ }
+ }
+ }
+
// TODO: We will probably get rid of this soon and always forward to the scene building thread.
fn build_scene(&mut self, resource_cache: &mut ResourceCache, scene_id: u64) {
let max_texture_size = resource_cache.max_texture_size();
if self.view.window_size.width > max_texture_size ||
self.view.window_size.height > max_texture_size {
error!("ERROR: Invalid window dimensions {}x{}. Please call api.set_window_size()",
self.view.window_size.width,
@@ -596,110 +687,16 @@ impl RenderBackend {
doc.pending.scene.remove_pipeline(pipeline_id);
doc.pending.removed_pipelines.push(pipeline_id);
DocumentOps::nop()
}
}
}
- fn process_frame_msg(
- &mut self,
- document_id: DocumentId,
- message: FrameMsg,
- ) -> DocumentOps {
- let doc = self.documents.get_mut(&document_id).expect("No document?");
-
- match message {
- FrameMsg::UpdateEpoch(pipeline_id, epoch) => {
- doc.current.scene.update_epoch(pipeline_id, epoch);
-
- DocumentOps::nop()
- }
- FrameMsg::EnableFrameOutput(pipeline_id, enable) => {
- if enable {
- doc.output_pipelines.insert(pipeline_id);
- } else {
- doc.output_pipelines.remove(&pipeline_id);
- }
- DocumentOps::nop()
- }
- FrameMsg::Scroll(delta, cursor) => {
- profile_scope!("Scroll");
-
- let mut should_render = true;
- let node_index = match doc.hit_tester {
- Some(ref hit_tester) => {
- // Ideally we would call doc.scroll_nearest_scrolling_ancestor here, but
- // we need have to avoid a double-borrow.
- let test = HitTest::new(None, cursor, HitTestFlags::empty());
- hit_tester.find_node_under_point(test)
- }
- None => {
- should_render = false;
- None
- }
- };
-
- let should_render =
- should_render &&
- doc.scroll_nearest_scrolling_ancestor(delta, node_index) &&
- doc.render_on_scroll == Some(true);
- DocumentOps {
- scroll: true,
- render: should_render,
- composite: should_render,
- ..DocumentOps::nop()
- }
- }
- FrameMsg::HitTest(pipeline_id, point, flags, tx) => {
-
- let result = match doc.hit_tester {
- Some(ref hit_tester) => {
- hit_tester.hit_test(HitTest::new(pipeline_id, point, flags))
- }
- None => HitTestResult { items: Vec::new() },
- };
-
- tx.send(result).unwrap();
- DocumentOps::nop()
- }
- FrameMsg::SetPan(pan) => {
- doc.view.pan = pan;
- DocumentOps::nop()
- }
- FrameMsg::ScrollNodeWithId(origin, id, clamp) => {
- profile_scope!("ScrollNodeWithScrollId");
-
- let should_render = doc.scroll_node(origin, id, clamp)
- && doc.render_on_scroll == Some(true);
-
- DocumentOps {
- scroll: true,
- render: should_render,
- composite: should_render,
- ..DocumentOps::nop()
- }
- }
- FrameMsg::GetScrollNodeState(tx) => {
- profile_scope!("GetScrollNodeState");
- tx.send(doc.get_scroll_node_state()).unwrap();
- DocumentOps::nop()
- }
- FrameMsg::UpdateDynamicProperties(property_bindings) => {
- doc.dynamic_properties.set_properties(property_bindings);
- DocumentOps::render()
- }
- FrameMsg::AppendDynamicProperties(property_bindings) => {
- doc.dynamic_properties.add_properties(property_bindings);
- DocumentOps::render()
- }
- }
- }
-
fn next_namespace_id(&self) -> IdNamespace {
IdNamespace(NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed) as u32)
}
pub fn make_unique_scene_id(&mut self) -> u64 {
// 2^64 scenes ought to be enough for anybody!
self.last_scene_id += 1;
self.last_scene_id
@@ -1065,22 +1062,26 @@ impl RenderBackend {
// before rendering. This is useful for rendering with the latest
// async transforms.
if op.render || transaction_msg.generate_frame {
if let Some(ref sampler) = self.sampler {
transaction_msg.frame_ops.append(&mut sampler.sample());
}
}
+ let doc = self.documents.get_mut(&document_id).unwrap();
+
for frame_msg in transaction_msg.frame_ops {
let _timer = profile_counters.total_time.timer();
- op.combine(self.process_frame_msg(document_id, frame_msg));
+ op.combine(doc.process_frame_msg(frame_msg));
}
- let doc = self.documents.get_mut(&document_id).unwrap();
+ if doc.dynamic_properties.flush_pending_updates() {
+ op.render = true;
+ }
if transaction_msg.generate_frame {
if let Some(ref mut ros) = doc.render_on_scroll {
*ros = true;
}
if doc.current.scene.root_pipeline_id.is_some() {
op.render = true;
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -398,17 +398,17 @@ impl RenderTask {
// task cache. This allows the blurred box-shadow rect to be cached
// in the texture cache across frames.
// TODO(gw): Consider moving this logic outside this function, especially
// as we add more clip sources that depend on render tasks.
// TODO(gw): If this ever shows up in a profile, we could pre-calculate
// whether a ClipSources contains any box-shadows and skip
// this iteration for the majority of cases.
for clip_item in &clips {
- let clip_sources = clip_store.get_opt_mut(&clip_item.clip_sources).expect("bug");
+ let clip_sources = clip_store.get_mut(clip_item.clip_sources_index);
for &mut (ref mut clip, _) in &mut clip_sources.clips {
match *clip {
ClipSource::BoxShadow(ref mut info) => {
let (cache_size, cache_key) = info.cache_key
.as_ref()
.expect("bug: no cache key set")
.clone();
let blur_radius_dp = cache_key.blur_radius_dp as f32;
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -1230,45 +1230,41 @@ impl ResourceCache {
}
}
pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
self.glyph_rasterizer.get_glyph_index(font_key, ch)
}
#[inline]
- pub fn get_cached_image(
- &self,
- request: ImageRequest,
- ) -> Result<CacheItem, ()> {
+ pub fn get_cached_image(&self, request: ImageRequest) -> Result<CacheItem, ()> {
debug_assert_eq!(self.state, State::QueryResources);
-
- // TODO(Jerry): add a debug option to visualize the corresponding area for
- // the Err() case of CacheItem.
- match *self.cached_images.get(&request.key) {
- ImageResult::UntiledAuto(ref image_info) => {
- Ok(self.texture_cache.get(&image_info.texture_cache_handle))
- }
- ImageResult::Multi(ref entries) => {
- let image_info = entries.get(&request.into());
- Ok(self.texture_cache.get(&image_info.texture_cache_handle))
- }
- ImageResult::Err(_) => {
- Err(())
- }
- }
+ let image_info = self.get_image_info(request)?;
+ Ok(self.get_texture_cache_item(&image_info.texture_cache_handle))
}
pub fn get_cached_render_task(
&self,
handle: &RenderTaskCacheEntryHandle,
) -> &RenderTaskCacheEntry {
self.cached_render_tasks.get_cache_entry(handle)
}
+ #[inline]
+ fn get_image_info(&self, request: ImageRequest) -> Result<&CachedImageInfo, ()> {
+ // TODO(Jerry): add a debug option to visualize the corresponding area for
+ // the Err() case of CacheItem.
+ match *self.cached_images.get(&request.key) {
+ ImageResult::UntiledAuto(ref image_info) => Ok(image_info),
+ ImageResult::Multi(ref entries) => Ok(entries.get(&request.into())),
+ ImageResult::Err(_) => Err(()),
+ }
+ }
+
+ #[inline]
pub fn get_texture_cache_item(&self, handle: &TextureCacheHandle) -> CacheItem {
self.texture_cache.get(handle)
}
pub fn get_image_properties(&self, image_key: ImageKey) -> Option<ImageProperties> {
let image_template = &self.resources.image_templates.get(image_key);
image_template.map(|image_template| {
--- a/gfx/webrender/src/scene.rs
+++ b/gfx/webrender/src/scene.rs
@@ -8,48 +8,82 @@ use api::{ItemRange, MixBlendMode, Stack
use internal_types::FastHashMap;
use std::sync::Arc;
/// Stores a map of the animated property bindings for the current display list. These
/// can be used to animate the transform and/or opacity of a display list without
/// re-submitting the display list itself.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
-#[derive(Clone)]
pub struct SceneProperties {
transform_properties: FastHashMap<PropertyBindingId, LayoutTransform>,
float_properties: FastHashMap<PropertyBindingId, f32>,
+ current_properties: DynamicProperties,
+ pending_properties: Option<DynamicProperties>,
}
impl SceneProperties {
pub fn new() -> Self {
SceneProperties {
transform_properties: FastHashMap::default(),
float_properties: FastHashMap::default(),
+ current_properties: DynamicProperties::default(),
+ pending_properties: None,
}
}
/// Set the current property list for this display list.
pub fn set_properties(&mut self, properties: DynamicProperties) {
- self.transform_properties.clear();
- self.float_properties.clear();
- self.add_properties(properties);
+ self.pending_properties = Some(properties);
}
/// Add to the current property list for this display list.
pub fn add_properties(&mut self, properties: DynamicProperties) {
- for property in properties.transforms {
- self.transform_properties
- .insert(property.key.id, property.value);
+ let mut pending_properties = self.pending_properties
+ .take()
+ .unwrap_or_default();
+
+ pending_properties.transforms.extend(properties.transforms);
+ pending_properties.floats.extend(properties.floats);
+
+ self.pending_properties = Some(pending_properties);
+ }
+
+ /// Flush any pending updates to the scene properties. Returns
+ /// true if the properties have changed since the last flush
+ /// was called. This code allows properties to be changed by
+ /// multiple set_properties and add_properties calls during a
+ /// single transaction, and still correctly determine if any
+ /// properties have changed. This can have significant power
+ /// saving implications, allowing a frame build to be skipped
+ /// if the properties haven't changed in many cases.
+ pub fn flush_pending_updates(&mut self) -> bool {
+ let mut properties_changed = false;
+
+ if let Some(pending_properties) = self.pending_properties.take() {
+ if pending_properties != self.current_properties {
+ self.transform_properties.clear();
+ self.float_properties.clear();
+
+ for property in &pending_properties.transforms {
+ self.transform_properties
+ .insert(property.key.id, property.value);
+ }
+
+ for property in &pending_properties.floats {
+ self.float_properties
+ .insert(property.key.id, property.value);
+ }
+
+ self.current_properties = pending_properties;
+ properties_changed = true;
+ }
}
- for property in properties.floats {
- self.float_properties
- .insert(property.key.id, property.value);
- }
+ properties_changed
}
/// Get the current value for a transform property.
pub fn resolve_layout_transform(
&self,
property: &PropertyBinding<LayoutTransform>,
) -> LayoutTransform {
match *property {
--- a/gfx/webrender/src/spatial_node.rs
+++ b/gfx/webrender/src/spatial_node.rs
@@ -21,21 +21,16 @@ pub enum SpatialNodeType {
StickyFrame(StickyFrameInfo),
/// Transforms it's content, but doesn't clip it. Can also be adjusted
/// by scroll events or setting scroll offsets.
ScrollFrame(ScrollFrameInfo),
/// A reference frame establishes a new coordinate space in the tree.
ReferenceFrame(ReferenceFrameInfo),
-
- /// An empty node, used to pad the ClipScrollTree's array of nodes so that
- /// we can immediately use each assigned SpatialNodeIndex. After display
- /// list flattening this node type should never be used.
- Empty,
}
/// Contains information common among all types of ClipScrollTree nodes.
#[derive(Clone, Debug)]
pub struct SpatialNode {
/// The transformation for this viewport in world coordinates is the transformation for
/// our parent reference frame, plus any accumulated scrolling offsets from nodes
/// between our reference frame and this node. For reference frames, we also include
@@ -89,20 +84,16 @@ impl SpatialNode {
pipeline_id,
node_type,
invertible: true,
coordinate_system_id: CoordinateSystemId(0),
coordinate_system_relative_transform: LayoutFastTransform::identity(),
}
}
- pub fn empty() -> SpatialNode {
- Self::new(PipelineId::dummy(), None, SpatialNodeType::Empty)
- }
-
pub fn new_scroll_frame(
pipeline_id: PipelineId,
parent_index: SpatialNodeIndex,
external_id: Option<ExternalScrollId>,
frame_rect: &LayoutRect,
content_size: &LayoutSize,
scroll_sensitivity: ScrollSensitivity,
) -> Self {
@@ -489,17 +480,16 @@ impl SpatialNode {
state.parent_accumulated_scroll_offset = LayoutVector2D::zero();
state.coordinate_system_relative_transform =
self.coordinate_system_relative_transform.clone();
let translation = -info.origin_in_parent_reference_frame;
state.nearest_scrolling_ancestor_viewport =
state.nearest_scrolling_ancestor_viewport
.translate(&translation);
}
- SpatialNodeType::Empty => unreachable!("Empty node remaining in ClipScrollTree."),
}
}
pub fn scrollable_size(&self) -> LayoutSize {
match self.node_type {
SpatialNodeType::ScrollFrame(state) => state.scrollable_size,
_ => LayoutSize::zero(),
}
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -1085,26 +1085,26 @@ pub enum PropertyBinding<T> {
impl<T> From<T> for PropertyBinding<T> {
fn from(value: T) -> PropertyBinding<T> {
PropertyBinding::Value(value)
}
}
/// The current value of an animated property. This is
/// supplied by the calling code.
-#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)]
pub struct PropertyValue<T> {
pub key: PropertyBindingKey<T>,
pub value: T,
}
/// When using `generate_frame()`, a list of `PropertyValue` structures
/// can optionally be supplied to provide the current value of any
/// animated properties.
-#[derive(Clone, Deserialize, Serialize, Debug)]
+#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Default)]
pub struct DynamicProperties {
pub transforms: Vec<PropertyValue<LayoutTransform>>,
pub floats: Vec<PropertyValue<f32>>,
}
pub trait RenderNotifier: Send {
fn clone(&self) -> Box<RenderNotifier>;
fn wake_up(&self);
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-e850fbd2e0e60a8de76c2d2464f0fa27316d5949
+8a4fe66528aa362721e4048aac3cd5abf7faaf2c
--- a/gfx/wrench/src/main.rs
+++ b/gfx/wrench/src/main.rs
@@ -177,33 +177,28 @@ impl WindowWrapper {
match *self {
WindowWrapper::Window(ref window, _) => window.swap_buffers().unwrap(),
WindowWrapper::Angle(_, ref context, _) => context.swap_buffers().unwrap(),
WindowWrapper::Headless(_, _) => {}
}
}
fn get_inner_size(&self) -> DeviceUintSize {
- //HACK: `winit` needs to figure out its hidpi story...
- #[cfg(target_os = "macos")]
- fn inner_size(window: &winit::Window) -> LogicalSize {
- let LogicalSize { width, height } = window.get_inner_size().unwrap();
- let factor = window.get_hidpi_factor();
- LogicalSize::new(width * factor, height * factor)
+ fn inner_size(window: &winit::Window) -> DeviceUintSize {
+ let size = window
+ .get_inner_size()
+ .unwrap()
+ .to_physical(window.get_hidpi_factor());
+ DeviceUintSize::new(size.width as u32, size.height as u32)
}
- #[cfg(not(target_os = "macos"))]
- fn inner_size(window: &winit::Window) -> LogicalSize {
- window.get_inner_size().unwrap()
- }
- let LogicalSize { width, height } = match *self {
+ match *self {
WindowWrapper::Window(ref window, _) => inner_size(window.window()),
WindowWrapper::Angle(ref window, ..) => inner_size(window),
- WindowWrapper::Headless(ref context, _) => LogicalSize::new(context.width as f64, context.height as f64),
- };
- DeviceUintSize::new(width as u32, height as u32)
+ WindowWrapper::Headless(ref context, _) => DeviceUintSize::new(context.width, context.height),
+ }
}
fn hidpi_factor(&self) -> f32 {
match *self {
WindowWrapper::Window(ref window, _) => window.get_hidpi_factor() as f32,
WindowWrapper::Angle(ref window, ..) => window.get_hidpi_factor() as f32,
WindowWrapper::Headless(_, _) => 1.0,
}