tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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