mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 16:55:40 +00:00
Bug 1505778 - Update webrender to commit b8829189cfc1769550c9ab4a4bb994e28621f009 (WR PR #3270). r=kats
Differential Revision: https://phabricator.services.mozilla.com/D11358 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
4f99ddecde
commit
93c86f325a
@ -11,7 +11,7 @@ use app_units::Au;
|
||||
use border::{ensure_no_corner_overlap, BorderRadiusAu};
|
||||
use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey};
|
||||
use box_shadow::get_max_scale_for_box_shadow;
|
||||
use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex};
|
||||
use clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex};
|
||||
use ellipse::Ellipse;
|
||||
use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
|
||||
use gpu_types::{BoxShadowStretchMode};
|
||||
@ -97,13 +97,14 @@ use util::{extract_inner_rect_safe, project_rect, ScaleOffset};
|
||||
// Type definitions for interning clip nodes.
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct ClipDataMarker;
|
||||
|
||||
pub type ClipDataStore = intern::DataStore<ClipItemKey, ClipNode, ClipDataMarker>;
|
||||
pub type ClipDataHandle = intern::Handle<ClipDataMarker>;
|
||||
pub type ClipDataUpdateList = intern::UpdateList<ClipItemKey>;
|
||||
pub type ClipDataInterner = intern::Interner<ClipItemKey, ClipItemSceneData, ClipDataMarker>;
|
||||
pub type ClipUid = intern::ItemUid<ClipDataMarker>;
|
||||
|
||||
// Result of comparing a clip node instance against a local rect.
|
||||
#[derive(Debug)]
|
||||
@ -256,7 +257,6 @@ struct ClipNodeInfo {
|
||||
conversion: ClipSpaceConversion,
|
||||
handle: ClipDataHandle,
|
||||
spatial_node_index: SpatialNodeIndex,
|
||||
has_non_root_coord_system: bool,
|
||||
}
|
||||
|
||||
impl ClipNode {
|
||||
@ -423,7 +423,6 @@ pub struct ClipChainInstance {
|
||||
// Combined clip rect for clips that are in the
|
||||
// same coordinate system as the primitive.
|
||||
pub local_clip_rect: LayoutRect,
|
||||
pub has_non_root_coord_system: bool,
|
||||
pub has_non_local_clips: bool,
|
||||
// If true, this clip chain requires allocation
|
||||
// of a clip mask.
|
||||
@ -578,7 +577,6 @@ impl ClipStore {
|
||||
// Run through the clip nodes, and see which ones affect this prim region.
|
||||
|
||||
let first_clip_node_index = self.clip_node_instances.len() as u32;
|
||||
let mut has_non_root_coord_system = false;
|
||||
let mut has_non_local_clips = false;
|
||||
let mut needs_mask = false;
|
||||
|
||||
@ -664,8 +662,6 @@ impl ClipStore {
|
||||
spatial_node_index: node_info.spatial_node_index,
|
||||
};
|
||||
self.clip_node_instances.push(instance);
|
||||
|
||||
has_non_root_coord_system |= node_info.has_non_root_coord_system;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -679,7 +675,6 @@ impl ClipStore {
|
||||
// Return a valid clip chain instance
|
||||
Some(ClipChainInstance {
|
||||
clips_range,
|
||||
has_non_root_coord_system,
|
||||
has_non_local_clips,
|
||||
local_clip_rect,
|
||||
pic_clip_rect,
|
||||
@ -1322,7 +1317,6 @@ fn add_clip_node_to_current_chain(
|
||||
conversion,
|
||||
handle,
|
||||
spatial_node_index: clip_spatial_node_index,
|
||||
has_non_root_coord_system: clip_spatial_node.coordinate_system_id != CoordinateSystemId::root(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ impl CoordinateSystem {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, PartialOrd)]
|
||||
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct SpatialNodeIndex(pub usize);
|
||||
|
@ -173,7 +173,6 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
output_pipelines: &FastHashSet<PipelineId>,
|
||||
frame_builder_config: &FrameBuilderConfig,
|
||||
new_scene: &mut Scene,
|
||||
scene_id: u64,
|
||||
picture_id_generator: &mut PictureIdGenerator,
|
||||
resources: &mut DocumentResources,
|
||||
) -> FrameBuilder {
|
||||
@ -221,7 +220,6 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
view.inner_rect,
|
||||
background_color,
|
||||
view.window_size,
|
||||
scene_id,
|
||||
flattener,
|
||||
)
|
||||
}
|
||||
@ -1007,6 +1005,7 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
&mut self.picture_id_generator,
|
||||
&mut self.prim_store,
|
||||
&self.resources.prim_interner,
|
||||
&self.clip_store,
|
||||
);
|
||||
(sc.is_3d(), extra_instance)
|
||||
},
|
||||
@ -1141,6 +1140,7 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
prim_list,
|
||||
stacking_context.spatial_node_index,
|
||||
max_clip,
|
||||
&self.clip_store,
|
||||
);
|
||||
let leaf_pic_index = self.prim_store.create_picture(leaf_picture);
|
||||
|
||||
@ -1185,6 +1185,7 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
prim_list,
|
||||
stacking_context.spatial_node_index,
|
||||
max_clip,
|
||||
&self.clip_store,
|
||||
);
|
||||
|
||||
current_pic_index = self.prim_store.create_picture(container_picture);
|
||||
@ -1211,6 +1212,7 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
prim_list,
|
||||
stacking_context.spatial_node_index,
|
||||
max_clip,
|
||||
&self.clip_store,
|
||||
);
|
||||
let filter_pic_index = self.prim_store.create_picture(filter_picture);
|
||||
current_pic_index = filter_pic_index;
|
||||
@ -1244,6 +1246,7 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
prim_list,
|
||||
stacking_context.spatial_node_index,
|
||||
max_clip,
|
||||
&self.clip_store,
|
||||
);
|
||||
let blend_pic_index = self.prim_store.create_picture(blend_picture);
|
||||
current_pic_index = blend_pic_index;
|
||||
@ -1585,6 +1588,7 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
prim_list,
|
||||
pending_shadow.clip_and_scroll.spatial_node_index,
|
||||
max_clip,
|
||||
&self.clip_store,
|
||||
);
|
||||
|
||||
// Create the primitive to draw the shadow picture into the scene.
|
||||
@ -2307,6 +2311,7 @@ impl FlattenedStackingContext {
|
||||
picture_id_generator: &mut PictureIdGenerator,
|
||||
prim_store: &mut PrimitiveStore,
|
||||
prim_interner: &PrimitiveDataInterner,
|
||||
clip_store: &ClipStore,
|
||||
) -> Option<PrimitiveInstance> {
|
||||
if !self.is_3d() || self.primitives.is_empty() {
|
||||
return None
|
||||
@ -2335,6 +2340,7 @@ impl FlattenedStackingContext {
|
||||
prim_list,
|
||||
self.spatial_node_index,
|
||||
LayoutRect::max_rect(),
|
||||
clip_store,
|
||||
);
|
||||
|
||||
let pic_index = prim_store.create_picture(container_picture);
|
||||
|
@ -57,7 +57,6 @@ pub struct FrameBuilder {
|
||||
screen_rect: DeviceUintRect,
|
||||
background_color: Option<ColorF>,
|
||||
window_size: DeviceUintSize,
|
||||
scene_id: u64,
|
||||
root_pic_index: PictureIndex,
|
||||
pub prim_store: PrimitiveStore,
|
||||
pub clip_store: ClipStore,
|
||||
@ -66,7 +65,6 @@ pub struct FrameBuilder {
|
||||
}
|
||||
|
||||
pub struct FrameBuildingContext<'a> {
|
||||
pub scene_id: u64,
|
||||
pub device_pixel_scale: DevicePixelScale,
|
||||
pub scene_properties: &'a SceneProperties,
|
||||
pub pipelines: &'a FastHashMap<PipelineId, Arc<ScenePipeline>>,
|
||||
@ -107,7 +105,6 @@ pub struct PictureContext {
|
||||
/// Mutable state of a picture that gets modified when
|
||||
/// the children are processed.
|
||||
pub struct PictureState {
|
||||
pub has_non_root_coord_system: bool,
|
||||
pub is_cacheable: bool,
|
||||
pub local_rect_changed: bool,
|
||||
pub map_local_to_pic: SpaceMapper<LayoutPixel, PicturePixel>,
|
||||
@ -146,7 +143,6 @@ impl FrameBuilder {
|
||||
screen_rect: DeviceUintRect::zero(),
|
||||
window_size: DeviceUintSize::zero(),
|
||||
background_color: None,
|
||||
scene_id: 0,
|
||||
root_pic_index: PictureIndex(0),
|
||||
config: FrameBuilderConfig {
|
||||
default_font_render_mode: FontRenderMode::Mono,
|
||||
@ -161,7 +157,6 @@ impl FrameBuilder {
|
||||
screen_rect: DeviceUintRect,
|
||||
background_color: Option<ColorF>,
|
||||
window_size: DeviceUintSize,
|
||||
scene_id: u64,
|
||||
flattener: DisplayListFlattener,
|
||||
) -> Self {
|
||||
FrameBuilder {
|
||||
@ -172,7 +167,6 @@ impl FrameBuilder {
|
||||
screen_rect,
|
||||
background_color,
|
||||
window_size,
|
||||
scene_id,
|
||||
config: flattener.config,
|
||||
}
|
||||
}
|
||||
@ -207,7 +201,6 @@ impl FrameBuilder {
|
||||
let world_rect = (self.screen_rect.to_f32() / device_pixel_scale).round_out();
|
||||
|
||||
let frame_context = FrameBuildingContext {
|
||||
scene_id: self.scene_id,
|
||||
device_pixel_scale,
|
||||
scene_properties,
|
||||
pipelines,
|
||||
|
@ -20,7 +20,7 @@
|
||||
//! TODO(gw): Add an occupied list head, for fast iteration of the occupied list
|
||||
//! to implement retain() style functionality.
|
||||
|
||||
use std::fmt;
|
||||
use std::{fmt, u32};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
@ -68,6 +68,14 @@ impl<M> FreeListHandle<M> {
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invalid() -> Self {
|
||||
Self {
|
||||
index: 0,
|
||||
epoch: Epoch::invalid(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> Clone for WeakFreeListHandle<M> {
|
||||
|
@ -63,15 +63,30 @@ pub struct UpdateList<S> {
|
||||
updates: Vec<Update<S>>,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
|
||||
pub struct ItemUid<T> {
|
||||
uid: usize,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Handle<T> {
|
||||
index: usize,
|
||||
index: u32,
|
||||
epoch: Epoch,
|
||||
uid: ItemUid<T>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl <T> Handle<T> where T: Copy {
|
||||
pub fn uid(&self) -> ItemUid<T> {
|
||||
self.uid
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub enum UpdateKind<S> {
|
||||
@ -150,7 +165,7 @@ impl<S, T, M> DataStore<S, T, M> where S: Debug, T: From<S>, M: Debug {
|
||||
impl<S, T, M> ops::Index<Handle<M>> for DataStore<S, T, M> {
|
||||
type Output = T;
|
||||
fn index(&self, handle: Handle<M>) -> &T {
|
||||
let item = &self.items[handle.index];
|
||||
let item = &self.items[handle.index as usize];
|
||||
assert_eq!(item.epoch, handle.epoch);
|
||||
&item.data
|
||||
}
|
||||
@ -160,7 +175,7 @@ impl<S, T, M> ops::Index<Handle<M>> for DataStore<S, T, M> {
|
||||
/// Retrieve an item from the store via handle
|
||||
impl<S, T, M> ops::IndexMut<Handle<M>> for DataStore<S, T, M> {
|
||||
fn index_mut(&mut self, handle: Handle<M>) -> &mut T {
|
||||
let item = &mut self.items[handle.index];
|
||||
let item = &mut self.items[handle.index as usize];
|
||||
assert_eq!(item.epoch, handle.epoch);
|
||||
&mut item.data
|
||||
}
|
||||
@ -182,6 +197,8 @@ pub struct Interner<S : Eq + Hash + Clone + Debug, D, M> {
|
||||
updates: Vec<Update<S>>,
|
||||
/// The current epoch for the interner.
|
||||
current_epoch: Epoch,
|
||||
/// Incrementing counter for identifying stable values.
|
||||
next_uid: usize,
|
||||
/// The information associated with each interned
|
||||
/// item that can be accessed by the interner.
|
||||
local_data: Vec<Item<D>>,
|
||||
@ -195,6 +212,7 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De
|
||||
free_list: Vec::new(),
|
||||
updates: Vec::new(),
|
||||
current_epoch: Epoch(1),
|
||||
next_uid: 0,
|
||||
local_data: Vec::new(),
|
||||
}
|
||||
}
|
||||
@ -221,10 +239,10 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De
|
||||
// via valid handles.
|
||||
if handle.epoch != self.current_epoch {
|
||||
self.updates.push(Update {
|
||||
index: handle.index,
|
||||
index: handle.index as usize,
|
||||
kind: UpdateKind::UpdateEpoch,
|
||||
});
|
||||
self.local_data[handle.index].epoch = self.current_epoch;
|
||||
self.local_data[handle.index as usize].epoch = self.current_epoch;
|
||||
}
|
||||
handle.epoch = self.current_epoch;
|
||||
return *handle;
|
||||
@ -246,14 +264,19 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De
|
||||
|
||||
// Generate a handle for access via the data store.
|
||||
let handle = Handle {
|
||||
index,
|
||||
index: index as u32,
|
||||
epoch: self.current_epoch,
|
||||
uid: ItemUid {
|
||||
uid: self.next_uid,
|
||||
_marker: PhantomData,
|
||||
},
|
||||
_marker: PhantomData,
|
||||
};
|
||||
|
||||
// Store this handle so the next time it is
|
||||
// interned, it gets re-used.
|
||||
self.map.insert(data.clone(), handle);
|
||||
self.next_uid += 1;
|
||||
|
||||
// Create the local data for this item that is
|
||||
// being interned.
|
||||
@ -291,9 +314,9 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De
|
||||
// - Add index to the free-list for re-use.
|
||||
// - Add an update to the data store to invalidate this slow.
|
||||
// - Remove from the hash map.
|
||||
free_list.push(handle.index);
|
||||
free_list.push(handle.index as usize);
|
||||
updates.push(Update {
|
||||
index: handle.index,
|
||||
index: handle.index as usize,
|
||||
kind: UpdateKind::Remove,
|
||||
});
|
||||
return false;
|
||||
@ -318,7 +341,7 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De
|
||||
impl<S, D, M> ops::Index<Handle<M>> for Interner<S, D, M> where S: Eq + Clone + Hash + Debug, M: Copy + Debug {
|
||||
type Output = D;
|
||||
fn index(&self, handle: Handle<M>) -> &D {
|
||||
let item = &self.local_data[handle.index];
|
||||
let item = &self.local_data[handle.index as usize];
|
||||
assert_eq!(item.epoch, handle.epoch);
|
||||
&item.data
|
||||
}
|
||||
|
@ -109,6 +109,7 @@ mod scene_builder;
|
||||
mod segment;
|
||||
mod shade;
|
||||
mod spatial_node;
|
||||
mod surface;
|
||||
mod texture_allocator;
|
||||
mod texture_cache;
|
||||
mod tiling;
|
||||
|
@ -3,11 +3,11 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use api::{DeviceRect, FilterOp, MixBlendMode, PipelineId, PremultipliedColorF, PictureRect, PicturePoint};
|
||||
use api::{DeviceIntRect, DeviceIntSize, DevicePoint, LayoutRect, PictureToRasterTransform, LayoutPixel};
|
||||
use api::{DevicePixelScale, PictureIntPoint, PictureIntRect, PictureIntSize, RasterRect, RasterSpace};
|
||||
use api::{DeviceIntRect, DevicePoint, LayoutRect, PictureToRasterTransform, LayoutPixel};
|
||||
use api::{DevicePixelScale, RasterRect, RasterSpace};
|
||||
use api::{PicturePixel, RasterPixel, WorldPixel, WorldRect};
|
||||
use box_shadow::{BLUR_SAMPLE_SCALE};
|
||||
use clip::ClipNodeCollector;
|
||||
use clip::{ClipNodeCollector, ClipStore};
|
||||
use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex};
|
||||
use euclid::{TypedScale, vec3, TypedRect};
|
||||
use internal_types::{FastHashMap, PlaneSplitter};
|
||||
@ -21,6 +21,7 @@ use render_task::{ClearMode, RenderTask, RenderTaskCacheEntryHandle};
|
||||
use render_task::{RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation};
|
||||
use scene::{FilterOpHelpers, SceneProperties};
|
||||
use smallvec::SmallVec;
|
||||
use surface::SurfaceDescriptor;
|
||||
use std::{mem, ops};
|
||||
use tiling::RenderTargetKind;
|
||||
use util::{TransformedRectKind, MatrixHelpers, MaxRect};
|
||||
@ -194,59 +195,6 @@ impl PictureIdGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
// Cache key that determines whether a pre-existing
|
||||
// picture in the texture cache matches the content
|
||||
// of the current picture.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct PictureCacheKey {
|
||||
// NOTE: We specifically want to ensure that we
|
||||
// don't include the device space origin
|
||||
// of this picture in the cache key, because
|
||||
// we want the cache to remain valid as it
|
||||
// is scrolled and/or translated by animation.
|
||||
// This is valid while we have the restriction
|
||||
// in place that only pictures that use the
|
||||
// root coordinate system are cached - once
|
||||
// we relax that, we'll need to consider some
|
||||
// extra parameters, depending on transform.
|
||||
|
||||
// This is a globally unique id of the scene this picture
|
||||
// is associated with, to avoid picture id collisions.
|
||||
scene_id: u64,
|
||||
|
||||
// The unique (for the scene_id) identifier for this picture.
|
||||
// TODO(gw): Currently, these will not be
|
||||
// shared across new display lists,
|
||||
// so will only remain valid during
|
||||
// scrolling. Next step will be to
|
||||
// allow deep comparisons on pictures
|
||||
// between display lists, allowing
|
||||
// pictures that are the same to be
|
||||
// cached across display lists!
|
||||
picture_id: PictureId,
|
||||
|
||||
// Store the rect within the unclipped device
|
||||
// rect that we are actually rendering. This ensures
|
||||
// that if the 'clipped' rect changes, we will see
|
||||
// that the cache is invalid and re-draw the picture.
|
||||
// TODO(gw): To reduce the number of invalidations that
|
||||
// happen as a cached picture scrolls off-screen,
|
||||
// we could round up the size of the off-screen
|
||||
// targets we draw (e.g. 512 pixels). This may
|
||||
// also simplify other parts of the code that
|
||||
// deal with clipped/unclipped rects, such as
|
||||
// the code to inflate the device rect for blurs.
|
||||
pic_relative_render_rect: PictureIntRect,
|
||||
|
||||
// Ensure that if the overall size of the picture
|
||||
// changes, the cache key will not match. This can
|
||||
// happen, for example, during zooming or changes
|
||||
// in device-pixel-ratio.
|
||||
unclipped_size: DeviceIntSize,
|
||||
}
|
||||
|
||||
/// Enum value describing the place of a picture in a 3D context.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Picture3DContext<C> {
|
||||
@ -491,6 +439,9 @@ pub struct PicturePrimitive {
|
||||
|
||||
/// Local clip rect for this picture.
|
||||
pub local_clip_rect: LayoutRect,
|
||||
|
||||
/// A descriptor for this surface that can be used as a cache key.
|
||||
surface_desc: Option<SurfaceDescriptor>,
|
||||
}
|
||||
|
||||
impl PicturePrimitive {
|
||||
@ -530,8 +481,32 @@ impl PicturePrimitive {
|
||||
prim_list: PrimitiveList,
|
||||
spatial_node_index: SpatialNodeIndex,
|
||||
local_clip_rect: LayoutRect,
|
||||
clip_store: &ClipStore,
|
||||
) -> Self {
|
||||
// For now, only create a cache descriptor for blur filters (which
|
||||
// includes text shadows). We can incrementally expand this to
|
||||
// handle more composite modes.
|
||||
let create_cache_descriptor = match requested_composite_mode {
|
||||
Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
|
||||
blur_radius > 0.0
|
||||
}
|
||||
Some(_) | None => {
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
let surface_desc = if create_cache_descriptor {
|
||||
SurfaceDescriptor::new(
|
||||
&prim_list.prim_instances,
|
||||
spatial_node_index,
|
||||
clip_store,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
PicturePrimitive {
|
||||
surface_desc,
|
||||
prim_list,
|
||||
state: None,
|
||||
secondary_render_task_id: None,
|
||||
@ -617,7 +592,6 @@ impl PicturePrimitive {
|
||||
};
|
||||
|
||||
let state = PictureState {
|
||||
has_non_root_coord_system: false,
|
||||
is_cacheable: true,
|
||||
local_rect_changed: false,
|
||||
map_local_to_pic,
|
||||
@ -845,6 +819,12 @@ impl PicturePrimitive {
|
||||
// local_scale.is_some();
|
||||
let establishes_raster_root = xf.has_perspective_component();
|
||||
|
||||
// TODO(gw): For now, we always raster in screen space. Soon,
|
||||
// we will be able to respect the requested raster
|
||||
// space, and/or override the requested raster root
|
||||
// if it makes sense to.
|
||||
let raster_space = RasterSpace::Screen;
|
||||
|
||||
let raster_spatial_node_index = if establishes_raster_root {
|
||||
surface_spatial_node_index
|
||||
} else {
|
||||
@ -865,6 +845,17 @@ impl PicturePrimitive {
|
||||
surface_index,
|
||||
});
|
||||
|
||||
// If we have a cache key / descriptor for this surface,
|
||||
// update any transforms it cares about.
|
||||
if let Some(ref mut surface_desc) = self.surface_desc {
|
||||
surface_desc.update(
|
||||
surface_spatial_node_index,
|
||||
raster_spatial_node_index,
|
||||
frame_context.clip_scroll_tree,
|
||||
raster_space,
|
||||
);
|
||||
}
|
||||
|
||||
surface_index
|
||||
}
|
||||
None => {
|
||||
@ -1054,34 +1045,53 @@ impl PicturePrimitive {
|
||||
let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0;
|
||||
let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
|
||||
|
||||
// The clipped field is the part of the picture that is visible
|
||||
// on screen. The unclipped field is the screen-space rect of
|
||||
// the complete picture, if no screen / clip-chain was applied
|
||||
// (this includes the extra space for blur region). To ensure
|
||||
// that we draw a large enough part of the picture to get correct
|
||||
// blur results, inflate that clipped area by the blur range, and
|
||||
// then intersect with the total screen rect, to minimize the
|
||||
// allocation size.
|
||||
let device_rect = clipped
|
||||
.inflate(blur_range, blur_range)
|
||||
.intersection(&unclipped.to_i32())
|
||||
.unwrap();
|
||||
// We need to choose whether to cache this picture, or draw
|
||||
// it into a temporary render target each frame. If we draw
|
||||
// it into a persistently cached texture, then we want to
|
||||
// draw the whole picture, without clipping it to the screen
|
||||
// dimensions, so that it can be reused as it scrolls into
|
||||
// view etc. However, if the unclipped size of the surface is
|
||||
// too big, then it will be very expensive to draw, and may
|
||||
// even be bigger than the maximum hardware render target
|
||||
// size. In these cases, it's probably best to not cache the
|
||||
// picture, and just draw a minimal portion of the picture
|
||||
// (clipped to screen bounds) to a temporary target each frame.
|
||||
|
||||
let uv_rect_kind = calculate_uv_rect_kind(
|
||||
&pic_rect,
|
||||
&transform,
|
||||
&device_rect,
|
||||
frame_context.device_pixel_scale,
|
||||
);
|
||||
// TODO(gw): This size is quite arbitrary - we should do some
|
||||
// profiling / telemetry to see when it makes sense
|
||||
// to cache a picture.
|
||||
const MAX_CACHE_SIZE: f32 = 2048.0;
|
||||
let too_big_to_cache = unclipped.size.width > MAX_CACHE_SIZE ||
|
||||
unclipped.size.height > MAX_CACHE_SIZE;
|
||||
|
||||
// If we are drawing a blur that has primitives or clips that contain
|
||||
// a complex coordinate system, don't bother caching them (for now).
|
||||
// It's likely that they are animating and caching may not help here
|
||||
// anyway. In the future we should relax this a bit, so that we can
|
||||
// cache tasks with complex coordinate systems if we detect the
|
||||
// relevant transforms haven't changed from frame to frame.
|
||||
if pic_state_for_children.has_non_root_coord_system ||
|
||||
// If we can't create a valid cache key for this descriptor (e.g.
|
||||
// due to it referencing old non-interned style primitives), then
|
||||
// don't try to cache it.
|
||||
let has_valid_cache_key = self.surface_desc.is_some();
|
||||
|
||||
if !has_valid_cache_key ||
|
||||
too_big_to_cache ||
|
||||
!pic_state_for_children.is_cacheable {
|
||||
// The clipped field is the part of the picture that is visible
|
||||
// on screen. The unclipped field is the screen-space rect of
|
||||
// the complete picture, if no screen / clip-chain was applied
|
||||
// (this includes the extra space for blur region). To ensure
|
||||
// that we draw a large enough part of the picture to get correct
|
||||
// blur results, inflate that clipped area by the blur range, and
|
||||
// then intersect with the total screen rect, to minimize the
|
||||
// allocation size.
|
||||
let device_rect = clipped
|
||||
.inflate(blur_range, blur_range)
|
||||
.intersection(&unclipped.to_i32())
|
||||
.unwrap();
|
||||
|
||||
let uv_rect_kind = calculate_uv_rect_kind(
|
||||
&pic_rect,
|
||||
&transform,
|
||||
&device_rect,
|
||||
frame_context.device_pixel_scale,
|
||||
);
|
||||
|
||||
let picture_task = RenderTask::new_picture(
|
||||
RenderTaskLocation::Dynamic(None, device_rect.size),
|
||||
unclipped.size,
|
||||
@ -1108,30 +1118,29 @@ impl PicturePrimitive {
|
||||
|
||||
PictureSurface::RenderTask(render_task_id)
|
||||
} else {
|
||||
// Get the relative clipped rect within the overall prim rect, that
|
||||
// forms part of the cache key.
|
||||
let pic_relative_render_rect = PictureIntRect::new(
|
||||
PictureIntPoint::new(
|
||||
device_rect.origin.x - unclipped.origin.x as i32,
|
||||
device_rect.origin.y - unclipped.origin.y as i32,
|
||||
),
|
||||
PictureIntSize::new(
|
||||
device_rect.size.width,
|
||||
device_rect.size.height,
|
||||
),
|
||||
);
|
||||
|
||||
// Request a render task that will cache the output in the
|
||||
// texture cache.
|
||||
let device_rect = unclipped.to_i32();
|
||||
|
||||
let uv_rect_kind = calculate_uv_rect_kind(
|
||||
&pic_rect,
|
||||
&transform,
|
||||
&device_rect,
|
||||
frame_context.device_pixel_scale,
|
||||
);
|
||||
|
||||
// TODO(gw): Probably worth changing the render task caching API
|
||||
// so that we don't need to always clone the key.
|
||||
let cache_key = self.surface_desc
|
||||
.as_ref()
|
||||
.expect("bug: no cache key for surface")
|
||||
.cache_key
|
||||
.clone();
|
||||
|
||||
let cache_item = frame_state.resource_cache.request_render_task(
|
||||
RenderTaskCacheKey {
|
||||
size: device_rect.size,
|
||||
kind: RenderTaskCacheKeyKind::Picture(PictureCacheKey {
|
||||
scene_id: frame_context.scene_id,
|
||||
picture_id: self.id,
|
||||
unclipped_size: unclipped.size.to_i32(),
|
||||
pic_relative_render_rect,
|
||||
}),
|
||||
kind: RenderTaskCacheKeyKind::Picture(cache_key),
|
||||
},
|
||||
frame_state.gpu_cache,
|
||||
frame_state.render_tasks,
|
||||
|
@ -11,7 +11,7 @@ use api::{DeviceIntSideOffsets, WorldPixel, BoxShadowClipMode, NormalBorder, Wor
|
||||
use api::{PicturePixel, RasterPixel, ColorDepth, LineStyle, LineOrientation, LayoutSizeAu, AuHelpers, LayoutVector2DAu};
|
||||
use app_units::Au;
|
||||
use border::{get_max_scale_for_border, build_border_instances, create_normal_border_prim};
|
||||
use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId, SpatialNodeIndex};
|
||||
use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex};
|
||||
use clip::{ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItem, ClipNodeCollector};
|
||||
use euclid::{TypedTransform3D, TypedRect, TypedScale};
|
||||
use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
|
||||
@ -121,6 +121,39 @@ pub enum CoordinateSpaceMapping<F, T> {
|
||||
Transform(TypedTransform3D<f32, F, T>),
|
||||
}
|
||||
|
||||
impl<F, T> CoordinateSpaceMapping<F, T> {
|
||||
pub fn new(
|
||||
ref_spatial_node_index: SpatialNodeIndex,
|
||||
target_node_index: SpatialNodeIndex,
|
||||
clip_scroll_tree: &ClipScrollTree,
|
||||
) -> Self {
|
||||
let spatial_nodes = &clip_scroll_tree.spatial_nodes;
|
||||
let ref_spatial_node = &spatial_nodes[ref_spatial_node_index.0];
|
||||
let target_spatial_node = &spatial_nodes[target_node_index.0];
|
||||
|
||||
if ref_spatial_node_index == target_node_index {
|
||||
CoordinateSpaceMapping::Local
|
||||
} else if ref_spatial_node.coordinate_system_id == target_spatial_node.coordinate_system_id {
|
||||
CoordinateSpaceMapping::ScaleOffset(
|
||||
ref_spatial_node.coordinate_system_relative_scale_offset
|
||||
.inverse()
|
||||
.accumulate(
|
||||
&target_spatial_node.coordinate_system_relative_scale_offset
|
||||
)
|
||||
)
|
||||
} else {
|
||||
let transform = clip_scroll_tree.get_relative_transform(
|
||||
target_node_index,
|
||||
ref_spatial_node_index,
|
||||
).expect("bug: should have already been culled");
|
||||
|
||||
CoordinateSpaceMapping::Transform(
|
||||
transform.with_source::<F>().with_destination::<T>()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SpaceMapper<F, T> {
|
||||
kind: CoordinateSpaceMapping<F, T>,
|
||||
@ -159,31 +192,13 @@ impl<F, T> SpaceMapper<F, T> where F: fmt::Debug {
|
||||
clip_scroll_tree: &ClipScrollTree,
|
||||
) {
|
||||
if target_node_index != self.current_target_spatial_node_index {
|
||||
let spatial_nodes = &clip_scroll_tree.spatial_nodes;
|
||||
let ref_spatial_node = &spatial_nodes[self.ref_spatial_node_index.0];
|
||||
let target_spatial_node = &spatial_nodes[target_node_index.0];
|
||||
self.current_target_spatial_node_index = target_node_index;
|
||||
|
||||
self.kind = if self.ref_spatial_node_index == target_node_index {
|
||||
CoordinateSpaceMapping::Local
|
||||
} else if ref_spatial_node.coordinate_system_id == target_spatial_node.coordinate_system_id {
|
||||
CoordinateSpaceMapping::ScaleOffset(
|
||||
ref_spatial_node.coordinate_system_relative_scale_offset
|
||||
.inverse()
|
||||
.accumulate(
|
||||
&target_spatial_node.coordinate_system_relative_scale_offset
|
||||
)
|
||||
)
|
||||
} else {
|
||||
let transform = clip_scroll_tree.get_relative_transform(
|
||||
target_node_index,
|
||||
self.ref_spatial_node_index,
|
||||
).expect("bug: should have already been culled");
|
||||
|
||||
CoordinateSpaceMapping::Transform(
|
||||
transform.with_source::<F>().with_destination::<T>()
|
||||
)
|
||||
};
|
||||
self.kind = CoordinateSpaceMapping::new(
|
||||
self.ref_spatial_node_index,
|
||||
target_node_index,
|
||||
clip_scroll_tree,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -580,13 +595,14 @@ impl PrimitiveTemplate {
|
||||
// Type definitions for interning primitives.
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
|
||||
pub struct PrimitiveDataMarker;
|
||||
|
||||
pub type PrimitiveDataStore = intern::DataStore<PrimitiveKey, PrimitiveTemplate, PrimitiveDataMarker>;
|
||||
pub type PrimitiveDataHandle = intern::Handle<PrimitiveDataMarker>;
|
||||
pub type PrimitiveDataUpdateList = intern::UpdateList<PrimitiveKey>;
|
||||
pub type PrimitiveDataInterner = intern::Interner<PrimitiveKey, PrimitiveSceneData, PrimitiveDataMarker>;
|
||||
pub type PrimitiveUid = intern::ItemUid<PrimitiveDataMarker>;
|
||||
|
||||
// Maintains a list of opacity bindings that have been collapsed into
|
||||
// the color of a single primitive. This is an important optimization
|
||||
@ -2137,8 +2153,6 @@ impl PrimitiveStore {
|
||||
Some((pic_context_for_children, mut pic_state_for_children, mut prim_list)) => {
|
||||
// Mark whether this picture has a complex coordinate system.
|
||||
let is_passthrough = pic_context_for_children.is_passthrough;
|
||||
pic_state_for_children.has_non_root_coord_system |=
|
||||
prim_context.spatial_node.coordinate_system_id != CoordinateSystemId::root();
|
||||
|
||||
self.prepare_primitives(
|
||||
&mut prim_list,
|
||||
@ -2259,8 +2273,6 @@ impl PrimitiveStore {
|
||||
);
|
||||
}
|
||||
|
||||
pic_state.has_non_root_coord_system |= clip_chain.has_non_root_coord_system;
|
||||
|
||||
prim_instance.combined_local_clip_rect = if pic_context.apply_local_clip_rect {
|
||||
clip_chain.local_clip_rect
|
||||
} else {
|
||||
@ -2455,11 +2467,6 @@ impl PrimitiveStore {
|
||||
prim_instance.spatial_node_index,
|
||||
);
|
||||
|
||||
// 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 |=
|
||||
spatial_node.coordinate_system_id != CoordinateSystemId::root();
|
||||
|
||||
pic_state.map_local_to_pic.set_target_spatial_node(
|
||||
prim_instance.spatial_node_index,
|
||||
frame_context.clip_scroll_tree,
|
||||
|
@ -477,7 +477,6 @@ struct PlainRenderBackend {
|
||||
frame_config: FrameBuilderConfig,
|
||||
documents: FastHashMap<DocumentId, DocumentView>,
|
||||
resources: PlainResources,
|
||||
last_scene_id: u64,
|
||||
}
|
||||
|
||||
/// The render backend is responsible for transforming high level display lists into
|
||||
@ -507,8 +506,6 @@ pub struct RenderBackend {
|
||||
sampler: Option<Box<AsyncPropertySampler + Send>>,
|
||||
size_of_op: Option<VoidPtrToSizeFn>,
|
||||
namespace_alloc_by_client: bool,
|
||||
|
||||
last_scene_id: u64,
|
||||
}
|
||||
|
||||
impl RenderBackend {
|
||||
@ -545,7 +542,6 @@ impl RenderBackend {
|
||||
recorder,
|
||||
sampler,
|
||||
size_of_op,
|
||||
last_scene_id: 0,
|
||||
namespace_alloc_by_client,
|
||||
}
|
||||
}
|
||||
@ -658,12 +654,6 @@ impl RenderBackend {
|
||||
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
|
||||
}
|
||||
|
||||
pub fn run(&mut self, mut profile_counters: BackendProfileCounters) {
|
||||
let mut frame_counter: u32 = 0;
|
||||
let mut keep_going = true;
|
||||
@ -1046,7 +1036,6 @@ impl RenderBackend {
|
||||
return;
|
||||
}
|
||||
|
||||
let scene_id = self.make_unique_scene_id();
|
||||
let doc = self.documents.get_mut(&document_id).unwrap();
|
||||
|
||||
if txn.should_build_scene() {
|
||||
@ -1054,7 +1043,6 @@ impl RenderBackend {
|
||||
view: doc.view.clone(),
|
||||
font_instances: self.resource_cache.get_font_instances(),
|
||||
output_pipelines: doc.output_pipelines.clone(),
|
||||
scene_id,
|
||||
});
|
||||
}
|
||||
|
||||
@ -1448,7 +1436,6 @@ impl RenderBackend {
|
||||
.map(|(id, doc)| (*id, doc.view.clone()))
|
||||
.collect(),
|
||||
resources,
|
||||
last_scene_id: self.last_scene_id,
|
||||
};
|
||||
|
||||
config.serialize(&backend, "backend");
|
||||
@ -1509,7 +1496,6 @@ impl RenderBackend {
|
||||
|
||||
let mut scenes_to_build = Vec::new();
|
||||
|
||||
let mut last_scene_id = backend.last_scene_id;
|
||||
for (id, view) in backend.documents {
|
||||
debug!("\tdocument {:?}", id);
|
||||
let scene_name = format!("scene-{}-{}", (id.0).0, id.1);
|
||||
@ -1568,8 +1554,6 @@ impl RenderBackend {
|
||||
None => true,
|
||||
};
|
||||
|
||||
last_scene_id += 1;
|
||||
|
||||
scenes_to_build.push(LoadScene {
|
||||
document_id: id,
|
||||
scene: doc.scene.clone(),
|
||||
@ -1577,7 +1561,6 @@ impl RenderBackend {
|
||||
config: self.frame_config.clone(),
|
||||
output_pipelines: doc.output_pipelines.clone(),
|
||||
font_instances: self.resource_cache.get_font_instances(),
|
||||
scene_id: last_scene_id,
|
||||
build_frame,
|
||||
doc_resources,
|
||||
});
|
||||
|
@ -21,13 +21,13 @@ use gpu_types::{BorderInstance, ImageSource, UvRectKind};
|
||||
use internal_types::{CacheTextureId, FastHashMap, LayerIndex, SavedTargetIndex};
|
||||
#[cfg(feature = "pathfinder")]
|
||||
use pathfinder_partitioner::mesh::Mesh;
|
||||
use picture::PictureCacheKey;
|
||||
use prim_store::{PictureIndex, ImageCacheKey, LineDecorationCacheKey};
|
||||
#[cfg(feature = "debugger")]
|
||||
use print_tree::{PrintTreePrinter};
|
||||
use render_backend::FrameId;
|
||||
use resource_cache::{CacheItem, ResourceCache};
|
||||
use std::{cmp, ops, usize, f32, i32};
|
||||
use surface::SurfaceCacheKey;
|
||||
use std::{cmp, ops, mem, usize, f32, i32};
|
||||
use texture_cache::{TextureCache, TextureCacheHandle, Eviction};
|
||||
use tiling::{RenderPass, RenderTargetIndex};
|
||||
use tiling::{RenderTargetKind};
|
||||
@ -1076,7 +1076,7 @@ pub enum RenderTaskCacheKeyKind {
|
||||
Image(ImageCacheKey),
|
||||
#[allow(dead_code)]
|
||||
Glyph(GpuGlyphCacheKey),
|
||||
Picture(PictureCacheKey),
|
||||
Picture(SurfaceCacheKey),
|
||||
BorderEdge(BorderEdgeCacheKey),
|
||||
BorderCorner(BorderCornerCacheKey),
|
||||
LineDecoration(LineDecorationCacheKey),
|
||||
@ -1145,19 +1145,18 @@ impl RenderTaskCache {
|
||||
// Nonetheless, we should remove stale entries
|
||||
// from here so that this hash map doesn't
|
||||
// grow indefinitely!
|
||||
let mut keys_to_remove = Vec::new();
|
||||
let cache_entries = &mut self.cache_entries;
|
||||
|
||||
for (key, handle) in &self.map {
|
||||
let entry = self.cache_entries.get(handle);
|
||||
if !texture_cache.is_allocated(&entry.handle) {
|
||||
keys_to_remove.push(key.clone())
|
||||
self.map.retain(|_, handle| {
|
||||
let retain = texture_cache.is_allocated(
|
||||
&cache_entries.get(handle).handle,
|
||||
);
|
||||
if !retain {
|
||||
let handle = mem::replace(handle, FreeListHandle::invalid());
|
||||
cache_entries.free(handle);
|
||||
}
|
||||
}
|
||||
|
||||
for key in &keys_to_remove {
|
||||
let handle = self.map.remove(key).unwrap();
|
||||
self.cache_entries.free(handle);
|
||||
}
|
||||
retain
|
||||
});
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
|
@ -97,14 +97,12 @@ pub struct SceneRequest {
|
||||
pub view: DocumentView,
|
||||
pub font_instances: FontInstanceMap,
|
||||
pub output_pipelines: FastHashSet<PipelineId>,
|
||||
pub scene_id: u64,
|
||||
}
|
||||
|
||||
#[cfg(feature = "replay")]
|
||||
pub struct LoadScene {
|
||||
pub document_id: DocumentId,
|
||||
pub scene: Scene,
|
||||
pub scene_id: u64,
|
||||
pub output_pipelines: FastHashSet<PipelineId>,
|
||||
pub font_instances: FontInstanceMap,
|
||||
pub view: DocumentView,
|
||||
@ -330,7 +328,6 @@ impl SceneBuilder {
|
||||
&item.output_pipelines,
|
||||
&self.config,
|
||||
&mut new_scene,
|
||||
item.scene_id,
|
||||
&mut self.picture_id_generator,
|
||||
&mut item.doc_resources,
|
||||
);
|
||||
@ -435,7 +432,6 @@ impl SceneBuilder {
|
||||
&request.output_pipelines,
|
||||
&self.config,
|
||||
&mut new_scene,
|
||||
request.scene_id,
|
||||
&mut self.picture_id_generator,
|
||||
&mut doc.resources,
|
||||
);
|
||||
|
331
gfx/webrender/src/surface.rs
Normal file
331
gfx/webrender/src/surface.rs
Normal file
@ -0,0 +1,331 @@
|
||||
/* 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::{LayoutPixel, PicturePixel, RasterSpace};
|
||||
use clip::{ClipChainId, ClipStore, ClipUid};
|
||||
use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex};
|
||||
use euclid::TypedTransform3D;
|
||||
use internal_types::FastHashSet;
|
||||
use prim_store::{CoordinateSpaceMapping, PrimitiveUid, PrimitiveInstance, PrimitiveInstanceKind};
|
||||
use std::hash;
|
||||
use util::ScaleOffset;
|
||||
|
||||
/*
|
||||
|
||||
Notes for future implementation work on surface caching:
|
||||
|
||||
State that can affect the contents of a cached surface:
|
||||
|
||||
Primitives
|
||||
These are handled by the PrimitiveUid value. The structure interning
|
||||
code during scene building guarantees that each PrimitiveUid will
|
||||
represent a unique identifier for the content of this primitive.
|
||||
Clip chains
|
||||
Similarly, the ClipUid value uniquely identifies the contents of
|
||||
a clip node.
|
||||
Transforms
|
||||
Each picture contains a list of transforms that affect the content
|
||||
of the picture itself. The value of the surface transform relative
|
||||
to the raster root transform is only relevant if the picture is
|
||||
being rasterized in screen-space.
|
||||
External images
|
||||
An external image (e.g. video) can change the contents of a picture
|
||||
without a scene build occurring. We don't need to handle this yet,
|
||||
but once images support interning and caching, we'll need to include
|
||||
a list of external image dependencies in the cache key.
|
||||
Property animation
|
||||
Transform animations are handled by the transforms case above. We don't
|
||||
need to handle opacity animations yet, since the interning and picture
|
||||
caching doesn't support images and / or solid rects. Once those
|
||||
primitives are ported, we'll need a list of property animation keys
|
||||
that a surface depends on.
|
||||
|
||||
*/
|
||||
|
||||
// Matches the definition of SK_ScalarNearlyZero in Skia.
|
||||
// TODO(gw): Some testing to see what's reasonable for this value
|
||||
// to avoid invalidating the cache for minor changes.
|
||||
const QUANTIZE_SCALE: f32 = 4096.0;
|
||||
|
||||
fn quantize(value: f32) -> f32 {
|
||||
(value * QUANTIZE_SCALE).round() / QUANTIZE_SCALE
|
||||
}
|
||||
|
||||
/// A quantized, hashable version of util::ScaleOffset that
|
||||
/// can be used as a cache key.
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct ScaleOffsetKey {
|
||||
scale_x: f32,
|
||||
scale_y: f32,
|
||||
offset_x: f32,
|
||||
offset_y: f32,
|
||||
}
|
||||
|
||||
impl ScaleOffsetKey {
|
||||
fn new(scale_offset: &ScaleOffset) -> Self {
|
||||
// TODO(gw): Since these are quantized, it might make sense in the future to
|
||||
// convert these to ints to remove the need for custom hash impl.
|
||||
ScaleOffsetKey {
|
||||
scale_x: quantize(scale_offset.scale.x),
|
||||
scale_y: quantize(scale_offset.scale.y),
|
||||
offset_x: quantize(scale_offset.offset.x),
|
||||
offset_y: quantize(scale_offset.offset.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ScaleOffsetKey {}
|
||||
|
||||
impl hash::Hash for ScaleOffsetKey {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
self.scale_x.to_bits().hash(state);
|
||||
self.scale_y.to_bits().hash(state);
|
||||
self.offset_x.to_bits().hash(state);
|
||||
self.offset_y.to_bits().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// A quantized, hashable version of PictureTransform that
|
||||
/// can be used as a cache key.
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct MatrixKey {
|
||||
values: [f32; 16],
|
||||
}
|
||||
|
||||
impl MatrixKey {
|
||||
fn new<Src, Dst>(transform: &TypedTransform3D<f32, Src, Dst>) -> Self {
|
||||
let mut values = transform.to_row_major_array();
|
||||
|
||||
// TODO(gw): Since these are quantized, it might make sense in the future to
|
||||
// convert these to ints to remove the need for custom hash impl.
|
||||
for value in &mut values {
|
||||
*value = quantize(*value);
|
||||
}
|
||||
|
||||
MatrixKey {
|
||||
values,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for MatrixKey {}
|
||||
|
||||
impl hash::Hash for MatrixKey {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
for value in &self.values {
|
||||
value.to_bits().hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A quantized, hashable version of CoordinateSpaceMapping that
|
||||
/// can be used as a cache key.
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
#[derive(Debug, PartialEq, Clone, Hash, Eq)]
|
||||
pub enum TransformKey {
|
||||
Local,
|
||||
ScaleOffset(ScaleOffsetKey),
|
||||
Transform(MatrixKey),
|
||||
}
|
||||
|
||||
impl TransformKey {
|
||||
pub fn local() -> Self {
|
||||
TransformKey::Local
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T> From<CoordinateSpaceMapping<F, T>> for TransformKey {
|
||||
/// Construct a transform cache key from a coordinate space mapping.
|
||||
fn from(mapping: CoordinateSpaceMapping<F, T>) -> TransformKey {
|
||||
match mapping {
|
||||
CoordinateSpaceMapping::Local => {
|
||||
TransformKey::Local
|
||||
}
|
||||
CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => {
|
||||
TransformKey::ScaleOffset(ScaleOffsetKey::new(scale_offset))
|
||||
}
|
||||
CoordinateSpaceMapping::Transform(ref transform) => {
|
||||
TransformKey::Transform(MatrixKey::new(transform))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This key uniquely identifies the contents of a cached
|
||||
/// picture surface.
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
||||
pub struct SurfaceCacheKey {
|
||||
/// The list of primitives that are part of this surface.
|
||||
/// The uid uniquely identifies the content of the primitive.
|
||||
pub primitive_ids: Vec<PrimitiveUid>,
|
||||
/// The list of clips that affect the primitives on this surface.
|
||||
/// The uid uniquely identifies the content of the clip.
|
||||
pub clip_ids: Vec<ClipUid>,
|
||||
/// A list of transforms that can affect the contents of primitives
|
||||
/// and/or clips on this picture surface.
|
||||
pub transforms: Vec<TransformKey>,
|
||||
/// Information about the transform of the picture surface itself. If we are
|
||||
/// drawing in screen-space, then the value of this affects the contents
|
||||
/// of the cached surface. If we're drawing in local space, then the transform
|
||||
/// of the surface in its parent is not relevant to the contents.
|
||||
pub raster_transform: TransformKey,
|
||||
}
|
||||
|
||||
/// A descriptor for the contents of a picture surface.
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct SurfaceDescriptor {
|
||||
/// The cache key identifies the contents or primitives, clips and the current
|
||||
/// state of relevant transforms.
|
||||
pub cache_key: SurfaceCacheKey,
|
||||
|
||||
/// The spatial nodes array is used to update the cache key each frame, without
|
||||
/// relying on the value of a spatial node index (these may change, if other parts of the
|
||||
/// display list result in a different clip-scroll tree).
|
||||
pub spatial_nodes: Vec<SpatialNodeIndex>,
|
||||
}
|
||||
|
||||
impl SurfaceDescriptor {
|
||||
/// Construct a new surface descriptor for this list of primitives.
|
||||
/// This method is fallible - it will return None if this picture
|
||||
/// contains primitives that can't currently be cached safely.
|
||||
pub fn new(
|
||||
prim_instances: &[PrimitiveInstance],
|
||||
pic_spatial_node_index: SpatialNodeIndex,
|
||||
clip_store: &ClipStore,
|
||||
) -> Option<Self> {
|
||||
let mut relevant_spatial_nodes = FastHashSet::default();
|
||||
let mut primitive_ids = Vec::new();
|
||||
let mut clip_ids = Vec::new();
|
||||
|
||||
for prim_instance in prim_instances {
|
||||
// If the prim has the same spatial node as the surface,
|
||||
// then the content can't move relative to it, so we don't
|
||||
// care if the transform changes.
|
||||
if pic_spatial_node_index != prim_instance.spatial_node_index {
|
||||
relevant_spatial_nodes.insert(prim_instance.spatial_node_index);
|
||||
}
|
||||
|
||||
// Collect clip node transforms that we care about.
|
||||
let mut clip_chain_id = prim_instance.clip_chain_id;
|
||||
while clip_chain_id != ClipChainId::NONE {
|
||||
let clip_chain_node = &clip_store.clip_chain_nodes[clip_chain_id.0 as usize];
|
||||
|
||||
// TODO(gw): This needs to be a bit more careful once we create
|
||||
// descriptors for pictures that might be pass-through.
|
||||
|
||||
// Ignore clip chain nodes that will be handled by the clip node collector.
|
||||
if clip_chain_node.spatial_node_index > pic_spatial_node_index {
|
||||
relevant_spatial_nodes.insert(prim_instance.spatial_node_index);
|
||||
|
||||
clip_ids.push(clip_chain_node.handle.uid());
|
||||
}
|
||||
|
||||
clip_chain_id = clip_chain_node.parent_clip_chain_id;
|
||||
}
|
||||
|
||||
// For now, we only handle interned primitives. If we encounter
|
||||
// a legacy primitive or picture, then fail to create a cache
|
||||
// descriptor.
|
||||
match prim_instance.kind {
|
||||
PrimitiveInstanceKind::Picture { .. } |
|
||||
PrimitiveInstanceKind::LegacyPrimitive { .. } => {
|
||||
return None;
|
||||
}
|
||||
PrimitiveInstanceKind::LineDecoration { .. } |
|
||||
PrimitiveInstanceKind::TextRun { .. } |
|
||||
PrimitiveInstanceKind::Clear => {}
|
||||
}
|
||||
|
||||
// Record the unique identifier for the content represented
|
||||
// by this primitive.
|
||||
primitive_ids.push(prim_instance.prim_data_handle.uid());
|
||||
}
|
||||
|
||||
// Get a list of spatial nodes that are relevant for the contents
|
||||
// of this picture. Sort them to ensure that for a given clip-scroll
|
||||
// tree, we end up with the same transform ordering.
|
||||
let mut spatial_nodes: Vec<SpatialNodeIndex> = relevant_spatial_nodes
|
||||
.into_iter()
|
||||
.collect();
|
||||
spatial_nodes.sort();
|
||||
|
||||
// Create the array of transform values that gets built each
|
||||
// frame during update.
|
||||
let transforms = vec![TransformKey::local(); spatial_nodes.len()];
|
||||
|
||||
let cache_key = SurfaceCacheKey {
|
||||
primitive_ids,
|
||||
clip_ids,
|
||||
transforms,
|
||||
raster_transform: TransformKey::local(),
|
||||
};
|
||||
|
||||
Some(SurfaceDescriptor {
|
||||
cache_key,
|
||||
spatial_nodes,
|
||||
})
|
||||
}
|
||||
|
||||
/// Update the transforms for this cache key, by extracting the current
|
||||
/// values from the clip-scroll tree state.
|
||||
pub fn update(
|
||||
&mut self,
|
||||
surface_spatial_node_index: SpatialNodeIndex,
|
||||
raster_spatial_node_index: SpatialNodeIndex,
|
||||
clip_scroll_tree: &ClipScrollTree,
|
||||
raster_space: RasterSpace,
|
||||
) {
|
||||
// Update the state of the transform for compositing this picture.
|
||||
self.cache_key.raster_transform = match raster_space {
|
||||
RasterSpace::Screen => {
|
||||
// In general cases, if we're rasterizing a picture in screen space, then the
|
||||
// value of the surface spatial node will affect the contents of the picture
|
||||
// itself. However, if the surface and raster spatial nodes are in the same
|
||||
// coordinate system (which is the common case!) then we are effectively drawing
|
||||
// in a local space anyway, so don't care about that transform for the purposes
|
||||
// of validating the surface cache contents.
|
||||
let raster_spatial_node = &clip_scroll_tree.spatial_nodes[raster_spatial_node_index.0];
|
||||
let surface_spatial_node = &clip_scroll_tree.spatial_nodes[surface_spatial_node_index.0];
|
||||
|
||||
let mut key = CoordinateSpaceMapping::<LayoutPixel, PicturePixel>::new(
|
||||
raster_spatial_node_index,
|
||||
surface_spatial_node_index,
|
||||
clip_scroll_tree,
|
||||
).into();
|
||||
|
||||
if let TransformKey::ScaleOffset(ref mut key) = key {
|
||||
if raster_spatial_node.coordinate_system_id == surface_spatial_node.coordinate_system_id {
|
||||
key.offset_x = 0.0;
|
||||
key.offset_y = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
key
|
||||
}
|
||||
RasterSpace::Local(..) => {
|
||||
TransformKey::local()
|
||||
}
|
||||
};
|
||||
|
||||
// Update the state of any relevant transforms for this picture.
|
||||
for (spatial_node_index, transform) in self.spatial_nodes
|
||||
.iter()
|
||||
.zip(self.cache_key.transforms.iter_mut())
|
||||
{
|
||||
*transform = CoordinateSpaceMapping::<LayoutPixel, PicturePixel>::new(
|
||||
raster_spatial_node_index,
|
||||
*spatial_node_index,
|
||||
clip_scroll_tree,
|
||||
).into();
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +1 @@
|
||||
6e445b0422075f66be4a2009745cad3fefe3429f
|
||||
b8829189cfc1769550c9ab4a4bb994e28621f009
|
||||
|
Loading…
Reference in New Issue
Block a user