зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1624125 - Track display list changes in DisplayItemCache r=jrmuizel
This is needed because display lists and DisplayItemCache have different lifetimes. For example, display lists can outlive WebRenderLayerManager when device reset occurs. A slightly nicer way of fixing this would be to couple DisplayItemCache with nsDisplayList or nsDisplayListBuilder. This is would currently require a lot of refactoring to look nice, because the painting code still supports non-retained display lists and non-WR code paths. Differential Revision: https://phabricator.services.mozilla.com/D68193 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
16396717d4
Коммит
9b727b1e0b
|
@ -5,20 +5,79 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DisplayItemCache.h"
|
||||
#include "nsDisplayList.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
static const size_t kInitialCacheSize = 1024;
|
||||
static const size_t kMaximumCacheSize = 10240;
|
||||
static const size_t kCacheThreshold = 1;
|
||||
|
||||
DisplayItemCache::DisplayItemCache()
|
||||
: mMaximumSize(0), mConsecutivePartialDisplayLists(0) {
|
||||
if (XRE_IsContentProcess() &&
|
||||
StaticPrefs::gfx_webrender_enable_item_cache_AtStartup()) {
|
||||
SetCapacity(kInitialCacheSize, kMaximumCacheSize);
|
||||
: mDisplayList(nullptr),
|
||||
mMaximumSize(0),
|
||||
mPipelineId{},
|
||||
mCaching(false),
|
||||
mInvalid(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) {
|
||||
ClearCache();
|
||||
} else {
|
||||
FreeUnusedSlots();
|
||||
}
|
||||
|
||||
mInvalid = false;
|
||||
}
|
||||
|
||||
void DisplayItemCache::ClearCache() {
|
||||
|
@ -77,20 +136,12 @@ void DisplayItemCache::SetCapacity(const size_t aInitialSize,
|
|||
}
|
||||
|
||||
Maybe<uint16_t> DisplayItemCache::AssignSlot(nsPaintedDisplayItem* aItem) {
|
||||
if (kCacheThreshold > mConsecutivePartialDisplayLists) {
|
||||
// Wait for |kCacheThreshold| partial display list builds, before caching
|
||||
// display items. This is meant to avoid caching overhead for interactions
|
||||
// or pages that do not work well with retained display lists.
|
||||
// TODO: This is a speculative optimization to minimize regressions.
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
if (!aItem->CanBeReused() || !aItem->CanBeCached()) {
|
||||
// Do not try to cache items that cannot be reused.
|
||||
if (!mCaching || !aItem->CanBeReused() || !aItem->CanBeCached()) {
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
auto& slot = aItem->CacheIndex();
|
||||
|
||||
if (!slot) {
|
||||
slot = GetNextFreeSlot();
|
||||
if (!slot) {
|
||||
|
@ -142,27 +193,5 @@ Maybe<uint16_t> DisplayItemCache::CanReuseItem(
|
|||
return slotIndex;
|
||||
}
|
||||
|
||||
void DisplayItemCache::UpdateState(const bool aPartialDisplayListBuildFailed,
|
||||
const wr::PipelineId& aPipelineId) {
|
||||
const bool pipelineIdChanged = UpdatePipelineId(aPipelineId);
|
||||
const bool invalidate = pipelineIdChanged || aPartialDisplayListBuildFailed;
|
||||
|
||||
mConsecutivePartialDisplayLists =
|
||||
invalidate ? 0 : mConsecutivePartialDisplayLists + 1;
|
||||
|
||||
if (IsEmpty()) {
|
||||
// The cache is empty so nothing needs to be updated.
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the cache if the partial display list build failed, or if the
|
||||
// pipeline id changed.
|
||||
if (invalidate) {
|
||||
ClearCache();
|
||||
} else {
|
||||
FreeUnusedSlots();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -8,9 +8,10 @@
|
|||
#define GFX_DISPLAY_ITEM_CACHE_H
|
||||
|
||||
#include "mozilla/webrender/WebRenderAPI.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsDisplayList;
|
||||
class nsDisplayListBuilder;
|
||||
class nsPaintedDisplayItem;
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -64,6 +65,28 @@ class DisplayItemCache final {
|
|||
public:
|
||||
DisplayItemCache();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
@ -81,16 +104,6 @@ class DisplayItemCache final {
|
|||
return mFreeSlots.IsEmpty() && CurrentSize() == mMaximumSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the cache state based on the given display list build information
|
||||
* and pipeline id.
|
||||
*
|
||||
* This is necessary because Gecko display items can only be reused for the
|
||||
* partial display list builds following a full display list build.
|
||||
*/
|
||||
void UpdateState(const bool aPartialDisplayListBuildFailed,
|
||||
const wr::PipelineId& aPipelineId);
|
||||
|
||||
/**
|
||||
* Returns the current cache size.
|
||||
*/
|
||||
|
@ -131,29 +144,23 @@ class DisplayItemCache final {
|
|||
|
||||
void ClearCache();
|
||||
void FreeUnusedSlots();
|
||||
bool GrowIfPossible();
|
||||
Maybe<uint16_t> GetNextFreeSlot();
|
||||
bool GrowIfPossible();
|
||||
void UpdateState();
|
||||
|
||||
/**
|
||||
* Sets the initial and max cache size to given |aInitialSize| and |aMaxSize|.
|
||||
*/
|
||||
void SetCapacity(const size_t aInitialSize, const size_t aMaximumSize);
|
||||
|
||||
/**
|
||||
* Returns true if the given |aPipelineId| is different from the previous one,
|
||||
* otherwise returns false.
|
||||
*/
|
||||
bool UpdatePipelineId(const wr::PipelineId& aPipelineId) {
|
||||
const bool isSame = mPreviousPipelineId.refOr(aPipelineId) == aPipelineId;
|
||||
mPreviousPipelineId = Some(aPipelineId);
|
||||
return !isSame;
|
||||
}
|
||||
// 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;
|
||||
Maybe<wr::PipelineId> mPreviousPipelineId;
|
||||
size_t mConsecutivePartialDisplayLists;
|
||||
|
||||
wr::PipelineId mPipelineId;
|
||||
bool mCaching;
|
||||
bool mInvalid;
|
||||
|
||||
CacheStats mCacheStats;
|
||||
};
|
||||
|
||||
|
|
|
@ -46,6 +46,14 @@ WebRenderLayerManager::WebRenderLayerManager(nsIWidget* aWidget)
|
|||
mStateManagers[renderRoot].mRenderRoot = renderRoot;
|
||||
mStateManagers[renderRoot].mLayerManager = this;
|
||||
}
|
||||
|
||||
if (XRE_IsContentProcess() &&
|
||||
StaticPrefs::gfx_webrender_enable_item_cache_AtStartup()) {
|
||||
static const size_t kInitialCacheSize = 1024;
|
||||
static const size_t kMaximumCacheSize = 10240;
|
||||
|
||||
mDisplayItemCache.SetCapacity(kInitialCacheSize, kMaximumCacheSize);
|
||||
}
|
||||
}
|
||||
|
||||
KnowsCompositor* WebRenderLayerManager::AsKnowsCompositor() { return mWrChild; }
|
||||
|
@ -198,6 +206,8 @@ bool WebRenderLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags) {
|
|||
return false;
|
||||
}
|
||||
|
||||
mDisplayItemCache.SkipWaitingForPartialDisplayList();
|
||||
|
||||
// Since we don't do repeat transactions right now, just set the time
|
||||
mAnimationReadyTime = TimeStamp::Now();
|
||||
|
||||
|
@ -347,14 +357,12 @@ void WebRenderLayerManager::EndTransactionWithoutLayer(
|
|||
// generating the WR display list is the closest equivalent
|
||||
PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::Layerization);
|
||||
|
||||
builder.UpdateCacheState(aDisplayListBuilder->PartialBuildFailed());
|
||||
mDisplayItemCache.SetDisplayList(aDisplayListBuilder, aDisplayList);
|
||||
|
||||
mWebRenderCommandBuilder.BuildWebRenderCommands(
|
||||
builder, resourceUpdates, aDisplayList, aDisplayListBuilder,
|
||||
mScrollDatas, std::move(aFilters));
|
||||
|
||||
builder.UpdateCacheSize();
|
||||
|
||||
builderDumpIndex =
|
||||
mWebRenderCommandBuilder.GetBuilderDumpIndex(builder.GetRenderRoot());
|
||||
containsSVGGroup = mWebRenderCommandBuilder.GetContainsSVGGroup();
|
||||
|
|
|
@ -912,6 +912,10 @@ DisplayListBuilder::DisplayListBuilder(PipelineId aId,
|
|||
mDisplayItemCache(aCache) {
|
||||
MOZ_COUNT_CTOR(DisplayListBuilder);
|
||||
mWrState = wr_state_new(aId, aContentSize, aCapacity);
|
||||
|
||||
if (mDisplayItemCache && mDisplayItemCache->IsEnabled()) {
|
||||
mDisplayItemCache->SetPipelineId(aId);
|
||||
}
|
||||
}
|
||||
|
||||
DisplayListBuilder::~DisplayListBuilder() {
|
||||
|
@ -967,6 +971,11 @@ void DisplayListBuilder::Finalize(wr::LayoutSize& aOutContentSize,
|
|||
void DisplayListBuilder::Finalize(
|
||||
layers::RenderRootDisplayListData& aOutTransaction) {
|
||||
MOZ_ASSERT(mRenderRoot == wr::RenderRoot::Default);
|
||||
|
||||
if (mDisplayItemCache && mDisplayItemCache->IsEnabled()) {
|
||||
wr_dp_set_cache_size(mWrState, mDisplayItemCache->CurrentSize());
|
||||
}
|
||||
|
||||
wr::VecU8 dl;
|
||||
wr_api_finalize_builder(SubBuilder(aOutTransaction.mRenderRoot).mWrState,
|
||||
&aOutTransaction.mContentSize,
|
||||
|
@ -1529,24 +1538,6 @@ bool DisplayListBuilder::ReuseItem(nsPaintedDisplayItem* aItem) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void DisplayListBuilder::UpdateCacheState(
|
||||
const bool aPartialDisplayListBuildFailed) {
|
||||
if (mDisplayItemCache && mDisplayItemCache->IsEnabled()) {
|
||||
mDisplayItemCache->UpdateState(aPartialDisplayListBuildFailed,
|
||||
CurrentPipelineId());
|
||||
#if 0
|
||||
mDisplayItemCache->Stats().Print();
|
||||
mDisplayItemCache->Stats().Reset();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayListBuilder::UpdateCacheSize() {
|
||||
if (mDisplayItemCache && mDisplayItemCache->IsEnabled()) {
|
||||
wr_dp_set_cache_size(mWrState, mDisplayItemCache->CurrentSize());
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<layers::ScrollableLayerGuid::ViewID>
|
||||
DisplayListBuilder::GetContainingFixedPosScrollTarget(
|
||||
const ActiveScrolledRoot* aAsr) {
|
||||
|
|
|
@ -641,9 +641,6 @@ class DisplayListBuilder final {
|
|||
*/
|
||||
bool ReuseItem(nsPaintedDisplayItem* aItem);
|
||||
|
||||
void UpdateCacheState(const bool aPartialDisplayListBuildFailed);
|
||||
void UpdateCacheSize();
|
||||
|
||||
uint64_t CurrentClipChainId() const {
|
||||
return mCurrentSpaceAndClipChain.clip_chain;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче