1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "DisplayItemCache.h"
8 #include "nsDisplayList.h"
9 
10 namespace mozilla {
11 namespace layers {
12 
DisplayItemCache()13 DisplayItemCache::DisplayItemCache()
14     : mDisplayList(nullptr),
15       mMaximumSize(0),
16       mPipelineId{},
17       mCaching(false),
18       mInvalid(false),
19       mSuppressed(false) {}
20 
SetDisplayList(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)21 void DisplayItemCache::SetDisplayList(nsDisplayListBuilder* aBuilder,
22                                       nsDisplayList* aList) {
23   if (!IsEnabled()) {
24     return;
25   }
26 
27   MOZ_ASSERT(aBuilder);
28   MOZ_ASSERT(aList);
29 
30   const bool listChanged = mDisplayList != aList;
31   const bool partialBuild = !aBuilder->PartialBuildFailed();
32 
33   if (listChanged && partialBuild) {
34     // If the display list changed during a partial update, it means that
35     // |SetDisplayList()| has missed one rebuilt display list.
36     mDisplayList = nullptr;
37     return;
38   }
39 
40   if (listChanged || !partialBuild) {
41     // The display list has been changed or rebuilt.
42     mDisplayList = aList;
43     mInvalid = true;
44   }
45 
46   UpdateState();
47 }
48 
SetPipelineId(const wr::PipelineId & aPipelineId)49 void DisplayItemCache::SetPipelineId(const wr::PipelineId& aPipelineId) {
50   mInvalid = mInvalid || !(mPipelineId == aPipelineId);
51   mPipelineId = aPipelineId;
52 }
53 
UpdateState()54 void DisplayItemCache::UpdateState() {
55   // |mCaching == true| if:
56   // 1) |SetDisplayList()| is called with a fully rebuilt display list
57   // followed by
58   // 2a) |SetDisplayList()| is called with a partially updated display list
59   // or
60   // 2b) |SkipWaitingForPartialDisplayList()| is called
61   mCaching = !mInvalid;
62 
63 #if 0
64   Stats().Print();
65   Stats().Reset();
66 #endif
67 
68   if (IsEmpty()) {
69     // The cache is empty so nothing needs to be updated.
70     mInvalid = false;
71     return;
72   }
73 
74   // Clear the cache if the current state is invalid.
75   if (mInvalid) {
76     Clear();
77   } else {
78     FreeUnusedSlots();
79   }
80 
81   mInvalid = false;
82 }
83 
Clear()84 void DisplayItemCache::Clear() {
85   memset(mSlots.Elements(), 0, mSlots.Length() * sizeof(Slot));
86   mFreeSlots.ClearAndRetainStorage();
87 
88   for (size_t i = 0; i < CurrentSize(); ++i) {
89     mFreeSlots.AppendElement(i);
90   }
91 }
92 
GetNextFreeSlot()93 Maybe<uint16_t> DisplayItemCache::GetNextFreeSlot() {
94   if (mFreeSlots.IsEmpty() && !GrowIfPossible()) {
95     return Nothing();
96   }
97 
98   return Some(mFreeSlots.PopLastElement());
99 }
100 
GrowIfPossible()101 bool DisplayItemCache::GrowIfPossible() {
102   if (IsFull()) {
103     return false;
104   }
105 
106   const auto currentSize = CurrentSize();
107   MOZ_ASSERT(currentSize <= mMaximumSize);
108 
109   // New slots are added one by one, which is amortized O(1) time complexity due
110   // to underlying storage implementation.
111   mSlots.AppendElement();
112   mFreeSlots.AppendElement(currentSize);
113   return true;
114 }
115 
FreeUnusedSlots()116 void DisplayItemCache::FreeUnusedSlots() {
117   for (size_t i = 0; i < CurrentSize(); ++i) {
118     auto& slot = mSlots[i];
119 
120     if (!slot.mUsed && slot.mOccupied) {
121       // This entry contained a cached item, but was not used.
122       slot.mOccupied = false;
123       mFreeSlots.AppendElement(i);
124     }
125 
126     slot.mUsed = false;
127   }
128 }
129 
SetCapacity(const size_t aInitialSize,const size_t aMaximumSize)130 void DisplayItemCache::SetCapacity(const size_t aInitialSize,
131                                    const size_t aMaximumSize) {
132   mMaximumSize = aMaximumSize;
133   mSlots.SetLength(aInitialSize);
134   mFreeSlots.SetCapacity(aMaximumSize);
135   Clear();
136 }
137 
AssignSlot(nsPaintedDisplayItem * aItem)138 Maybe<uint16_t> DisplayItemCache::AssignSlot(nsPaintedDisplayItem* aItem) {
139   if (!mCaching || !aItem->CanBeReused() || !aItem->CanBeCached()) {
140     return Nothing();
141   }
142 
143   auto& slot = aItem->CacheIndex();
144 
145   if (!slot) {
146     slot = GetNextFreeSlot();
147     if (!slot) {
148       // The item does not fit in the cache.
149       return Nothing();
150     }
151   }
152 
153   MOZ_ASSERT(slot && CurrentSize() > *slot);
154   return slot;
155 }
156 
MarkSlotOccupied(uint16_t aSlotIndex,const wr::WrSpaceAndClipChain & aSpaceAndClip)157 void DisplayItemCache::MarkSlotOccupied(
158     uint16_t aSlotIndex, const wr::WrSpaceAndClipChain& aSpaceAndClip) {
159   // Caching of the item succeeded, update the slot state.
160   auto& slot = mSlots[aSlotIndex];
161   MOZ_ASSERT(!slot.mOccupied);
162   slot.mOccupied = true;
163   MOZ_ASSERT(!slot.mUsed);
164   slot.mUsed = true;
165   slot.mSpaceAndClip = aSpaceAndClip;
166 }
167 
CanReuseItem(nsPaintedDisplayItem * aItem,const wr::WrSpaceAndClipChain & aSpaceAndClip)168 Maybe<uint16_t> DisplayItemCache::CanReuseItem(
169     nsPaintedDisplayItem* aItem, const wr::WrSpaceAndClipChain& aSpaceAndClip) {
170   auto& slotIndex = aItem->CacheIndex();
171   if (!slotIndex) {
172     return Nothing();
173   }
174 
175   MOZ_ASSERT(slotIndex && CurrentSize() > *slotIndex);
176 
177   auto& slot = mSlots[*slotIndex];
178   if (!slot.mOccupied) {
179     // The display item has a stale cache slot. Recache the item.
180     return Nothing();
181   }
182 
183   if (!(aSpaceAndClip == slot.mSpaceAndClip)) {
184     // Spatial id and clip id can change between display lists, if items that
185     // generate them change their order.
186     slot.mOccupied = false;
187     aItem->SetCantBeCached();
188     slotIndex = Nothing();
189   } else {
190     slot.mUsed = true;
191   }
192 
193   return slotIndex;
194 }
195 
196 }  // namespace layers
197 }  // namespace mozilla
198