DisplayItemCache.cpp (5126B)
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 13 DisplayItemCache::DisplayItemCache() 14 : mDisplayList(nullptr), 15 mMaximumSize(0), 16 mPipelineId{}, 17 mCaching(false), 18 mInvalid(false), 19 mSuppressed(false) {} 20 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 49 void DisplayItemCache::SetPipelineId(const wr::PipelineId& aPipelineId) { 50 mInvalid = mInvalid || !(mPipelineId == aPipelineId); 51 mPipelineId = aPipelineId; 52 } 53 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 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 93 Maybe<uint16_t> DisplayItemCache::GetNextFreeSlot() { 94 if (mFreeSlots.IsEmpty() && !GrowIfPossible()) { 95 return Nothing(); 96 } 97 98 return Some(mFreeSlots.PopLastElement()); 99 } 100 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 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 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 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 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 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 (mSuppressed) { 184 slot.mOccupied = false; 185 slotIndex = Nothing(); 186 return Nothing(); 187 } 188 189 if (!(aSpaceAndClip == slot.mSpaceAndClip)) { 190 // Spatial id and clip id can change between display lists, if items that 191 // generate them change their order. 192 slot.mOccupied = false; 193 aItem->SetCantBeCached(); 194 slotIndex = Nothing(); 195 } else { 196 slot.mUsed = true; 197 } 198 199 return slotIndex; 200 } 201 202 } // namespace layers 203 } // namespace mozilla