Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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/. */
#ifndef GFX_DISPLAY_ITEM_CACHE_H
#define GFX_DISPLAY_ITEM_CACHE_H
#include "mozilla/webrender/WebRenderAPI.h"
#include "nsTArray.h"
namespace mozilla {
class nsDisplayList;
class nsDisplayListBuilder;
class nsPaintedDisplayItem;
namespace wr {
class DisplayListBuilder;
} // namespace wr
namespace layers {
class CacheStats {
public:
CacheStats() = default;
void Reset() { mCached = mReused = mTotal = 0; }
void Print() {
static uint64_t avgC = 1;
static uint64_t avgR = 1;
static uint64_t avgT = 1;
avgC += mCached;
avgR += mReused;
avgT += mTotal;
printf("Cached: %zu (avg: %f), Reused: %zu (avg: %f), Total: %zu\n",
mCached, (double)avgC / (double)avgT, mReused,
(double)avgR / (double)avgT, mTotal);
}
void AddCached() { mCached++; }
void AddReused() { mReused++; }
void AddTotal() { mTotal++; }
private:
size_t mCached = 0;
size_t mReused = 0;
size_t mTotal = 0;
};
/**
* DisplayItemCache keeps track of which Gecko display items have already had
* their respective WebRender display items sent to WebRender backend.
*
* Ideally creating the WR display items for a Gecko display item would not
* depend on any external state. However currently pipeline id, clip id, and
* spatial id can change between display lists, even if the Gecko display items
* have not. This state is tracked by DisplayItemCache.
*/
class DisplayItemCache final {
public:
DisplayItemCache();
/**
* Clears the cache.
*/
void Clear();
/**
* Sets the initial and max cache size to given |aInitialSize| and |aMaxSize|.
*/
void SetCapacity(const size_t aInitialSize, const size_t aMaximumSize);
/**
* Sets the display list used by the cache.
*/
void SetDisplayList(nsDisplayListBuilder* aBuilder, nsDisplayList* aList);
/**
* Sets the pipeline id used by the cache.
*/
void SetPipelineId(const wr::PipelineId& aPipelineId);
/**
* Enables caching immediately if the cache is valid, and display list is set.
*/
void SkipWaitingForPartialDisplayList() {
mCaching = mDisplayList && !mInvalid;
}
/**
* Returns true if display item caching is enabled, otherwise false.
*/
bool IsEnabled() const { return !mSuppressed && mMaximumSize > 0; }
/**
* Suppress display item caching. This doesn't clear any existing cached
* items or change the underlying capacity, it just makes IsEnabled() return
* false.
* It will also make CanReuseItem return false for the duration of the
* suppression.
*/
bool SetSuppressed(bool aSuppressed) {
if (aSuppressed == mSuppressed) {
return mSuppressed;
}
mSuppressed = aSuppressed;
return !mSuppressed;
}
/**
* Returns true if there are no cached items, otherwise false.
*/
bool IsEmpty() const { return mFreeSlots.Length() == CurrentSize(); }
/**
* Returns true if the cache has reached the maximum size, otherwise false.
*/
bool IsFull() const {
return mFreeSlots.IsEmpty() && CurrentSize() == mMaximumSize;
}
/**
* Returns the current cache size.
*/
size_t CurrentSize() const { return mSlots.Length(); }
/**
* If there are free slots in the cache, assigns a cache slot to the given
* display item |aItem| and returns it. Otherwise returns Nothing().
*/
Maybe<uint16_t> AssignSlot(nsPaintedDisplayItem* aItem);
/**
* Marks the slot with the given |slotIndex| occupied and used.
* Also stores the current space and clipchain |aSpaceAndClip|.
*/
void MarkSlotOccupied(uint16_t slotIndex,
const wr::WrSpaceAndClipChain& aSpaceAndClip);
/**
* Returns the slot index of the the given display item |aItem|, if the item
* can be reused. The current space and clipchain |aSpaceAndClip| is used to
* check whether the cached item is still valid.
* If the item cannot be reused, returns Nothing().
*/
Maybe<uint16_t> CanReuseItem(nsPaintedDisplayItem* aItem,
const wr::WrSpaceAndClipChain& aSpaceAndClip);
CacheStats& Stats() { return mCacheStats; }
private:
struct Slot {
Slot() : mSpaceAndClip{}, mOccupied(false), mUsed(false) {}
wr::WrSpaceAndClipChain mSpaceAndClip;
bool mOccupied;
bool mUsed;
};
void FreeUnusedSlots();
Maybe<uint16_t> GetNextFreeSlot();
bool GrowIfPossible();
void UpdateState();
// The lifetime of display lists exceed the lifetime of DisplayItemCache.
// This pointer stores the address of the display list that is using this
// cache, and it is only used for pointer comparisons.
nsDisplayList* mDisplayList;
size_t mMaximumSize;
nsTArray<Slot> mSlots;
nsTArray<uint16_t> mFreeSlots;
wr::PipelineId mPipelineId;
bool mCaching;
bool mInvalid;
bool mSuppressed;
CacheStats mCacheStats;
};
class MOZ_RAII AutoDisplayItemCacheSuppressor {
public:
explicit AutoDisplayItemCacheSuppressor(DisplayItemCache* aCache)
: mCache(aCache) {
mWasSuppressed = mCache->SetSuppressed(true);
}
// Note that this restores the original state rather than unconditionally
// unsuppressing the cache for future-proofing/robustification. Currently
// we only ever use this RAII in one non-recursive function, but we might
// decide to expand its usage to other scenarios and end up with nested
// suppressions, in which case restoring the state back to what we found it
// is better.
~AutoDisplayItemCacheSuppressor() { mCache->SetSuppressed(mWasSuppressed); }
private:
DisplayItemCache* mCache;
bool mWasSuppressed;
};
} // namespace layers
} // namespace mozilla
#endif /* GFX_DISPLAY_ITEM_CACHE_H */