| ☠☠ backed out by 65b1787817bd ☠ ☠ | |
| author | Miko Mynttinen <mikokm@gmail.com> |
| Wed, 22 Jan 2020 22:17:18 +0000 | |
| changeset 511694 | a4d9a1af297af013b60da92594942f02c955a338 |
| parent 511693 | 7cab39f4df928c46f5c745a528fd769886f870c0 |
| child 511695 | 30481c41873ab75cb2b0c49860d6b67ad7e38bf5 |
| push id | 37054 |
| push user | btara@mozilla.com |
| push date | Sat, 25 Jan 2020 09:45:27 +0000 |
| treeherder | mozilla-central@f7f534f08b48 [default view] [failures only] |
| perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
| reviewers | kvark |
| bugs | 1558926 |
| milestone | 74.0a1 |
| first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
| last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/gfx/layers/moz.build +++ b/gfx/layers/moz.build @@ -251,16 +251,17 @@ EXPORTS.mozilla.layers += [ 'SyncObject.h', 'TextureSourceProvider.h', 'TextureWrapperImage.h', 'TransactionIdAllocator.h', 'TreeTraversal.h', 'UpdateImageHelper.h', 'wr/AsyncImagePipelineManager.h', 'wr/ClipManager.h', + 'wr/DisplayItemCache.h', 'wr/IpcResourceUpdateQueue.h', 'wr/RenderRootBoundary.h', 'wr/RenderRootStateManager.h', 'wr/RenderRootTypes.h', 'wr/StackingContextHelper.h', 'wr/WebRenderBridgeChild.h', 'wr/WebRenderBridgeParent.h', 'wr/WebRenderCanvasRenderer.h', @@ -518,16 +519,17 @@ UNIFIED_SOURCES += [ 'ShareableCanvasRenderer.cpp', 'SourceSurfaceSharedData.cpp', 'SourceSurfaceVolatileData.cpp', 'SyncObject.cpp', 'TextureSourceProvider.cpp', 'TextureWrapperImage.cpp', 'wr/AsyncImagePipelineManager.cpp', 'wr/ClipManager.cpp', + 'wr/DisplayItemCache.cpp', 'wr/IpcResourceUpdateQueue.cpp', 'wr/RenderRootStateManager.cpp', 'wr/RenderRootTypes.cpp', 'wr/StackingContextHelper.cpp', 'wr/WebRenderBridgeChild.cpp', 'wr/WebRenderBridgeParent.cpp', 'wr/WebRenderCanvasRenderer.cpp', 'wr/WebRenderCommandBuilder.cpp',
new file mode 100644 --- /dev/null +++ b/gfx/layers/wr/DisplayItemCache.cpp @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "DisplayItemCache.h" + +namespace mozilla { +namespace layers { + +void DisplayItemCache::UpdateState(const bool aPartialDisplayListBuildFailed, + const wr::PipelineId& aPipelineId) { + if (!IsEnabled()) { + return; + } + + // Clear the cache if the partial display list build failed, or if the + // pipeline id changed. + const bool clearCache = + UpdatePipelineId(aPipelineId) || aPartialDisplayListBuildFailed; + + if (clearCache) { + memset(mCachedItemState.Elements(), 0, + mCachedItemState.Length() * sizeof(CacheEntry)); + mNextIndex = 0; + mFreeList.Clear(); + } + + PopulateFreeList(clearCache); +} + +void DisplayItemCache::PopulateFreeList(const bool aAddAll) { + uint16_t index = 0; + for (auto& state : mCachedItemState) { + if (aAddAll || (!state.mUsed && state.mCached)) { + // This entry contained a cached item, but was not used. + state.mCached = false; + mFreeList.AppendElement(index); + } + + state.mUsed = false; + index++; + } +} + +static bool CanCacheItem(const nsDisplayItem* aItem) { + // Only cache leaf display items that can be reused. + if (!aItem->CanBeReused()) { + return false; + } + + switch (aItem->GetType()) { + case DisplayItemType::TYPE_BACKGROUND_COLOR: + // case DisplayItemType::TYPE_TEXT: + MOZ_ASSERT(!aItem->HasChildren()); + return true; + default: + return false; + } +} + +void DisplayItemCache::MaybeStartCaching(nsPaintedDisplayItem* aItem, + wr::DisplayListBuilder& aBuilder) { + if (!IsEnabled()) { + return; + } + + Stats().AddTotal(); + + auto& index = aItem->CacheIndex(); + if (!index) { + if (!CanCacheItem(aItem)) { + // The item cannot be cached. + return; + } + + index = GetNextCacheIndex(); + if (!index) { + // The item does not fit in the cache. + return; + } + } + + // Update the current cache index, which is used by |MaybeEndCaching()| below. + MOZ_ASSERT(!mCurrentIndex); + mCurrentIndex = index; + + MOZ_ASSERT(CanCacheItem(aItem)); + MOZ_ASSERT(mCurrentIndex && CurrentCacheSize() > *mCurrentIndex); + + auto& state = mCachedItemState[*mCurrentIndex]; + MOZ_ASSERT(!state.mCached); + state.mCached = true; + MOZ_ASSERT(!state.mUsed); + state.mUsed = true; + state.mSpaceAndClip = aBuilder.CurrentSpaceAndClipChain(); + + Stats().AddCached(); + aBuilder.StartCachedItem(*mCurrentIndex); +} + +void DisplayItemCache::MaybeEndCaching(wr::DisplayListBuilder& aBuilder) { + if (IsEnabled() && mCurrentIndex) { + aBuilder.EndCachedItem(*mCurrentIndex); + mCurrentIndex = Nothing(); + } +} + +bool DisplayItemCache::ReuseItem(nsPaintedDisplayItem* aItem, + wr::DisplayListBuilder& aBuilder) { + if (!IsEnabled()) { + return false; + } + + auto& index = aItem->CacheIndex(); + if (!index) { + return false; + } + + auto& state = mCachedItemState[*index]; + if (!state.mCached) { + // The display item has a stale cache state. + return false; // Recache item. + } + + // Spatial id and clip id can change between display lists. + if (!(aBuilder.CurrentSpaceAndClipChain() == state.mSpaceAndClip)) { + // TODO(miko): Technically we might be able to update just the changed data + // here but it adds a lot of complexity. + // Mark the cache state false and recache the item. + state.mCached = false; + return false; + } + + Stats().AddReused(); + Stats().AddTotal(); + + state.mUsed = true; + aBuilder.ReuseItem(*index); + return true; +} + +} // namespace layers +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/gfx/layers/wr/DisplayItemCache.h @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef GFX_DISPLAY_ITEM_CACHE_H +#define GFX_DISPLAY_ITEM_CACHE_H + +#include "mozilla/webrender/WebRenderAPI.h" +#include "mozilla/Maybe.h" +#include "nsTArray.h" + +class nsPaintedDisplayItem; + +namespace mozilla { + +namespace wr { +class DisplayListBuilder; +} // namespace wr + +namespace layers { + +class CacheStats { + public: + CacheStats() = default; + + void Reset() { mCached = mReused = mTotal = 0; } + + void Print() { + printf("Cached: %zu, Reused: %zu, Total: %zu\n", mCached, mReused, mTotal); + } + + void AddCached() { mCached++; } + void AddReused() { mReused++; } + void AddTotal() { mTotal++; } + + private: + size_t mCached = 0; + size_t mReused = 0; + size_t mTotal = 0; +}; + +/** + * DisplayItemCache keeps track of which Gecko display items have already had + * their respective WebRender display items sent to WebRender backend. + * + * Ideally creating the WR display items for a Gecko display item would not + * depend on any external state. However currently pipeline id, clip id, and + * spatial id can change between display lists, even if the Gecko display items + * have not. This state is tracked by DisplayItemCache. + */ +class DisplayItemCache final { + public: + DisplayItemCache() : mMaxCacheSize(0), mNextIndex(0) {} + + bool IsEnabled() const { return mMaxCacheSize > 0; } + + /** + * Updates the cache state based on the given display list build information + * and pipeline id. + * + * This is necessary because Gecko display items can only be reused for the + * partial display list builds following a full display list build. + */ + void UpdateState(const bool aPartialDisplayListBuildFailed, + const wr::PipelineId& aPipelineId); + + /** + * Returns the current cache size. + */ + size_t CurrentCacheSize() const { + return IsEnabled() ? mCachedItemState.Length() : 0; + } + + /** + * Sets the initial and max cache size to given |aInitialSize| and |aMaxSize|. + * + * Currently the cache size is constant, but a good improvement would be to + * set the initial and maximum size based on the display list length. + */ + void SetCapacity(const size_t aInitialSize, const size_t aMaxSize) { + mMaxCacheSize = aMaxSize; + mCachedItemState.SetCapacity(aMaxSize); + mCachedItemState.SetLength(aInitialSize); + mFreeList.SetCapacity(aMaxSize); + } + + /** + * If the given display item |aItem| can be cached, update the cache state of + * the item and tell WR DisplayListBuilder |aBuilder| to cache WR display + * items until |EndCaching()| is called. + * + * If the display item cannot be cached, this function does nothing. + */ + void MaybeStartCaching(nsPaintedDisplayItem* aItem, + wr::DisplayListBuilder& aBuilder); + + /** + * Tell WR DisplayListBuilder |aBuilder| to stop caching WR display items. + * + * If the display item cannot be cached, this function does nothing. + */ + void MaybeEndCaching(wr::DisplayListBuilder& aBuilder); + + /** + * If the given |aItem| has been cached, tell WR DisplayListBuilder |aBuilder| + * to reuse it. + * Returns true if the item was reused, otherwise returns false. + */ + bool ReuseItem(nsPaintedDisplayItem* aItem, wr::DisplayListBuilder& aBuilder); + + CacheStats& Stats() { return mCacheStats; } + + private: + struct CacheEntry { + wr::WrSpaceAndClipChain mSpaceAndClip; + bool mCached; + bool mUsed; + }; + + Maybe<uint16_t> GetNextCacheIndex() { + if (mFreeList.IsEmpty()) { + return Nothing(); + } + + return Some(mFreeList.PopLastElement()); + } + + /** + * Iterates through |mCachedItemState| and adds unused entries to free list. + * If |aAddAll| is true, adds every entry regardless of the state. + */ + void PopulateFreeList(const bool aAddAll); + + /** + * Returns true if the given |aPipelineId| is different from the previous one, + * otherwise returns false. + */ + bool UpdatePipelineId(const wr::PipelineId& aPipelineId) { + const bool isSame = mPreviousPipelineId.refOr(aPipelineId) == aPipelineId; + mPreviousPipelineId = Some(aPipelineId); + return !isSame; + } + + nsTArray<CacheEntry> mCachedItemState; + nsTArray<uint16_t> mFreeList; + size_t mMaxCacheSize; + uint16_t mNextIndex; + Maybe<uint16_t> mCurrentIndex; + Maybe<wr::PipelineId> mPreviousPipelineId; + CacheStats mCacheStats; +}; + +} // namespace layers +} // namespace mozilla + +#endif /* GFX_DISPLAY_ITEM_CACHE_H */
--- a/gfx/webrender_bindings/src/bindings.rs +++ b/gfx/webrender_bindings/src/bindings.rs @@ -2287,30 +2287,32 @@ impl WebRenderFrameBuilder { } } pub struct WrState { pipeline_id: WrPipelineId, frame_builder: WebRenderFrameBuilder, current_tag: Option<ItemTag>, + current_item_key: Option<ItemKey>, } #[no_mangle] pub extern "C" fn wr_state_new(pipeline_id: WrPipelineId, content_size: LayoutSize, capacity: usize) -> *mut WrState { assert!(unsafe { !is_in_render_thread() }); let state = Box::new(WrState { pipeline_id: pipeline_id, frame_builder: WebRenderFrameBuilder::with_capacity(pipeline_id, content_size, capacity), current_tag: None, + current_item_key: None, }); Box::into_raw(state) } #[no_mangle] pub extern "C" fn wr_state_delete(state: *mut WrState) { assert!(unsafe { !is_in_render_thread() }); @@ -2671,16 +2673,17 @@ pub extern "C" fn wr_dp_push_rect(state: // NB: the damp-e10s talos-test will frequently crash on startup if we // early-return here for empty rects. I couldn't figure out why, but // it's pretty harmless to feed these through, so, uh, we do? clip_rect: clip_rect.unwrap_or(LayoutRect::zero()), clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder.dl_builder.push_rect( &prim_info, color, ); } @@ -2701,16 +2704,17 @@ pub extern "C" fn wr_dp_push_rect_with_p if clip_rect.is_none() { return; } let prim_info = CommonItemProperties { clip_rect: clip_rect.unwrap(), clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder.dl_builder.push_rect( &prim_info, color, ); } @@ -2753,16 +2757,17 @@ pub extern "C" fn wr_dp_push_backdrop_fi if clip_rect.is_none() { return; } let prim_info = CommonItemProperties { clip_rect: clip_rect.unwrap(), clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder.dl_builder.push_backdrop_filter( &prim_info, &filters, &filter_datas, &[], ); @@ -2781,16 +2786,17 @@ pub extern "C" fn wr_dp_push_clear_rect( if clip_rect.is_none() { return; } let prim_info = CommonItemProperties { clip_rect: clip_rect.unwrap(), clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(true), hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder.dl_builder.push_clear_rect( &prim_info, ); } #[no_mangle] @@ -2807,16 +2813,17 @@ pub extern "C" fn wr_dp_push_hit_test(st if clip_rect.is_none() { return; } let prim_info = CommonItemProperties { clip_rect: clip_rect.unwrap(), clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder.dl_builder.push_hit_test( &prim_info, ); } #[no_mangle] @@ -2834,16 +2841,17 @@ pub extern "C" fn wr_dp_push_clear_rect_ if clip_rect.is_none() { return; } let prim_info = CommonItemProperties { clip_rect: clip_rect.unwrap(), clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(true), hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder.dl_builder.push_clear_rect( &prim_info, ); } #[no_mangle] @@ -2861,16 +2869,17 @@ pub extern "C" fn wr_dp_push_image(state let space_and_clip = parent.to_webrender(state.pipeline_id); let prim_info = CommonItemProperties { clip_rect: clip, clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, + item_key: state.current_item_key, }; let alpha_type = if premultiplied_alpha { AlphaType::PremultipliedAlpha } else { AlphaType::Alpha }; @@ -2901,16 +2910,17 @@ pub extern "C" fn wr_dp_push_repeating_i let space_and_clip = parent.to_webrender(state.pipeline_id); let prim_info = CommonItemProperties { clip_rect: clip, clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, + item_key: state.current_item_key, }; let alpha_type = if premultiplied_alpha { AlphaType::PremultipliedAlpha } else { AlphaType::Alpha }; @@ -2945,16 +2955,17 @@ pub extern "C" fn wr_dp_push_yuv_planar_ let space_and_clip = parent.to_webrender(state.pipeline_id); let prim_info = CommonItemProperties { clip_rect: clip, clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder .dl_builder .push_yuv_image(&prim_info, bounds, YuvData::PlanarYCbCr(image_key_0, image_key_1, image_key_2), color_depth, @@ -2981,16 +2992,17 @@ pub extern "C" fn wr_dp_push_yuv_NV12_im let space_and_clip = parent.to_webrender(state.pipeline_id); let prim_info = CommonItemProperties { clip_rect: clip, clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder .dl_builder .push_yuv_image(&prim_info, bounds, YuvData::NV12(image_key_0, image_key_1), color_depth, @@ -3016,16 +3028,17 @@ pub extern "C" fn wr_dp_push_yuv_interle let space_and_clip = parent.to_webrender(state.pipeline_id); let prim_info = CommonItemProperties { clip_rect: clip, clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder .dl_builder .push_yuv_image(&prim_info, bounds, YuvData::InterleavedYCbCr(image_key_0), color_depth, @@ -3051,17 +3064,18 @@ pub extern "C" fn wr_dp_push_text(state: let space_and_clip = parent.to_webrender(state.pipeline_id); let prim_info = CommonItemProperties { clip_rect: clip, spatial_id: space_and_clip.spatial_id, clip_id: space_and_clip.clip_id, flags: prim_flags(is_backface_visible), - hit_info: state.current_tag + hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder .dl_builder .push_text(&prim_info, bounds, &glyph_slice, font_key, @@ -3108,16 +3122,17 @@ pub extern "C" fn wr_dp_push_line(state: let space_and_clip = parent.to_webrender(state.pipeline_id); let prim_info = CommonItemProperties { clip_rect: *clip, clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder .dl_builder .push_line(&prim_info, bounds, wavy_line_thickness, orientation, @@ -3153,16 +3168,17 @@ pub extern "C" fn wr_dp_push_border(stat let space_and_clip = parent.to_webrender(state.pipeline_id); let prim_info = CommonItemProperties { clip_rect: clip, clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder .dl_builder .push_border(&prim_info, rect, widths, border_details); @@ -3202,16 +3218,17 @@ pub extern "C" fn wr_dp_push_border_imag let space_and_clip = parent.to_webrender(state.pipeline_id); let prim_info = CommonItemProperties { clip_rect: clip, clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder.dl_builder.push_border( &prim_info, rect, params.widths, border_details, ); @@ -3260,16 +3277,17 @@ pub extern "C" fn wr_dp_push_border_grad let space_and_clip = parent.to_webrender(state.pipeline_id); let prim_info = CommonItemProperties { clip_rect: clip, clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder.dl_builder.push_border( &prim_info, rect, widths.into(), border_details, ); @@ -3322,16 +3340,17 @@ pub extern "C" fn wr_dp_push_border_radi let space_and_clip = parent.to_webrender(state.pipeline_id); let prim_info = CommonItemProperties { clip_rect: clip, clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder.dl_builder.push_border( &prim_info, rect, widths.into(), border_details, ); @@ -3365,16 +3384,17 @@ pub extern "C" fn wr_dp_push_linear_grad let space_and_clip = parent.to_webrender(state.pipeline_id); let prim_info = CommonItemProperties { clip_rect: clip, clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder.dl_builder.push_gradient( &prim_info, rect, gradient, tile_size.into(), tile_spacing.into(), @@ -3409,16 +3429,17 @@ pub extern "C" fn wr_dp_push_radial_grad let space_and_clip = parent.to_webrender(state.pipeline_id); let prim_info = CommonItemProperties { clip_rect: clip, clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder.dl_builder.push_radial_gradient( &prim_info, rect, gradient, tile_size, tile_spacing); @@ -3442,16 +3463,17 @@ pub extern "C" fn wr_dp_push_box_shadow( let space_and_clip = parent.to_webrender(state.pipeline_id); let prim_info = CommonItemProperties { clip_rect: clip, clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: prim_flags(is_backface_visible), hit_info: state.current_tag, + item_key: state.current_item_key, }; state.frame_builder .dl_builder .push_box_shadow(&prim_info, box_bounds, offset, color,
--- a/gfx/wr/webrender_api/src/display_item.rs +++ b/gfx/wr/webrender_api/src/display_item.rs @@ -28,16 +28,20 @@ pub const MAX_BLUR_RADIUS: f32 = 300.; /// is missing then the item doesn't take part in hit testing at all. This /// is composed of two numbers. In Servo, the first is an identifier while the /// second is used to select the cursor that should be used during mouse /// movement. In Gecko, the first is a scrollframe identifier, while the second /// is used to store various flags that APZ needs to properly process input /// events. pub type ItemTag = (u64, u16); +/// An identifier used to refer to previously sent display items. Currently it +/// refers to individual display items, but this may change later. +pub type ItemKey = u16; + bitflags! { #[repr(C)] #[derive(Deserialize, MallocSizeOf, Serialize, PeekPoke)] pub struct PrimitiveFlags: u8 { /// The CSS backface-visibility property (yes, it can be really granular) const IS_BACKFACE_VISIBLE = 1 << 0; /// If set, this primitive represents a scroll bar container const IS_SCROLLBAR_CONTAINER = 1 << 1; @@ -69,30 +73,33 @@ pub struct CommonItemProperties { /// The coordinate-space the item is in (yes, it can be really granular) pub spatial_id: SpatialId, /// Opaque bits for our clients to use for hit-testing. This is the most /// dubious "common" field, but because it's an Option, it usually only /// wastes a single byte (for None). pub hit_info: Option<ItemTag>, /// Various flags describing properties of this primitive. pub flags: PrimitiveFlags, + /// The unique id of this display item. + pub item_key: Option<ItemKey> } impl CommonItemProperties { /// Convenience for tests. pub fn new( clip_rect: LayoutRect, space_and_clip: SpaceAndClipInfo, ) -> Self { Self { clip_rect, spatial_id: space_and_clip.spatial_id, clip_id: space_and_clip.clip_id, hit_info: None, flags: PrimitiveFlags::default(), + item_key: None, } } } /// Per-primitive information about the nodes in the clip tree and /// the spatial tree that the primitive belongs to. /// /// Note: this is a separate struct from `PrimitiveInfo` because
new file mode 100644 --- /dev/null +++ b/gfx/wr/webrender_api/src/display_item_cache.rs @@ -0,0 +1,106 @@ +/* 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 crate::display_item::*; +use crate::display_list::*; + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +pub struct CachedDisplayItem { + item: DisplayItem, + data: Vec<u8>, +} + +impl CachedDisplayItem { + pub fn item(&self) -> &DisplayItem { + &self.item + } + + pub fn data_as_item_range<T>(&self) -> ItemRange<T> { + ItemRange::new(&self.data) + } +} + +impl From<DisplayItemRef<'_, '_>> for CachedDisplayItem { + fn from (item_ref: DisplayItemRef) -> Self { + let item = item_ref.item(); + + match item { + DisplayItem::Text(..) => CachedDisplayItem { + item: *item, + data: item_ref.glyphs().bytes().to_vec(), + }, + DisplayItem::Rectangle(..) | + DisplayItem::Image(..) => CachedDisplayItem { + item: *item, + data: Vec::new(), + }, + _ => { unimplemented!("Unsupported display item type"); } + } + } +} + +#[derive(Default, Deserialize, Serialize)] +pub struct DisplayItemCache { + items: Vec<Option<CachedDisplayItem>> +} + +impl DisplayItemCache { + fn grow_if_needed( + &mut self, + capacity: usize + ) { + if capacity > self.items.len() { + self.items.resize_with(capacity, || None::<CachedDisplayItem>); + // println!("Current cache size: {:?}", + // mem::size_of::<CachedDisplayItem>() * capacity); + } + } + + pub fn add_item( + &mut self, + key: Option<ItemKey>, + item: DisplayItemRef + ) { + let index = usize::from(key.expect("Cached item without key")); + self.items[index] = Some(CachedDisplayItem::from(item)); + } + + pub fn get_item( + &self, + key: ItemKey + ) -> Option<&CachedDisplayItem> { + self.items[key as usize].as_ref() + } + + pub fn update( + &mut self, + display_list: &BuiltDisplayList + ) { + self.grow_if_needed(display_list.cache_size()); + + let mut iter = display_list.extra_data_iter(); + + loop { + let item = match iter.next() { + Some(item) => item, + None => break, + }; + + match item.item() { + DisplayItem::Rectangle(ref info) => { + self.add_item(info.common.item_key, item); + } + DisplayItem::Text(ref info) => { + self.add_item(info.common.item_key, item); + } + DisplayItem::Image(ref info) => { + self.add_item(info.common.item_key, item); + } + item @ _ => { + unimplemented!("Unexpected item in extra data: {:?}", item); + } + } + } + } +}
--- a/gfx/wr/webrender_api/src/display_list.rs +++ b/gfx/wr/webrender_api/src/display_list.rs @@ -57,20 +57,31 @@ impl<'a, T> Default for ItemRange<'a, T> ItemRange { bytes: Default::default(), _boo: PhantomData, } } } impl<'a, T> ItemRange<'a, T> { + pub fn new(bytes: &'a [u8]) -> Self { + Self { + bytes, + _boo: PhantomData + } + } + pub fn is_empty(&self) -> bool { // Nothing more than space for a length (0). self.bytes.len() <= mem::size_of::<usize>() } + + pub fn bytes(&self) -> &[u8] { + &self.bytes + } } impl<'a, T: Default> ItemRange<'a, T> { pub fn iter(&self) -> AuxIter<'a, T> { AuxIter::new(T::default(), self.bytes) } }
--- a/gfx/wr/webrender_api/src/lib.rs +++ b/gfx/wr/webrender_api/src/lib.rs @@ -39,21 +39,23 @@ extern crate time; extern crate malloc_size_of; extern crate peek_poke; mod api; pub mod channel; mod color; mod display_item; +mod display_item_cache; mod display_list; mod font; mod gradient_builder; mod image; pub mod units; pub use crate::api::*; pub use crate::color::*; pub use crate::display_item::*; +pub use crate::display_item_cache::DisplayItemCache; pub use crate::display_list::*; pub use crate::font::*; pub use crate::gradient_builder::*; pub use crate::image::*;
--- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -3863,16 +3863,21 @@ type: bool #ifdef DEBUG value: true #else value: false #endif mirror: once +- name: gfx.webrender.enable-item-cache + type: bool + value: false + mirror: once + #ifdef NIGHTLY_BUILD # Keep this pref hidden on non-nightly builds to avoid people accidentally # turning it on. - name: gfx.webrender.panic-on-gl-error type: bool value: false mirror: once #endif