diff --git a/gfx/webrender/src/batch.rs b/gfx/webrender/src/batch.rs index bcfa13dcc0d0..426c22889916 100644 --- a/gfx/webrender/src/batch.rs +++ b/gfx/webrender/src/batch.rs @@ -497,6 +497,9 @@ impl AlphaBatchBuilder { // 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 = [ @@ -654,14 +657,18 @@ impl AlphaBatchBuilder { 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 @@ -1056,8 +1063,13 @@ impl AlphaBatchBuilder { 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, @@ -1385,6 +1397,7 @@ impl BrushPrimitive { resource_cache: &ResourceCache, gpu_cache: &mut GpuCache, deferred_resolves: &mut Vec, + is_chased: bool, ) -> Option<(BrushBatchKind, BatchTextures, [i32; 3])> { match self.kind { BrushKind::Image { request, ref source, .. } => { @@ -1406,6 +1419,9 @@ impl BrushPrimitive { 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 @@ -1751,7 +1767,7 @@ impl ClipBatcher { ) { 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(), @@ -1772,16 +1788,14 @@ impl ClipBatcher { ) { 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); diff --git a/gfx/webrender/src/clip.rs b/gfx/webrender/src/clip.rs index c942842dce81..4fd9ecd758af 100644 --- a/gfx/webrender/src/clip.rs +++ b/gfx/webrender/src/clip.rs @@ -9,22 +9,51 @@ 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 type ClipStore = FreeList; -pub type ClipSourcesHandle = FreeListHandle; -pub type ClipSourcesWeakHandle = WeakFreeListHandle; +pub struct ClipStore { + clip_sources: Vec, +} + +impl ClipStore { + pub fn new() -> ClipStore { + ClipStore { + clip_sources: Vec::new(), + } + } + + 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 { @@ -91,28 +120,6 @@ pub enum ClipSource { LineDecoration(LineDecorationClipSource), } -impl From 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, @@ -280,10 +287,14 @@ pub struct ClipSources { pub local_outer_rect: Option, 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) -> Self { + pub fn new( + clips: Vec, + 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 = @@ -301,9 +312,33 @@ impl ClipSources { 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 } @@ -647,8 +682,7 @@ impl Iterator for ClipChainNodeIter { #[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, } diff --git a/gfx/webrender/src/clip_node.rs b/gfx/webrender/src/clip_node.rs index f297887e8aa1..78363d6c7c89 100644 --- a/gfx/webrender/src/clip_node.rs +++ b/gfx/webrender/src/clip_node.rs @@ -3,19 +3,16 @@ * 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, + pub clip_sources_index: ClipSourcesIndex, /// An index to a ClipChain defined by this ClipNode's hiearchy in the display /// list. @@ -31,35 +28,18 @@ pub struct ClipNode { } 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, @@ -77,8 +57,7 @@ impl ClipNode { 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 diff --git a/gfx/webrender/src/clip_scroll_tree.rs b/gfx/webrender/src/clip_scroll_tree.rs index 265382702bbc..53a7d8906877 100644 --- a/gfx/webrender/src/clip_scroll_tree.rs +++ b/gfx/webrender/src/clip_scroll_tree.rs @@ -5,7 +5,7 @@ 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; @@ -256,14 +256,13 @@ impl ClipScrollTree { ); 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); @@ -355,33 +354,29 @@ impl ClipScrollTree { 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, pipeline_id: PipelineId, frame_rect: &LayoutRect, content_size: &LayoutSize, scroll_sensitivity: ScrollSensitivity, - ) { + ) -> SpatialNodeIndex { let node = SpatialNode::new_scroll_frame( pipeline_id, parent_index, @@ -390,18 +385,17 @@ impl ClipScrollTree { 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, source_transform: Option>, source_perspective: Option, origin_in_parent_reference_frame: LayoutVector2D, pipeline_id: PipelineId, - ) { + ) -> SpatialNodeIndex { let node = SpatialNode::new_reference_frame( parent_index, source_transform, @@ -409,22 +403,21 @@ impl ClipScrollTree { 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( @@ -437,47 +430,22 @@ impl ClipScrollTree { 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) { @@ -508,7 +476,6 @@ impl ClipScrollTree { 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)); diff --git a/gfx/webrender/src/display_list_flattener.rs b/gfx/webrender/src/display_list_flattener.rs index 05ded2bd7513..a3227e007d1a 100644 --- a/gfx/webrender/src/display_list_flattener.rs +++ b/gfx/webrender/src/display_list_flattener.rs @@ -13,7 +13,7 @@ use api::{LineOrientation, LineStyle, LocalClip, NinePatchBorderSource, Pipeline 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}; @@ -33,7 +33,7 @@ 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}; @@ -44,52 +44,20 @@ static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF { 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, - - /// 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, - - /// 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, - - /// 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, + spatial_node_map: FastHashMap, } 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) { @@ -97,53 +65,24 @@ impl ClipIdToIndexMapper { 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 @@ -154,6 +93,16 @@ impl ClipIdToIndexMapper { 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 @@ -248,7 +197,6 @@ impl<'a> DisplayListFlattener<'a> { 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, @@ -401,13 +349,12 @@ impl<'a> DisplayListFlattener<'a> { 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); } @@ -532,8 +479,6 @@ impl<'a> DisplayListFlattener<'a> { }, }; - 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, @@ -791,23 +736,29 @@ impl<'a> DisplayListFlattener<'a> { None } + fn add_clip_sources( + &mut self, + clip_sources: Vec, + spatial_node_index: SpatialNodeIndex, + ) -> Option { + 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, + clip_sources: Option, 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, @@ -877,6 +828,10 @@ impl<'a> DisplayListFlattener<'a> { .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( @@ -893,6 +848,10 @@ impl<'a> DisplayListFlattener<'a> { } 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); @@ -919,7 +878,7 @@ impl<'a> DisplayListFlattener<'a> { 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 ); @@ -1219,10 +1178,8 @@ impl<'a> DisplayListFlattener<'a> { source_perspective: Option, 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, @@ -1230,6 +1187,7 @@ impl<'a> DisplayListFlattener<'a> { 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) => @@ -1289,19 +1247,18 @@ impl<'a> DisplayListFlattener<'a> { 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 } @@ -1316,10 +1273,8 @@ impl<'a> DisplayListFlattener<'a> { 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, @@ -1327,6 +1282,7 @@ impl<'a> DisplayListFlattener<'a> { 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 } @@ -1455,7 +1411,7 @@ impl<'a> DisplayListFlattener<'a> { let prim_index = self.create_primitive( info, - Vec::new(), + None, PrimitiveContainer::Brush(prim), ); @@ -1967,28 +1923,14 @@ impl<'a> DisplayListFlattener<'a> { 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 { diff --git a/gfx/webrender/src/freelist.rs b/gfx/webrender/src/freelist.rs index 8ff53e98f2c7..cbf22d3d922f 100644 --- a/gfx/webrender/src/freelist.rs +++ b/gfx/webrender/src/freelist.rs @@ -3,7 +3,6 @@ * 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 @@ -86,15 +85,6 @@ impl FreeList { } } - pub fn recycle(self) -> FreeList { - 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; diff --git a/gfx/webrender/src/gpu_types.rs b/gfx/webrender/src/gpu_types.rs index eac92bd5e872..c3a813b2b8de 100644 --- a/gfx/webrender/src/gpu_types.rs +++ b/gfx/webrender/src/gpu_types.rs @@ -195,6 +195,7 @@ impl PrimitiveHeaders { // 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, @@ -349,12 +350,15 @@ impl From for PrimitiveInstance { 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 diff --git a/gfx/webrender/src/hit_test.rs b/gfx/webrender/src/hit_test.rs index 4e9b8d3a9219..57b02534daee 100644 --- a/gfx/webrender/src/hit_test.rs +++ b/gfx/webrender/src/hit_test.rs @@ -34,6 +34,29 @@ pub struct HitTestClipNode { regions: Vec, } +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 @@ -148,15 +171,11 @@ impl HitTester { } 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 { @@ -346,33 +365,6 @@ impl HitTester { } } -fn get_regions_for_clip_node( - node: &ClipNode, - clip_store: &ClipStore -) -> Vec { - 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, diff --git a/gfx/webrender/src/prim_store.rs b/gfx/webrender/src/prim_store.rs index f545dfe6bb7c..d9a169e6acae 100644 --- a/gfx/webrender/src/prim_store.rs +++ b/gfx/webrender/src/prim_store.rs @@ -13,7 +13,7 @@ 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}; @@ -188,7 +188,7 @@ pub struct ScreenRect { #[derive(Debug)] pub struct PrimitiveMetadata { pub opacity: PrimitiveOpacity, - pub clip_sources: Option, + pub clip_sources_index: Option, pub prim_kind: PrimitiveKind, pub cpu_prim_index: SpecificPrimitiveIndex, pub gpu_location: GpuCacheHandle, @@ -1281,14 +1281,14 @@ impl PrimitiveStore { local_rect: &LayoutRect, local_clip_rect: &LayoutRect, is_backface_visible: bool, - clip_sources: Option, + clip_sources_index: Option, tag: Option, 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, @@ -1983,6 +1983,9 @@ impl PrimitiveStore { 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, @@ -2059,7 +2062,7 @@ impl PrimitiveStore { 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 @@ -2074,7 +2077,7 @@ impl PrimitiveStore { // 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. @@ -2285,8 +2288,8 @@ impl PrimitiveStore { 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, @@ -2307,8 +2310,7 @@ impl PrimitiveStore { 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. @@ -2643,10 +2645,6 @@ impl PrimitiveStore { }; 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). @@ -2656,6 +2654,13 @@ impl PrimitiveStore { 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 |= @@ -2716,7 +2721,12 @@ impl PrimitiveStore { }; 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, }; @@ -2749,7 +2759,12 @@ impl PrimitiveStore { 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, }; diff --git a/gfx/webrender/src/render_backend.rs b/gfx/webrender/src/render_backend.rs index a410a9e1c827..c9fb077f3760 100644 --- a/gfx/webrender/src/render_backend.rs +++ b/gfx/webrender/src/render_backend.rs @@ -169,6 +169,97 @@ impl Document { !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(); @@ -601,100 +692,6 @@ impl RenderBackend { } } - 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) } @@ -1070,12 +1067,16 @@ impl RenderBackend { } } + 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 { diff --git a/gfx/webrender/src/render_task.rs b/gfx/webrender/src/render_task.rs index fbb20e5f300c..631baed66bfd 100644 --- a/gfx/webrender/src/render_task.rs +++ b/gfx/webrender/src/render_task.rs @@ -403,7 +403,7 @@ impl RenderTask { // 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) => { diff --git a/gfx/webrender/src/resource_cache.rs b/gfx/webrender/src/resource_cache.rs index 6183be7c9e59..5e04145d453b 100644 --- a/gfx/webrender/src/resource_cache.rs +++ b/gfx/webrender/src/resource_cache.rs @@ -1235,26 +1235,10 @@ impl ResourceCache { } #[inline] - pub fn get_cached_image( - &self, - request: ImageRequest, - ) -> Result { + pub fn get_cached_image(&self, request: ImageRequest) -> Result { 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( @@ -1264,6 +1248,18 @@ impl ResourceCache { 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) } diff --git a/gfx/webrender/src/scene.rs b/gfx/webrender/src/scene.rs index 27b9cc3b6982..ef55211cabbf 100644 --- a/gfx/webrender/src/scene.rs +++ b/gfx/webrender/src/scene.rs @@ -13,10 +13,11 @@ use std::sync::Arc; /// 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, float_properties: FastHashMap, + current_properties: DynamicProperties, + pending_properties: Option, } impl SceneProperties { @@ -24,27 +25,60 @@ impl SceneProperties { 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. diff --git a/gfx/webrender/src/spatial_node.rs b/gfx/webrender/src/spatial_node.rs index b7b110fa86ea..b544f36609f2 100644 --- a/gfx/webrender/src/spatial_node.rs +++ b/gfx/webrender/src/spatial_node.rs @@ -26,11 +26,6 @@ pub enum SpatialNodeType { /// 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. @@ -94,10 +89,6 @@ impl SpatialNode { } } - pub fn empty() -> SpatialNode { - Self::new(PipelineId::dummy(), None, SpatialNodeType::Empty) - } - pub fn new_scroll_frame( pipeline_id: PipelineId, parent_index: SpatialNodeIndex, @@ -494,7 +485,6 @@ impl SpatialNode { state.nearest_scrolling_ancestor_viewport .translate(&translation); } - SpatialNodeType::Empty => unreachable!("Empty node remaining in ClipScrollTree."), } } diff --git a/gfx/webrender_api/src/api.rs b/gfx/webrender_api/src/api.rs index 1be9edd0ea54..9aa604b17e21 100644 --- a/gfx/webrender_api/src/api.rs +++ b/gfx/webrender_api/src/api.rs @@ -1090,7 +1090,7 @@ impl From for PropertyBinding { /// 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 { pub key: PropertyBindingKey, pub value: T, @@ -1099,7 +1099,7 @@ pub struct PropertyValue { /// 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>, pub floats: Vec>, diff --git a/gfx/webrender_bindings/revision.txt b/gfx/webrender_bindings/revision.txt index 0f4e34fd9054..4e8cc14cd219 100644 --- a/gfx/webrender_bindings/revision.txt +++ b/gfx/webrender_bindings/revision.txt @@ -1 +1 @@ -e850fbd2e0e60a8de76c2d2464f0fa27316d5949 +8a4fe66528aa362721e4048aac3cd5abf7faaf2c diff --git a/gfx/wrench/src/main.rs b/gfx/wrench/src/main.rs index c84d6134f1c2..28b0defee8be 100644 --- a/gfx/wrench/src/main.rs +++ b/gfx/wrench/src/main.rs @@ -182,23 +182,18 @@ impl WindowWrapper { } 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 {