diff options
Diffstat (limited to 'gfx/layers/wr/DisplayItemCache.cpp')
-rw-r--r-- | gfx/layers/wr/DisplayItemCache.cpp | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/gfx/layers/wr/DisplayItemCache.cpp b/gfx/layers/wr/DisplayItemCache.cpp new file mode 100644 index 0000000000..cd55da7424 --- /dev/null +++ b/gfx/layers/wr/DisplayItemCache.cpp @@ -0,0 +1,203 @@ +/* -*- 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" +#include "nsDisplayList.h" + +namespace mozilla { +namespace layers { + +DisplayItemCache::DisplayItemCache() + : mDisplayList(nullptr), + mMaximumSize(0), + mPipelineId{}, + mCaching(false), + mInvalid(false), + mSuppressed(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) { + Clear(); + } else { + FreeUnusedSlots(); + } + + mInvalid = false; +} + +void DisplayItemCache::Clear() { + memset(mSlots.Elements(), 0, mSlots.Length() * sizeof(Slot)); + mFreeSlots.ClearAndRetainStorage(); + + for (size_t i = 0; i < CurrentSize(); ++i) { + mFreeSlots.AppendElement(i); + } +} + +Maybe<uint16_t> DisplayItemCache::GetNextFreeSlot() { + if (mFreeSlots.IsEmpty() && !GrowIfPossible()) { + return Nothing(); + } + + return Some(mFreeSlots.PopLastElement()); +} + +bool DisplayItemCache::GrowIfPossible() { + if (IsFull()) { + return false; + } + + const auto currentSize = CurrentSize(); + MOZ_ASSERT(currentSize <= mMaximumSize); + + // New slots are added one by one, which is amortized O(1) time complexity due + // to underlying storage implementation. + mSlots.AppendElement(); + mFreeSlots.AppendElement(currentSize); + return true; +} + +void DisplayItemCache::FreeUnusedSlots() { + for (size_t i = 0; i < CurrentSize(); ++i) { + auto& slot = mSlots[i]; + + if (!slot.mUsed && slot.mOccupied) { + // This entry contained a cached item, but was not used. + slot.mOccupied = false; + mFreeSlots.AppendElement(i); + } + + slot.mUsed = false; + } +} + +void DisplayItemCache::SetCapacity(const size_t aInitialSize, + const size_t aMaximumSize) { + mMaximumSize = aMaximumSize; + mSlots.SetLength(aInitialSize); + mFreeSlots.SetCapacity(aMaximumSize); + Clear(); +} + +Maybe<uint16_t> DisplayItemCache::AssignSlot(nsPaintedDisplayItem* aItem) { + if (!mCaching || !aItem->CanBeReused() || !aItem->CanBeCached()) { + return Nothing(); + } + + auto& slot = aItem->CacheIndex(); + + if (!slot) { + slot = GetNextFreeSlot(); + if (!slot) { + // The item does not fit in the cache. + return Nothing(); + } + } + + MOZ_ASSERT(slot && CurrentSize() > *slot); + return slot; +} + +void DisplayItemCache::MarkSlotOccupied( + uint16_t aSlotIndex, const wr::WrSpaceAndClipChain& aSpaceAndClip) { + // Caching of the item succeeded, update the slot state. + auto& slot = mSlots[aSlotIndex]; + MOZ_ASSERT(!slot.mOccupied); + slot.mOccupied = true; + MOZ_ASSERT(!slot.mUsed); + slot.mUsed = true; + slot.mSpaceAndClip = aSpaceAndClip; +} + +Maybe<uint16_t> DisplayItemCache::CanReuseItem( + nsPaintedDisplayItem* aItem, const wr::WrSpaceAndClipChain& aSpaceAndClip) { + auto& slotIndex = aItem->CacheIndex(); + if (!slotIndex) { + return Nothing(); + } + + MOZ_ASSERT(slotIndex && CurrentSize() > *slotIndex); + + auto& slot = mSlots[*slotIndex]; + if (!slot.mOccupied) { + // The display item has a stale cache slot. Recache the item. + return Nothing(); + } + + if (mSuppressed) { + slot.mOccupied = false; + slotIndex = Nothing(); + return Nothing(); + } + + if (!(aSpaceAndClip == slot.mSpaceAndClip)) { + // Spatial id and clip id can change between display lists, if items that + // generate them change their order. + slot.mOccupied = false; + aItem->SetCantBeCached(); + slotIndex = Nothing(); + } else { + slot.mUsed = true; + } + + return slotIndex; +} + +} // namespace layers +} // namespace mozilla |