Skip to content

Separate interning text run #3369

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Dec 6, 2018
146 changes: 75 additions & 71 deletions webrender/src/batch.rs

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion webrender/src/clip.rs
Original file line number Diff line number Diff line change
@@ -104,7 +104,6 @@ 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)]
296 changes: 200 additions & 96 deletions webrender/src/display_list_flattener.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion webrender/src/frame_builder.rs
Original file line number Diff line number Diff line change
@@ -275,7 +275,7 @@ impl FrameBuilder {
&frame_context,
resource_cache,
gpu_cache,
&resources.prim_data_store,
resources,
&self.clip_store,
&mut retained_tiles,
);
94 changes: 69 additions & 25 deletions webrender/src/intern.rs
Original file line number Diff line number Diff line change
@@ -2,11 +2,13 @@
* 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::LayoutPrimitiveInfo;
use internal_types::FastHashMap;
use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;
use std::{mem, ops, u64};
use std::sync::atomic::{AtomicUsize, Ordering};
use util::VecHelper;

/*
@@ -64,26 +66,37 @@ pub struct UpdateList<S> {
data: Vec<S>,
}

lazy_static! {
static ref NEXT_UID: AtomicUsize = AtomicUsize::new(0);
}

/// A globally, unique identifier
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub struct ItemUid<T> {
pub struct ItemUid {
uid: usize,
_marker: PhantomData<T>,
}

impl ItemUid {
pub fn next_uid() -> ItemUid {
let uid = NEXT_UID.fetch_add(1, Ordering::Relaxed);
ItemUid { uid }
}
}

#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Copy, Clone)]
pub struct Handle<T> {
pub struct Handle<M: Copy> {
index: u32,
epoch: Epoch,
uid: ItemUid<T>,
_marker: PhantomData<T>,
uid: ItemUid,
_marker: PhantomData<M>,
}

impl <T> Handle<T> where T: Copy {
pub fn uid(&self) -> ItemUid<T> {
impl <M> Handle<M> where M: Copy {
pub fn uid(&self) -> ItemUid {
self.uid
}
}
@@ -122,16 +135,19 @@ pub struct DataStore<S, T, M> {
_marker: PhantomData<M>,
}

impl<S, T, M> DataStore<S, T, M> where S: Debug, T: From<S>, M: Debug {
/// Construct a new data store
pub fn new() -> Self {
impl<S, T, M> ::std::default::Default for DataStore<S, T, M> where S: Debug, T: From<S>, M: Debug
{
fn default() -> Self {
DataStore {
items: Vec::new(),
_source: PhantomData,
_marker: PhantomData,
}
}
}

impl<S, T, M> DataStore<S, T, M> where S: Debug, T: From<S>, M: Debug
{
/// Apply any updates from the scene builder thread to
/// this data store.
pub fn apply_updates(
@@ -160,7 +176,9 @@ impl<S, T, M> DataStore<S, T, M> where S: Debug, T: From<S>, M: Debug {
}

/// Retrieve an item from the store via handle
impl<S, T, M> ops::Index<Handle<M>> for DataStore<S, T, M> {
impl<S, T, M> ops::Index<Handle<M>> for DataStore<S, T, M>
where M: Copy
{
type Output = T;
fn index(&self, handle: Handle<M>) -> &T {
let item = &self.items[handle.index as usize];
@@ -171,7 +189,10 @@ impl<S, T, M> ops::Index<Handle<M>> for DataStore<S, T, M> {

/// Retrieve a mutable item from the store via handle
/// Retrieve an item from the store via handle
impl<S, T, M> ops::IndexMut<Handle<M>> for DataStore<S, T, M> {
impl<S, T, M> ops::IndexMut<Handle<M>> for DataStore<S, T, M>
where
M: Copy
{
fn index_mut(&mut self, handle: Handle<M>) -> &mut T {
let item = &mut self.items[handle.index as usize];
assert_eq!(item.epoch, handle.epoch);
@@ -186,7 +207,11 @@ impl<S, T, M> ops::IndexMut<Handle<M>> for DataStore<S, T, M> {
/// an update list of additions / removals.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct Interner<S : Eq + Hash + Clone + Debug, D, M> {
pub struct Interner<S, D, M>
where
S: Eq + Hash + Clone + Debug,
M: Copy
{
/// Uniquely map an interning key to a handle
map: FastHashMap<S, Handle<M>>,
/// List of free slots in the data store for re-use.
@@ -197,27 +222,33 @@ pub struct Interner<S : Eq + Hash + Clone + Debug, D, M> {
update_data: Vec<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>>,
}

impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + Debug {
/// Construct a new interner
pub fn new() -> Self {
impl<S, D, M> ::std::default::Default for Interner<S, D, M>
where
S: Eq + Hash + Clone + Debug,
M: Copy + Debug
{
fn default() -> Self {
Interner {
map: FastHashMap::default(),
free_list: Vec::new(),
updates: Vec::new(),
update_data: Vec::new(),
current_epoch: Epoch(1),
next_uid: 0,
local_data: Vec::new(),
}
}
}

impl<S, D, M> Interner<S, D, M>
where
S: Eq + Hash + Clone + Debug,
M: Copy + Debug
{
/// Intern a data structure, and return a handle to
/// that data. The handle can then be stored in the
/// frame builder, and safely accessed via the data
@@ -268,17 +299,13 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De
let handle = Handle {
index: index as u32,
epoch: self.current_epoch,
uid: ItemUid {
uid: self.next_uid,
_marker: PhantomData,
},
uid: ItemUid::next_uid(),
_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.
@@ -338,11 +365,28 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De
}

/// Retrieve the local data for an item from the interner via handle
impl<S, D, M> ops::Index<Handle<M>> for Interner<S, D, M> where S: Eq + Clone + Hash + Debug, M: Copy + Debug {
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 as usize];
assert_eq!(item.epoch, handle.epoch);
&item.data
}
}

/// Implement `Internable` for a type that wants participate in interning.
///
/// see DisplayListFlattener::add_interned_primitive<P>
pub trait Internable {
type Marker: Copy + Debug;
type Source: Eq + Hash + Clone + Debug;
type StoreData: From<Self::Source>;
type InternData;

/// Build a new key from self with `info`.
fn build_key(self, info: &LayoutPrimitiveInfo) -> Self::Source;
}
4 changes: 1 addition & 3 deletions webrender/src/lib.rs
Original file line number Diff line number Diff line change
@@ -64,9 +64,6 @@ extern crate serde;
#[macro_use]
extern crate thread_profiler;

#[macro_use]
mod storage;

mod batch;
mod border;
mod box_shadow;
@@ -112,6 +109,7 @@ mod scene_builder;
mod segment;
mod shade;
mod spatial_node;
mod storage;
mod surface;
mod texture_allocator;
mod texture_cache;
60 changes: 41 additions & 19 deletions webrender/src/picture.rs
Original file line number Diff line number Diff line change
@@ -7,24 +7,27 @@ use api::{DeviceIntRect, DevicePoint, LayoutRect, PictureToRasterTransform, Layo
use api::{DevicePixelScale, RasterRect, RasterSpace, PictureSize, DeviceIntPoint, ColorF, ImageKey, DirtyRect};
use api::{PicturePixel, RasterPixel, WorldPixel, WorldRect, ImageFormat, ImageDescriptor};
use box_shadow::{BLUR_SAMPLE_SCALE};
use clip::{ClipNodeCollector, ClipStore, ClipChainId, ClipChainNode, ClipUid};
use clip::{ClipNodeCollector, ClipStore, ClipChainId, ClipChainNode};
use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex};
use device::TextureFilter;
use euclid::{TypedScale, vec3, TypedRect, TypedPoint2D, TypedSize2D};
use euclid::approxeq::ApproxEq;
use intern::ItemUid;
use internal_types::{FastHashMap, PlaneSplitter};
use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PictureContext};
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
use gpu_types::{TransformPalette, TransformPaletteId, UvRectKind};
use internal_types::FastHashSet;
use plane_split::{Clipper, Polygon, Splitter};
use prim_store::{PictureIndex, PrimitiveInstance, SpaceMapper, VisibleFace, PrimitiveInstanceKind, PrimitiveUid};
use prim_store::{get_raster_rects, PrimitiveDataInterner, PrimitiveDataStore, CoordinateSpaceMapping};
use prim_store::{PictureIndex, PrimitiveInstance, SpaceMapper, VisibleFace, PrimitiveInstanceKind};
use prim_store::{get_raster_rects, CoordinateSpaceMapping};
use prim_store::{OpacityBindingStorage, PrimitiveTemplateKind, ImageInstanceStorage, OpacityBindingIndex, SizeKey};
use render_backend::FrameResources;
use render_task::{ClearMode, RenderTask, RenderTaskCacheEntryHandle, TileBlit};
use render_task::{RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation};
use resource_cache::ResourceCache;
use scene::{FilterOpHelpers, SceneProperties};
use scene_builder::DocumentResources;
use smallvec::SmallVec;
use surface::{SurfaceDescriptor, TransformKey};
use std::{mem, ops};
@@ -236,11 +239,11 @@ pub struct TileTransformIndex(u32);
pub struct TileDescriptor {
/// List of primitive unique identifiers. The uid is guaranteed
/// to uniquely describe the content of the primitive.
pub prim_uids: Vec<PrimitiveUid>,
pub prim_uids: Vec<ItemUid>,

/// List of clip node unique identifiers. The uid is guaranteed
/// to uniquely describe the content of the clip node.
pub clip_uids: Vec<ClipUid>,
pub clip_uids: Vec<ItemUid>,

/// List of local tile transform ids that are used to position
/// the primitive and clip items above.
@@ -600,7 +603,7 @@ impl TileCache {
prim_instance: &PrimitiveInstance,
surface_spatial_node_index: SpatialNodeIndex,
clip_scroll_tree: &ClipScrollTree,
prim_data_store: &PrimitiveDataStore,
resources: &FrameResources,
clip_chain_nodes: &[ClipChainNode],
pictures: &[PicturePrimitive],
resource_cache: &ResourceCache,
@@ -612,7 +615,7 @@ impl TileCache {
clip_scroll_tree,
);

let prim_data = &prim_data_store[prim_instance.prim_data_handle];
let prim_data = &resources.as_common_data(&prim_instance);

// Map the primitive local rect into the picture space.
// TODO(gw): We should maybe store this in the primitive template
@@ -648,18 +651,18 @@ impl TileCache {
// Build the list of resources that this primitive has dependencies on.
let mut opacity_bindings: SmallVec<[PropertyBindingId; 4]> = SmallVec::new();
let mut clip_chain_spatial_nodes: SmallVec<[SpatialNodeIndex; 8]> = SmallVec::new();
let mut clip_chain_uids: SmallVec<[ClipUid; 8]> = SmallVec::new();
let mut clip_chain_uids: SmallVec<[ItemUid; 8]> = SmallVec::new();
let mut image_keys: SmallVec<[ImageKey; 8]> = SmallVec::new();
let mut current_clip_chain_id = prim_instance.clip_chain_id;

// Some primitives can not be cached (e.g. external video images)
let is_cacheable = prim_instance.is_cacheable(
prim_data_store,
&resources.prim_data_store,
resource_cache,
);

match prim_instance.kind {
PrimitiveInstanceKind::Picture { pic_index } => {
PrimitiveInstanceKind::Picture { pic_index,.. } => {
// Pictures can depend on animated opacity bindings.
let pic = &pictures[pic_index.0];
if let Some(PictureCompositeMode::Filter(FilterOp::Opacity(binding, _))) = pic.requested_composite_mode {
@@ -678,7 +681,8 @@ impl TileCache {
}
}
}
PrimitiveInstanceKind::Image { image_instance_index, .. } => {
PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => {
let prim_data = &resources.prim_data_store[data_handle];
let image_instance = &image_instances[image_instance_index];
let opacity_binding_index = image_instance.opacity_binding_index;

@@ -700,7 +704,8 @@ impl TileCache {
}
}
}
PrimitiveInstanceKind::YuvImage { .. } => {
PrimitiveInstanceKind::YuvImage { data_handle, .. } => {
let prim_data = &resources.prim_data_store[data_handle];
match prim_data.kind {
PrimitiveTemplateKind::YuvImage { ref yuv_key, .. } => {
image_keys.extend_from_slice(yuv_key);
@@ -712,7 +717,7 @@ impl TileCache {
}
PrimitiveInstanceKind::TextRun { .. } |
PrimitiveInstanceKind::LineDecoration { .. } |
PrimitiveInstanceKind::Clear |
PrimitiveInstanceKind::Clear { .. } |
PrimitiveInstanceKind::NormalBorder { .. } |
PrimitiveInstanceKind::LinearGradient { .. } |
PrimitiveInstanceKind::RadialGradient { .. } |
@@ -777,7 +782,7 @@ impl TileCache {
}

// Update the tile descriptor, used for tile comparison during scene swaps.
tile.descriptor.prim_uids.push(prim_instance.prim_data_handle.uid());
tile.descriptor.prim_uids.push(prim_instance.uid());
tile.descriptor.clip_uids.extend_from_slice(&clip_chain_uids);
}
}
@@ -1258,7 +1263,7 @@ impl PrimitiveList {
/// significantly faster.
pub fn new(
mut prim_instances: Vec<PrimitiveInstance>,
prim_interner: &PrimitiveDataInterner,
resources: &DocumentResources
) -> Self {
let mut pictures = SmallVec::new();
let mut clusters_map = FastHashMap::default();
@@ -1271,7 +1276,7 @@ impl PrimitiveList {
// Check if this primitive is a picture. In future we should
// remove this match and embed this info directly in the primitive instance.
let is_pic = match prim_instance.kind {
PrimitiveInstanceKind::Picture { pic_index } => {
PrimitiveInstanceKind::Picture { pic_index, .. } => {
pictures.push(pic_index);
true
}
@@ -1280,9 +1285,26 @@ impl PrimitiveList {
}
};

let prim_data = match prim_instance.kind {
PrimitiveInstanceKind::Picture { data_handle, .. } |
PrimitiveInstanceKind::LineDecoration { data_handle, .. } |
PrimitiveInstanceKind::NormalBorder { data_handle, .. } |
PrimitiveInstanceKind::ImageBorder { data_handle, .. } |
PrimitiveInstanceKind::Rectangle { data_handle, .. } |
PrimitiveInstanceKind::YuvImage { data_handle, .. } |
PrimitiveInstanceKind::Image { data_handle, .. } |
PrimitiveInstanceKind::LinearGradient { data_handle, .. } |
PrimitiveInstanceKind::RadialGradient { data_handle, ..} |
PrimitiveInstanceKind::Clear { data_handle, .. } => {
&resources.prim_interner[data_handle]
}
PrimitiveInstanceKind::TextRun { data_handle, .. } => {
&resources.text_run_interner[data_handle]
}
};

// Get the key for the cluster that this primitive should
// belong to.
let prim_data = &prim_interner[prim_instance.prim_data_handle];
let key = PrimitiveClusterKey {
spatial_node_index: prim_instance.spatial_node_index,
is_backface_visible: prim_data.is_backface_visible,
@@ -1908,7 +1930,7 @@ impl PicturePrimitive {
state: &mut PictureUpdateState,
frame_context: &FrameBuildingContext,
resource_cache: &mut ResourceCache,
prim_data_store: &PrimitiveDataStore,
resources: &FrameResources,
pictures: &[PicturePrimitive],
clip_store: &ClipStore,
opacity_binding_store: &OpacityBindingStorage,
@@ -1926,7 +1948,7 @@ impl PicturePrimitive {
prim_instance,
surface_spatial_node_index,
&frame_context.clip_scroll_tree,
prim_data_store,
resources,
&clip_store.clip_chain_nodes,
pictures,
resource_cache,
766 changes: 374 additions & 392 deletions webrender/src/prim_store.rs → webrender/src/prim_store/mod.rs

Large diffs are not rendered by default.

329 changes: 329 additions & 0 deletions webrender/src/prim_store/text_run.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,329 @@
/* 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::{AuHelpers, ColorF, DevicePixelScale, GlyphInstance, LayoutPrimitiveInfo};
use api::{LayoutToWorldTransform, LayoutVector2DAu, RasterSpace};
use api::Shadow;
use display_list_flattener::{AsInstanceKind, CreateShadow, IsVisible};
use frame_builder::{FrameBuildingState, PictureContext};
use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT};
use gpu_cache::GpuCache;
use intern;
use prim_store::{PrimitiveOpacity, PrimitiveSceneData, PrimitiveScratchBuffer};
use prim_store::{PrimitiveStore, PrimKeyCommonData, PrimTemplateCommonData};
use render_task::{RenderTaskTree};
use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
use resource_cache::{ResourceCache};
use tiling::SpecialRenderPasses;
use util::{MatrixHelpers};
use prim_store::PrimitiveInstanceKind;
use std::ops;
use storage;

/// A run of glyphs, with associated font information.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct TextRunKey {
pub common: PrimKeyCommonData,
pub font: FontInstance,
pub offset: LayoutVector2DAu,
pub glyphs: Vec<GlyphInstance>,
pub shadow: bool,
}

impl TextRunKey {
pub fn new(info: &LayoutPrimitiveInfo, text_run: TextRun) -> Self {
TextRunKey {
common: PrimKeyCommonData::with_info(info),
font: text_run.font,
offset: text_run.offset.into(),
glyphs: text_run.glyphs,
shadow: text_run.shadow,
}
}
}

impl AsInstanceKind<TextRunDataHandle> for TextRunKey {
/// Construct a primitive instance that matches the type
/// of primitive key.
fn as_instance_kind(
&self,
data_handle: TextRunDataHandle,
prim_store: &mut PrimitiveStore,
) -> PrimitiveInstanceKind {
let run_index = prim_store.text_runs.push(TextRunPrimitive {
used_font: self.font.clone(),
glyph_keys_range: storage::Range::empty(),
shadow: self.shadow,
});

PrimitiveInstanceKind::TextRun{ data_handle, run_index }
}
}

#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct TextRunTemplate {
pub common: PrimTemplateCommonData,
pub font: FontInstance,
pub offset: LayoutVector2DAu,
pub glyphs: Vec<GlyphInstance>,
}

impl ops::Deref for TextRunTemplate {
type Target = PrimTemplateCommonData;
fn deref(&self) -> &Self::Target {
&self.common
}
}

impl ops::DerefMut for TextRunTemplate {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.common
}
}

impl From<TextRunKey> for TextRunTemplate {
fn from(item: TextRunKey) -> Self {
let common = PrimTemplateCommonData::with_key_common(item.common);
TextRunTemplate {
common,
font: item.font,
offset: item.offset,
glyphs: item.glyphs,
}
}
}

impl TextRunTemplate {
/// Update the GPU cache for a given primitive template. This may be called multiple
/// times per frame, by each primitive reference that refers to this interned
/// template. The initial request call to the GPU cache ensures that work is only
/// done if the cache entry is invalid (due to first use or eviction).
pub fn update(
&mut self,
frame_state: &mut FrameBuildingState,
) {
self.write_prim_gpu_blocks(frame_state);
self.opacity = PrimitiveOpacity::translucent();
}

fn write_prim_gpu_blocks(
&mut self,
frame_state: &mut FrameBuildingState,
) {
if let Some(mut request) = frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) {
request.push(ColorF::from(self.font.color).premultiplied());
// this is the only case where we need to provide plain color to GPU
let bg_color = ColorF::from(self.font.bg_color);
request.push([bg_color.r, bg_color.g, bg_color.b, 1.0]);
request.push([
self.offset.x.to_f32_px(),
self.offset.y.to_f32_px(),
0.0,
0.0,
]);

let mut gpu_block = [0.0; 4];
for (i, src) in self.glyphs.iter().enumerate() {
// Two glyphs are packed per GPU block.

if (i & 1) == 0 {
gpu_block[0] = src.point.x;
gpu_block[1] = src.point.y;
} else {
gpu_block[2] = src.point.x;
gpu_block[3] = src.point.y;
request.push(gpu_block);
}
}

// Ensure the last block is added in the case
// of an odd number of glyphs.
if (self.glyphs.len() & 1) != 0 {
request.push(gpu_block);
}

assert!(request.current_used_block_num() <= MAX_VERTEX_TEXTURE_WIDTH);
}
}
}

#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub struct TextRunDataMarker;

pub type TextRunDataStore = intern::DataStore<TextRunKey, TextRunTemplate, TextRunDataMarker>;
pub type TextRunDataHandle = intern::Handle<TextRunDataMarker>;
pub type TextRunDataUpdateList = intern::UpdateList<TextRunKey>;
pub type TextRunDataInterner = intern::Interner<TextRunKey, PrimitiveSceneData, TextRunDataMarker>;

pub struct TextRun {
pub font: FontInstance,
pub offset: LayoutVector2DAu,
pub glyphs: Vec<GlyphInstance>,
pub shadow: bool,
}

impl intern::Internable for TextRun {
type Marker = TextRunDataMarker;
type Source = TextRunKey;
type StoreData = TextRunTemplate;
type InternData = PrimitiveSceneData;

/// Build a new key from self with `info`.
fn build_key(self, info: &LayoutPrimitiveInfo) -> TextRunKey {
TextRunKey::new(info, self)
}
}

impl CreateShadow for TextRun {
fn create_shadow(&self, shadow: &Shadow) -> Self {
let mut font = FontInstance {
color: shadow.color.into(),
..self.font.clone()
};
if shadow.blur_radius > 0.0 {
font.disable_subpixel_aa();
}

TextRun {
font,
glyphs: self.glyphs.clone(),
offset: self.offset + shadow.offset.to_au(),
shadow: true
}
}
}

impl IsVisible for TextRun {
fn is_visible(&self) -> bool {
self.font.color.a > 0
}
}

#[derive(Debug)]
pub struct TextRunPrimitive {
pub used_font: FontInstance,
pub glyph_keys_range: storage::Range<GlyphKey>,
pub shadow: bool,
}

impl TextRunPrimitive {
pub fn update_font_instance(
&mut self,
specified_font: &FontInstance,
device_pixel_scale: DevicePixelScale,
transform: &LayoutToWorldTransform,
allow_subpixel_aa: bool,
raster_space: RasterSpace,
) -> bool {
// Get the current font size in device pixels
let device_font_size = specified_font.size.scale_by(device_pixel_scale.0);

// Determine if rasterizing glyphs in local or screen space.
// Only support transforms that can be coerced to simple 2D transforms.
let transform_glyphs = if transform.has_perspective_component() ||
!transform.has_2d_inverse() ||
// Font sizes larger than the limit need to be scaled, thus can't use subpixels.
transform.exceeds_2d_scale(FONT_SIZE_LIMIT / device_font_size.to_f64_px()) ||
// Otherwise, ensure the font is rasterized in screen-space.
raster_space != RasterSpace::Screen {
false
} else {
true
};

// Get the font transform matrix (skew / scale) from the complete transform.
let font_transform = if transform_glyphs {
// Quantize the transform to minimize thrashing of the glyph cache.
FontTransform::from(transform).quantize()
} else {
FontTransform::identity()
};

// If the transform or device size is different, then the caller of
// this method needs to know to rebuild the glyphs.
let cache_dirty =
self.used_font.transform != font_transform ||
self.used_font.size != device_font_size;

// Construct used font instance from the specified font instance
self.used_font = FontInstance {
transform: font_transform,
size: device_font_size,
..specified_font.clone()
};

// If subpixel AA is disabled due to the backing surface the glyphs
// are being drawn onto, disable it (unless we are using the
// specifial subpixel mode that estimates background color).
if (!allow_subpixel_aa && self.used_font.bg_color.a == 0) ||
// If using local space glyphs, we don't want subpixel AA.
!transform_glyphs {
self.used_font.disable_subpixel_aa();
}

cache_dirty
}

pub fn prepare_for_render(
&mut self,
specified_font: &FontInstance,
glyphs: &[GlyphInstance],
device_pixel_scale: DevicePixelScale,
transform: &LayoutToWorldTransform,
pic_context: &PictureContext,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
special_render_passes: &mut SpecialRenderPasses,
scratch: &mut PrimitiveScratchBuffer,
) {
let cache_dirty = self.update_font_instance(
specified_font,
device_pixel_scale,
transform,
pic_context.allow_subpixel_aa,
pic_context.raster_space,
);

if self.glyph_keys_range.is_empty() || cache_dirty {
let subpx_dir = self.used_font.get_subpx_dir();

self.glyph_keys_range = scratch.glyph_keys.extend(
glyphs.iter().map(|src| {
let world_offset = self.used_font.transform.transform(&src.point);
let device_offset = device_pixel_scale.transform_point(&world_offset);
GlyphKey::new(src.index, device_offset, subpx_dir)
}));
}

resource_cache.request_glyphs(
self.used_font.clone(),
&scratch.glyph_keys[self.glyph_keys_range],
gpu_cache,
render_tasks,
special_render_passes,
);
}
}

#[test]
#[cfg(target_os = "linux")]
fn test_struct_sizes() {
use std::mem;
// The sizes of these structures are critical for performance on a number of
// talos stress tests. If you get a failure here on CI, there's two possibilities:
// (a) You made a structure smaller than it currently is. Great work! Update the
// test expectations and move on.
// (b) You made a structure larger. This is not necessarily a problem, but should only
// be done with care, and after checking if talos performance regresses badly.
assert_eq!(mem::size_of::<TextRun>(), 112, "TextRun size changed");
assert_eq!(mem::size_of::<TextRunTemplate>(), 168, "TextRunTemplate size changed");
assert_eq!(mem::size_of::<TextRunKey>(), 144, "TextRunKey size changed");
assert_eq!(mem::size_of::<TextRunPrimitive>(), 88, "TextRunPrimitive size changed");
}
35 changes: 29 additions & 6 deletions webrender/src/render_backend.rs
Original file line number Diff line number Diff line change
@@ -30,7 +30,9 @@ use frame_builder::{FrameBuilder, FrameBuilderConfig};
use gpu_cache::GpuCache;
use hit_test::{HitTest, HitTester};
use internal_types::{DebugOutput, FastHashMap, FastHashSet, RenderedDocument, ResultMsg};
use prim_store::{PrimitiveDataStore, PrimitiveScratchBuffer};
use prim_store::{PrimitiveDataStore, PrimitiveScratchBuffer, PrimitiveInstance};
use prim_store::{PrimitiveInstanceKind, PrimTemplateCommonData};
use prim_store::text_run::TextRunDataStore;
use profiler::{BackendProfileCounters, IpcProfileCounters, ResourceProfileCounters};
use record::ApiRecordingReceiver;
use renderer::{AsyncPropertySampler, PipelineInfo};
@@ -192,6 +194,7 @@ impl FrameStamp {
// between display lists.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Default)]
pub struct FrameResources {
/// The store of currently active / available clip nodes. This is kept
/// in sync with the clip interner in the scene builder for each document.
@@ -200,13 +203,32 @@ pub struct FrameResources {
/// Currently active / available primitives. Kept in sync with the
/// primitive interner in the scene builder, per document.
pub prim_data_store: PrimitiveDataStore,
pub text_run_data_store: TextRunDataStore,
}

impl FrameResources {
fn new() -> Self {
FrameResources {
clip_data_store: ClipDataStore::new(),
prim_data_store: PrimitiveDataStore::new(),
pub fn as_common_data(
&self,
prim_inst: &PrimitiveInstance
) -> &PrimTemplateCommonData {
match prim_inst.kind {
PrimitiveInstanceKind::Picture { data_handle, .. } |
PrimitiveInstanceKind::LineDecoration { data_handle, .. } |
PrimitiveInstanceKind::NormalBorder { data_handle, .. } |
PrimitiveInstanceKind::ImageBorder { data_handle, .. } |
PrimitiveInstanceKind::Rectangle { data_handle, .. } |
PrimitiveInstanceKind::YuvImage { data_handle, .. } |
PrimitiveInstanceKind::Image { data_handle, .. } |
PrimitiveInstanceKind::LinearGradient { data_handle, .. } |
PrimitiveInstanceKind::RadialGradient { data_handle, .. } |
PrimitiveInstanceKind::Clear { data_handle, .. } => {
let prim_data = &self.prim_data_store[data_handle];
&prim_data.common
}
PrimitiveInstanceKind::TextRun { data_handle, .. } => {
let prim_data = &self.text_run_data_store[data_handle];
&prim_data.common
}
}
}
}
@@ -289,7 +311,7 @@ impl Document {
hit_tester_is_valid: false,
rendered_frame_is_valid: false,
has_built_scene: false,
resources: FrameResources::new(),
resources: FrameResources::default(),
scratch: PrimitiveScratchBuffer::new(),
}
}
@@ -1183,6 +1205,7 @@ impl RenderBackend {
if let Some(updates) = doc_resource_updates {
doc.resources.clip_data_store.apply_updates(updates.clip_updates);
doc.resources.prim_data_store.apply_updates(updates.prim_updates);
doc.resources.text_run_data_store.apply_updates(updates.text_run_updates);
}

// TODO: this scroll variable doesn't necessarily mean we scrolled. It is only used
44 changes: 36 additions & 8 deletions webrender/src/scene_builder.rs
Original file line number Diff line number Diff line change
@@ -12,8 +12,11 @@ use frame_builder::{FrameBuilderConfig, FrameBuilder};
use clip::{ClipDataInterner, ClipDataUpdateList};
use clip_scroll_tree::ClipScrollTree;
use display_list_flattener::DisplayListFlattener;
use intern::{Internable, Interner};
use internal_types::{FastHashMap, FastHashSet};
use prim_store::{PrimitiveDataInterner, PrimitiveDataUpdateList, PrimitiveStoreStats};
use prim_store::{PrimitiveDataInterner, PrimitiveDataUpdateList, PrimitiveKeyKind};
use prim_store::PrimitiveStoreStats;
use prim_store::text_run::{TextRunDataInterner, TextRun, TextRunDataUpdateList};
use resource_cache::FontInstanceMap;
use render_backend::DocumentView;
use renderer::{PipelineInfo, SceneBuilderHooks};
@@ -28,6 +31,7 @@ use std::time::Duration;
pub struct DocumentResourceUpdates {
pub clip_updates: ClipDataUpdateList,
pub prim_updates: PrimitiveDataUpdateList,
pub text_run_updates: TextRunDataUpdateList,
}

/// Represents the work associated to a transaction before scene building.
@@ -159,20 +163,32 @@ pub enum SceneSwapResult {
// display lists is (a) fast (b) done during scene building.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Default)]
pub struct DocumentResources {
pub clip_interner: ClipDataInterner,
pub prim_interner: PrimitiveDataInterner,
pub text_run_interner: TextRunDataInterner,
}

impl DocumentResources {
fn new() -> Self {
DocumentResources {
clip_interner: ClipDataInterner::new(),
prim_interner: PrimitiveDataInterner::new(),
}
// Access to `DocumentResources` interners by `Internable`
pub trait InternerMut<I: Internable>
{
fn interner_mut(&mut self) -> &mut Interner<I::Source, I::InternData, I::Marker>;
}

impl InternerMut<PrimitiveKeyKind> for DocumentResources {
fn interner_mut(&mut self) -> &mut PrimitiveDataInterner {
&mut self.prim_interner
}
}

impl InternerMut<TextRun> for DocumentResources {
fn interner_mut(&mut self) -> &mut TextRunDataInterner {
&mut self.text_run_interner
}
}


// A document in the scene builder contains the current scene,
// as well as a persistent clip interner. This allows clips
// to be de-duplicated, and persisted in the GPU cache between
@@ -187,7 +203,7 @@ impl Document {
fn new(scene: Scene) -> Self {
Document {
scene,
resources: DocumentResources::new(),
resources: DocumentResources::default(),
prim_store_stats: PrimitiveStoreStats::empty(),
}
}
@@ -341,10 +357,16 @@ impl SceneBuilder {
.prim_interner
.end_frame_and_get_pending_updates();

let text_run_updates = item
.doc_resources
.text_run_interner
.end_frame_and_get_pending_updates();

doc_resource_updates = Some(
DocumentResourceUpdates {
clip_updates,
prim_updates,
text_run_updates,
}
);

@@ -453,10 +475,16 @@ impl SceneBuilder {
.prim_interner
.end_frame_and_get_pending_updates();

let text_run_updates = doc
.resources
.text_run_interner
.end_frame_and_get_pending_updates();

doc_resource_updates = Some(
DocumentResourceUpdates {
clip_updates,
prim_updates,
text_run_updates,
}
);

27 changes: 8 additions & 19 deletions webrender/src/surface.rs
Original file line number Diff line number Diff line change
@@ -3,11 +3,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use api::{LayoutPixel, PicturePixel, RasterSpace};
use clip::{ClipChainId, ClipStore, ClipUid};
use clip::{ClipChainId, ClipStore};
use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex};
use euclid::TypedTransform3D;
use intern::ItemUid;
use internal_types::FastHashSet;
use prim_store::{CoordinateSpaceMapping, PrimitiveUid, PrimitiveInstance, PrimitiveInstanceKind};
use prim_store::{CoordinateSpaceMapping, PrimitiveInstance, PrimitiveInstanceKind};
use std::hash;
use util::ScaleOffset;

@@ -165,10 +166,10 @@ impl<F, T> From<CoordinateSpaceMapping<F, T>> for TransformKey {
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>,
pub primitive_ids: Vec<ItemUid>,
/// 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>,
pub clip_ids: Vec<ItemUid>,
/// A list of transforms that can affect the contents of primitives
/// and/or clips on this picture surface.
pub transforms: Vec<TransformKey>,
@@ -235,25 +236,13 @@ impl SurfaceDescriptor {
// 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 { .. } => {
return None;
}
PrimitiveInstanceKind::Image { .. } |
PrimitiveInstanceKind::YuvImage { .. } |
PrimitiveInstanceKind::LineDecoration { .. } |
PrimitiveInstanceKind::LinearGradient { .. } |
PrimitiveInstanceKind::RadialGradient { .. } |
PrimitiveInstanceKind::TextRun { .. } |
PrimitiveInstanceKind::NormalBorder { .. } |
PrimitiveInstanceKind::Rectangle { .. } |
PrimitiveInstanceKind::ImageBorder { .. } |
PrimitiveInstanceKind::Clear => {}
if let PrimitiveInstanceKind::Picture { .. } = prim_instance.kind {
return None;
}

// Record the unique identifier for the content represented
// by this primitive.
primitive_ids.push(prim_instance.prim_data_handle.uid());
primitive_ids.push(prim_instance.uid());
}

// Get a list of spatial nodes that are relevant for the contents