diff --git a/gfx/layers/wr/DisplayItemCache.cpp b/gfx/layers/wr/DisplayItemCache.cpp index b77e47f7ad86..f855d5858a49 100644 --- a/gfx/layers/wr/DisplayItemCache.cpp +++ b/gfx/layers/wr/DisplayItemCache.cpp @@ -5,20 +5,79 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "DisplayItemCache.h" +#include "nsDisplayList.h" namespace mozilla { namespace layers { -static const size_t kInitialCacheSize = 1024; -static const size_t kMaximumCacheSize = 10240; -static const size_t kCacheThreshold = 1; - DisplayItemCache::DisplayItemCache() - : mMaximumSize(0), mConsecutivePartialDisplayLists(0) { - if (XRE_IsContentProcess() && - StaticPrefs::gfx_webrender_enable_item_cache_AtStartup()) { - SetCapacity(kInitialCacheSize, kMaximumCacheSize); + : mDisplayList(nullptr), + mMaximumSize(0), + mPipelineId{}, + mCaching(false), + mInvalid(false) {} + +void DisplayItemCache::SetDisplayList(nsDisplayListBuilder* aBuilder, + nsDisplayList* aList) { + if (!IsEnabled()) { + return; } + + MOZ_ASSERT(aBuilder); + MOZ_ASSERT(aList); + + const bool listChanged = mDisplayList != aList; + const bool partialBuild = !aBuilder->PartialBuildFailed(); + + if (listChanged && partialBuild) { + // If the display list changed during a partial update, it means that + // |SetDisplayList()| has missed one rebuilt display list. + mDisplayList = nullptr; + return; + } + + if (listChanged || !partialBuild) { + // The display list has been changed or rebuilt. + mDisplayList = aList; + mInvalid = true; + } + + UpdateState(); +} + +void DisplayItemCache::SetPipelineId(const wr::PipelineId& aPipelineId) { + mInvalid = mInvalid || !(mPipelineId == aPipelineId); + mPipelineId = aPipelineId; +} + +void DisplayItemCache::UpdateState() { + // |mCaching == true| if: + // 1) |SetDisplayList()| is called with a fully rebuilt display list + // followed by + // 2a) |SetDisplayList()| is called with a partially updated display list + // or + // 2b) |SkipWaitingForPartialDisplayList()| is called + mCaching = !mInvalid; + +#if 0 + Stats().Print(); + Stats().Reset(); +#endif + + if (IsEmpty()) { + // The cache is empty so nothing needs to be updated. + mInvalid = false; + return; + } + + // Clear the cache if the current state is invalid. + if (mInvalid) { + ClearCache(); + } else { + FreeUnusedSlots(); + } + + mInvalid = false; } void DisplayItemCache::ClearCache() { @@ -77,20 +136,12 @@ void DisplayItemCache::SetCapacity(const size_t aInitialSize, } Maybe DisplayItemCache::AssignSlot(nsPaintedDisplayItem* aItem) { - if (kCacheThreshold > mConsecutivePartialDisplayLists) { - // Wait for |kCacheThreshold| partial display list builds, before caching - // display items. This is meant to avoid caching overhead for interactions - // or pages that do not work well with retained display lists. - // TODO: This is a speculative optimization to minimize regressions. - return Nothing(); - } - - if (!aItem->CanBeReused() || !aItem->CanBeCached()) { - // Do not try to cache items that cannot be reused. + if (!mCaching || !aItem->CanBeReused() || !aItem->CanBeCached()) { return Nothing(); } auto& slot = aItem->CacheIndex(); + if (!slot) { slot = GetNextFreeSlot(); if (!slot) { @@ -142,27 +193,5 @@ Maybe DisplayItemCache::CanReuseItem( return slotIndex; } -void DisplayItemCache::UpdateState(const bool aPartialDisplayListBuildFailed, - const wr::PipelineId& aPipelineId) { - const bool pipelineIdChanged = UpdatePipelineId(aPipelineId); - const bool invalidate = pipelineIdChanged || aPartialDisplayListBuildFailed; - - mConsecutivePartialDisplayLists = - invalidate ? 0 : mConsecutivePartialDisplayLists + 1; - - if (IsEmpty()) { - // The cache is empty so nothing needs to be updated. - return; - } - - // Clear the cache if the partial display list build failed, or if the - // pipeline id changed. - if (invalidate) { - ClearCache(); - } else { - FreeUnusedSlots(); - } -} - } // namespace layers } // namespace mozilla diff --git a/gfx/layers/wr/DisplayItemCache.h b/gfx/layers/wr/DisplayItemCache.h index cdebff9247e9..f7f12f70b366 100644 --- a/gfx/layers/wr/DisplayItemCache.h +++ b/gfx/layers/wr/DisplayItemCache.h @@ -8,9 +8,10 @@ #define GFX_DISPLAY_ITEM_CACHE_H #include "mozilla/webrender/WebRenderAPI.h" -#include "mozilla/Maybe.h" #include "nsTArray.h" +class nsDisplayList; +class nsDisplayListBuilder; class nsPaintedDisplayItem; namespace mozilla { @@ -64,6 +65,28 @@ class DisplayItemCache final { public: DisplayItemCache(); + /** + * Sets the initial and max cache size to given |aInitialSize| and |aMaxSize|. + */ + void SetCapacity(const size_t aInitialSize, const size_t aMaximumSize); + + /** + * Sets the display list used by the cache. + */ + void SetDisplayList(nsDisplayListBuilder* aBuilder, nsDisplayList* aList); + + /** + * Sets the pipeline id used by the cache. + */ + void SetPipelineId(const wr::PipelineId& aPipelineId); + + /** + * Enables caching immediately if the cache is valid, and display list is set. + */ + void SkipWaitingForPartialDisplayList() { + mCaching = mDisplayList && !mInvalid; + } + /** * Returns true if display item caching is enabled, otherwise false. */ @@ -81,16 +104,6 @@ class DisplayItemCache final { return mFreeSlots.IsEmpty() && CurrentSize() == mMaximumSize; } - /** - * 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. */ @@ -131,29 +144,23 @@ class DisplayItemCache final { void ClearCache(); void FreeUnusedSlots(); - bool GrowIfPossible(); Maybe GetNextFreeSlot(); + bool GrowIfPossible(); + void UpdateState(); - /** - * Sets the initial and max cache size to given |aInitialSize| and |aMaxSize|. - */ - void SetCapacity(const size_t aInitialSize, const size_t aMaximumSize); - - /** - * 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; - } + // The lifetime of display lists exceed the lifetime of DisplayItemCache. + // This pointer stores the address of the display list that is using this + // cache, and it is only used for pointer comparisons. + nsDisplayList* mDisplayList; size_t mMaximumSize; nsTArray mSlots; nsTArray mFreeSlots; - Maybe mPreviousPipelineId; - size_t mConsecutivePartialDisplayLists; + + wr::PipelineId mPipelineId; + bool mCaching; + bool mInvalid; + CacheStats mCacheStats; }; diff --git a/gfx/layers/wr/WebRenderLayerManager.cpp b/gfx/layers/wr/WebRenderLayerManager.cpp index ea467d53b000..7ff9e4c8595f 100644 --- a/gfx/layers/wr/WebRenderLayerManager.cpp +++ b/gfx/layers/wr/WebRenderLayerManager.cpp @@ -46,6 +46,14 @@ WebRenderLayerManager::WebRenderLayerManager(nsIWidget* aWidget) mStateManagers[renderRoot].mRenderRoot = renderRoot; mStateManagers[renderRoot].mLayerManager = this; } + + if (XRE_IsContentProcess() && + StaticPrefs::gfx_webrender_enable_item_cache_AtStartup()) { + static const size_t kInitialCacheSize = 1024; + static const size_t kMaximumCacheSize = 10240; + + mDisplayItemCache.SetCapacity(kInitialCacheSize, kMaximumCacheSize); + } } KnowsCompositor* WebRenderLayerManager::AsKnowsCompositor() { return mWrChild; } @@ -198,6 +206,8 @@ bool WebRenderLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags) { return false; } + mDisplayItemCache.SkipWaitingForPartialDisplayList(); + // Since we don't do repeat transactions right now, just set the time mAnimationReadyTime = TimeStamp::Now(); @@ -347,14 +357,12 @@ void WebRenderLayerManager::EndTransactionWithoutLayer( // generating the WR display list is the closest equivalent PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::Layerization); - builder.UpdateCacheState(aDisplayListBuilder->PartialBuildFailed()); + mDisplayItemCache.SetDisplayList(aDisplayListBuilder, aDisplayList); mWebRenderCommandBuilder.BuildWebRenderCommands( builder, resourceUpdates, aDisplayList, aDisplayListBuilder, mScrollDatas, std::move(aFilters)); - builder.UpdateCacheSize(); - builderDumpIndex = mWebRenderCommandBuilder.GetBuilderDumpIndex(builder.GetRenderRoot()); containsSVGGroup = mWebRenderCommandBuilder.GetContainsSVGGroup(); diff --git a/gfx/webrender_bindings/WebRenderAPI.cpp b/gfx/webrender_bindings/WebRenderAPI.cpp index 2e5dc62c393e..bfe99aa03fa8 100644 --- a/gfx/webrender_bindings/WebRenderAPI.cpp +++ b/gfx/webrender_bindings/WebRenderAPI.cpp @@ -912,6 +912,10 @@ DisplayListBuilder::DisplayListBuilder(PipelineId aId, mDisplayItemCache(aCache) { MOZ_COUNT_CTOR(DisplayListBuilder); mWrState = wr_state_new(aId, aContentSize, aCapacity); + + if (mDisplayItemCache && mDisplayItemCache->IsEnabled()) { + mDisplayItemCache->SetPipelineId(aId); + } } DisplayListBuilder::~DisplayListBuilder() { @@ -967,6 +971,11 @@ void DisplayListBuilder::Finalize(wr::LayoutSize& aOutContentSize, void DisplayListBuilder::Finalize( layers::RenderRootDisplayListData& aOutTransaction) { MOZ_ASSERT(mRenderRoot == wr::RenderRoot::Default); + + if (mDisplayItemCache && mDisplayItemCache->IsEnabled()) { + wr_dp_set_cache_size(mWrState, mDisplayItemCache->CurrentSize()); + } + wr::VecU8 dl; wr_api_finalize_builder(SubBuilder(aOutTransaction.mRenderRoot).mWrState, &aOutTransaction.mContentSize, @@ -1529,24 +1538,6 @@ bool DisplayListBuilder::ReuseItem(nsPaintedDisplayItem* aItem) { return false; } -void DisplayListBuilder::UpdateCacheState( - const bool aPartialDisplayListBuildFailed) { - if (mDisplayItemCache && mDisplayItemCache->IsEnabled()) { - mDisplayItemCache->UpdateState(aPartialDisplayListBuildFailed, - CurrentPipelineId()); -#if 0 - mDisplayItemCache->Stats().Print(); - mDisplayItemCache->Stats().Reset(); -#endif - } -} - -void DisplayListBuilder::UpdateCacheSize() { - if (mDisplayItemCache && mDisplayItemCache->IsEnabled()) { - wr_dp_set_cache_size(mWrState, mDisplayItemCache->CurrentSize()); - } -} - Maybe DisplayListBuilder::GetContainingFixedPosScrollTarget( const ActiveScrolledRoot* aAsr) { diff --git a/gfx/webrender_bindings/WebRenderAPI.h b/gfx/webrender_bindings/WebRenderAPI.h index 6980f4360503..93a10afc4880 100644 --- a/gfx/webrender_bindings/WebRenderAPI.h +++ b/gfx/webrender_bindings/WebRenderAPI.h @@ -641,9 +641,6 @@ class DisplayListBuilder final { */ bool ReuseItem(nsPaintedDisplayItem* aItem); - void UpdateCacheState(const bool aPartialDisplayListBuildFailed); - void UpdateCacheSize(); - uint64_t CurrentClipChainId() const { return mCurrentSpaceAndClipChain.clip_chain; }