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:
WR Updater Bot 2018-11-08 18:56:03 +00:00
parent 4f99ddecde
commit 93c86f325a
14 changed files with 550 additions and 200 deletions

View File

@ -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(),
})
}

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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> {

View File

@ -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
}

View File

@ -109,6 +109,7 @@ mod scene_builder;
mod segment;
mod shade;
mod spatial_node;
mod surface;
mod texture_allocator;
mod texture_cache;
mod tiling;

View File

@ -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,

View File

@ -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,

View File

@ -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,
});

View File

@ -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(

View File

@ -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,
);

View 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();
}
}
}

View File

@ -1 +1 @@
6e445b0422075f66be4a2009745cad3fefe3429f
b8829189cfc1769550c9ab4a4bb994e28621f009