summaryrefslogtreecommitdiffstats
path: root/gfx/layers/wr/DisplayItemCache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/wr/DisplayItemCache.cpp')
-rw-r--r--gfx/layers/wr/DisplayItemCache.cpp203
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