Bug 1490404 - Part 2: Add retained display list statistics r=mattwoodrow

Differential Revision: https://phabricator.services.mozilla.com/D33680

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Miko Mynttinen 2019-06-06 17:54:14 +00:00
Родитель fbfcf25cc9
Коммит 6c57050ada
4 изменённых файлов: 206 добавлений и 77 удалений

Просмотреть файл

@ -66,7 +66,7 @@ std::string Diagnostics::GetFrameOverlayString(const GPUStats& aStats) {
gpuTimeString = nsPrintfCString("%0.1fms", mGPUDrawMs.Average()).get();
}
// DL = nsDisplayListBuilder
// DL = nsDisplayListBuilder, p = partial, f = full
// FLB = FrameLayerBuilder
// R = ClientLayerManager::EndTransaction
// CP = ShadowLayerForwarder::EndTransaction (txn build)
@ -80,17 +80,10 @@ std::string Diagnostics::GetFrameOverlayString(const GPUStats& aStats) {
"[CC] Build: %0.1fms Exec: %0.1fms GPU: %s Fill Ratio: %0.1f/%0.1f",
mPrepareMs.Average(), mCompositeMs.Average(), gpuTimeString.c_str(),
pixelFillRatio, screenFillRatio);
nsCString line3;
if (mDlb2Ms.Average() != 0.0f) {
line3 += nsPrintfCString(
"[Content] DL: %0.1f/%0.1fms FLB: %0.1fms Raster: %0.1fms",
mDlb2Ms.Average(), mDlbMs.Average(), mFlbMs.Average(),
mRasterMs.Average());
} else {
line3 += nsPrintfCString(
"[Content] DL: %0.1fms FLB: %0.1fms Raster: %0.1fms", mDlbMs.Average(),
mFlbMs.Average(), mRasterMs.Average());
}
nsPrintfCString line3(
"[Content] DL p: %0.1f DL f: %0.1fms FLB: %0.1fms Raster: %0.1fms",
mDlbMs.Average(), mDlb2Ms.Average(), mFlbMs.Average(),
mRasterMs.Average());
nsPrintfCString line4("[IPDL] Build: %0.1fms Send: %0.1fms Update: %0.1fms",
mSerializeMs.Average(), mSendMs.Average(),
mUpdateMs.Average());

Просмотреть файл

@ -3558,8 +3558,11 @@ struct TemporaryDisplayListBuilder {
const bool aBuildCaret)
: mBuilder(aFrame, aBuilderMode, aBuildCaret) {}
~TemporaryDisplayListBuilder() { mList.DeleteAll(&mBuilder); }
nsDisplayListBuilder mBuilder;
nsDisplayList mList;
RetainedDisplayListMetrics mMetrics;
};
nsresult nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext,
@ -3569,7 +3572,6 @@ nsresult nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext,
nsDisplayListBuilderMode aBuilderMode,
PaintFrameFlags aFlags) {
AUTO_PROFILER_LABEL("nsLayoutUtils::PaintFrame", GRAPHICS);
typedef RetainedDisplayListBuilder::PartialUpdateResult PartialUpdateResult;
#ifdef MOZ_DUMP_PAINTING
if (!gPaintCountStack) {
@ -3620,16 +3622,21 @@ nsresult nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext,
Maybe<TemporaryDisplayListBuilder> temporaryBuilder;
nsDisplayListBuilder* builder = nullptr;
nsDisplayList* list = nullptr;
RetainedDisplayListMetrics* metrics = nullptr;
if (useRetainedBuilder) {
builder = retainedBuilder->Builder();
list = retainedBuilder->List();
metrics = retainedBuilder->Metrics();
} else {
temporaryBuilder.emplace(aFrame, aBuilderMode, buildCaret);
builder = &temporaryBuilder->mBuilder;
list = &temporaryBuilder->mList;
metrics = &temporaryBuilder->mMetrics;
}
MOZ_ASSERT(builder && list && metrics);
// Retained builder exists, but display list retaining is disabled.
if (!useRetainedBuilder && retainedBuilder) {
// Clear the modified frames lists and frame properties.
@ -3639,6 +3646,9 @@ nsresult nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext,
retainedBuilder->List()->DeleteAll(retainedBuilder->Builder());
}
metrics->Reset();
metrics->StartBuild();
builder->BeginFrame();
if (aFlags & PaintFrameFlags::InTransform) {
@ -3745,8 +3755,6 @@ nsresult nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext,
AUTO_PROFILER_TRACING("Paint", "DisplayList", GRAPHICS);
PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::DisplayList);
TimeStamp dlStart = TimeStamp::Now();
{
// If a scrollable container layer is created in
// nsDisplayList::PaintForFrame, it will be the scroll parent for display
@ -3826,28 +3834,35 @@ nsresult nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext,
if (StaticPrefs::LayoutVerifyRetainDisplayList()) {
beforeMergeChecker.Set(list, "BM");
}
updateState = retainedBuilder->AttemptPartialUpdate(
aBackstop, beforeMergeChecker ? &toBeMergedChecker : nullptr);
if ((updateState != PartialUpdateResult::Failed) &&
beforeMergeChecker) {
afterMergeChecker.Set(list, "AM");
}
metrics->EndPartialBuild(updateState);
} else {
// Partial updates are disabled.
metrics->mPartialUpdateResult = PartialUpdateResult::Failed;
metrics->mPartialUpdateFailReason = PartialUpdateFailReason::Disabled;
}
if ((updateState != PartialUpdateResult::Failed) &&
(StaticPrefs::LayoutDisplayListBuildTwice() || afterMergeChecker)) {
updateState = PartialUpdateResult::Failed;
if (StaticPrefs::LayersDrawFPS()) {
if (RefPtr<LayerManager> lm = builder->GetWidgetLayerManager()) {
if (PaintTiming* pt = ClientLayerManager::MaybeGetPaintTiming(lm)) {
pt->dl2Ms() = (TimeStamp::Now() - dlStart).ToMilliseconds();
}
}
}
dlStart = TimeStamp::Now();
// Rebuild the full display list if the partial display list build failed,
// or if the merge checker is used.
bool doFullRebuild =
updateState == PartialUpdateResult::Failed || afterMergeChecker;
if (StaticPrefs::LayoutDisplayListBuildTwice()) {
// Build display list twice to compare partial and full display list
// build times.
metrics->StartBuild();
doFullRebuild = true;
}
if (updateState == PartialUpdateResult::Failed) {
if (doFullRebuild) {
list->DeleteAll(builder);
builder->ClearRetainedWindowRegions();
@ -3860,6 +3875,8 @@ nsresult nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext,
visibleRegion, aBackstop);
builder->LeavePresShell(aFrame, list);
metrics->EndFullBuild();
updateState = PartialUpdateResult::Updated;
if (afterMergeChecker) {
@ -3888,13 +3905,15 @@ nsresult nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext,
builder->SetIsBuilding(false);
builder->IncrementPresShellPaintCount(presShell);
}
if (StaticPrefs::LayersDrawFPS()) {
if (RefPtr<LayerManager> lm = builder->GetWidgetLayerManager()) {
if (PaintTiming* pt = ClientLayerManager::MaybeGetPaintTiming(lm)) {
pt->dlMs() = (TimeStamp::Now() - dlStart).ToMilliseconds();
}
}
if (StaticPrefs::LayersDrawFPS()) {
RefPtr<LayerManager> lm = builder->GetWidgetLayerManager();
PaintTiming* pt = ClientLayerManager::MaybeGetPaintTiming(lm);
if (pt) {
pt->dlMs() = static_cast<float>(metrics->mPartialBuildDuration);
pt->dl2Ms() = static_cast<float>(metrics->mFullBuildDuration);
}
}
@ -4116,13 +4135,28 @@ nsresult nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext,
{
AUTO_PROFILER_TRACING("Paint", "DisplayListResources", GRAPHICS);
// Flush the list so we don't trigger the IsEmpty-on-destruction assertion
if (!useRetainedBuilder) {
list->DeleteAll(builder);
}
builder->EndFrame();
if (!useRetainedBuilder) {
temporaryBuilder.reset();
}
}
#if 0
if (XRE_IsParentProcess()) {
if (metrics->mPartialUpdateResult == PartialUpdateResult::Failed) {
printf("DL partial update failed: %s, Frame: %p\n",
metrics->FailReasonString(), aFrame);
} else {
printf(
"DL partial build success!"
" new: %d, reused: %d, rebuilt: %d, removed: %d, total: %d\n",
metrics->mNewItems, metrics->mReusedItems, metrics->mRebuiltItems,
metrics->mRemovedItems, metrics->mTotalItems);
}
}
#endif
return NS_OK;
}

Просмотреть файл

@ -192,6 +192,7 @@ bool RetainedDisplayListBuilder::PreProcessDisplayList(
}
item->Destroy(&mBuilder);
Metrics()->mRemovedItems++;
i++;
aUpdated = PartialUpdateResult::Updated;
@ -396,6 +397,7 @@ void OldItemInfo::Discard(RetainedDisplayListBuilder* aBuilder,
if (mItem) {
MOZ_ASSERT(mOwnsItem);
mItem->Destroy(aBuilder->Builder());
aBuilder->Metrics()->mRemovedItems++;
}
mItem = nullptr;
}
@ -436,6 +438,7 @@ class MergeState {
HasModifiedFrame(aNewItem));
if (!aNewItem->HasModifiedFrame() &&
HasMatchingItemInOldList(aNewItem, &oldIndex)) {
mBuilder->Metrics()->mRebuiltItems++;
nsDisplayItem* oldItem = mOldItems[oldIndex.val].mItem;
MOZ_DIAGNOSTIC_ASSERT(oldItem->GetPerFrameKey() ==
aNewItem->GetPerFrameKey() &&
@ -648,6 +651,8 @@ class MergeState {
#endif
mMergedItems.AppendToTop(aItem);
mBuilder->Metrics()->mTotalItems++;
MergedListIndex newIndex =
mMergedDAG.AddNode(aDirectPredecessors, aExtraDirectPredecessor);
return newIndex;
@ -671,6 +676,7 @@ class MergeState {
mBuilder->IncrementSubDocPresShellPaintCount(item);
}
item->SetReused(true);
mBuilder->Metrics()->mReusedItems++;
mOldItems[aNode.val].AddedToMergedList(
AddNewNode(item, Some(aNode), aDirectPredecessors, Nothing()));
}
@ -813,6 +819,7 @@ bool RetainedDisplayListBuilder::MergeDisplayLists(
Maybe<MergedListIndex> previousItemIndex;
while (nsDisplayItem* item = aNewList->RemoveBottom()) {
Metrics()->mNewItems++;
previousItemIndex = merge.ProcessItemFromNewList(item, previousItemIndex);
}
@ -1302,11 +1309,26 @@ bool RetainedDisplayListBuilder::ComputeRebuildRegion(
return true;
}
/*
* A simple early exit heuristic to avoid slow partial display list rebuilds.
*/
static bool ShouldBuildPartial(nsTArray<nsIFrame*>& aModifiedFrames) {
bool RetainedDisplayListBuilder::ShouldBuildPartial(
nsTArray<nsIFrame*>& aModifiedFrames) {
if (mList.IsEmpty()) {
// Partial builds without a previous display list do not make sense.
Metrics()->mPartialUpdateFailReason = PartialUpdateFailReason::EmptyList;
return false;
}
if (aModifiedFrames.Length() > StaticPrefs::LayoutRebuildFrameLimit()) {
// Computing a dirty rect with too many modified frames can be slow.
Metrics()->mPartialUpdateFailReason = PartialUpdateFailReason::RebuildLimit;
return false;
}
// We don't support retaining with overlay scrollbars, since they require
// us to look at the display list and pick the highest z-index, which
// we can't do during partial building.
if (mBuilder.DisablePartialUpdates()) {
mBuilder.SetDisablePartialUpdates(false);
Metrics()->mPartialUpdateFailReason = PartialUpdateFailReason::Disabled;
return false;
}
@ -1324,6 +1346,7 @@ static bool ShouldBuildPartial(nsTArray<nsIFrame*>& aModifiedFrames) {
if (type == LayoutFrameType::Viewport ||
type == LayoutFrameType::PageContent ||
type == LayoutFrameType::Canvas || type == LayoutFrameType::Scrollbar) {
Metrics()->mPartialUpdateFailReason = PartialUpdateFailReason::FrameType;
return false;
}
}
@ -1331,6 +1354,26 @@ static bool ShouldBuildPartial(nsTArray<nsIFrame*>& aModifiedFrames) {
return true;
}
void RetainedDisplayListBuilder::InvalidateCaretFramesIfNeeded(
nsTArray<nsIFrame*>& aModifiedFrames) {
if (mPreviousCaret == mBuilder.GetCaretFrame()) {
// The current caret frame is the same as the previous one.
return;
}
if (mPreviousCaret &&
mBuilder.MarkFrameModifiedDuringBuilding(mPreviousCaret)) {
aModifiedFrames.AppendElement(mPreviousCaret);
}
if (mBuilder.GetCaretFrame() &&
mBuilder.MarkFrameModifiedDuringBuilding(mBuilder.GetCaretFrame())) {
aModifiedFrames.AppendElement(mBuilder.GetCaretFrame());
}
mPreviousCaret = mBuilder.GetCaretFrame();
}
static void ClearFrameProps(nsTArray<nsIFrame*>& aFrames) {
for (nsIFrame* f : aFrames) {
if (f->HasOverrideDirtyRegion()) {
@ -1347,13 +1390,10 @@ static void ClearFrameProps(nsTArray<nsIFrame*>& aFrames) {
class AutoClearFramePropsArray {
public:
explicit AutoClearFramePropsArray(size_t aCapacity) : mFrames(aCapacity) {}
AutoClearFramePropsArray() = default;
~AutoClearFramePropsArray() { ClearFrameProps(mFrames); }
nsTArray<nsIFrame*>& Frames() { return mFrames; }
bool IsEmpty() const { return mFrames.IsEmpty(); }
private:
@ -1367,9 +1407,8 @@ void RetainedDisplayListBuilder::ClearFramesWithProps() {
&framesWithProps.Frames());
}
auto RetainedDisplayListBuilder::AttemptPartialUpdate(
nscolor aBackstop, mozilla::DisplayListChecker* aChecker)
-> PartialUpdateResult {
PartialUpdateResult RetainedDisplayListBuilder::AttemptPartialUpdate(
nscolor aBackstop, mozilla::DisplayListChecker* aChecker) {
mBuilder.RemoveModifiedWindowRegions();
mBuilder.ClearWindowOpaqueRegion();
@ -1387,33 +1426,11 @@ auto RetainedDisplayListBuilder::AttemptPartialUpdate(
GetModifiedAndFramesWithProps(&mBuilder, &modifiedFrames.Frames(),
&framesWithProps.Frames());
// Do not allow partial builds if the retained display list is empty, or if
// ShouldBuildPartial heuristic fails.
bool shouldBuildPartial =
!mList.IsEmpty() && ShouldBuildPartial(modifiedFrames.Frames());
// Do not allow partial builds if the |ShouldBuildPartial()| heuristic fails.
bool shouldBuildPartial = ShouldBuildPartial(modifiedFrames.Frames());
// We don't support retaining with overlay scrollbars, since they require
// us to look at the display list and pick the highest z-index, which
// we can't do during partial building.
if (mBuilder.DisablePartialUpdates()) {
shouldBuildPartial = false;
mBuilder.SetDisablePartialUpdates(false);
}
if (mPreviousCaret != mBuilder.GetCaretFrame()) {
if (mPreviousCaret) {
if (mBuilder.MarkFrameModifiedDuringBuilding(mPreviousCaret)) {
modifiedFrames.Frames().AppendElement(mPreviousCaret);
}
}
if (mBuilder.GetCaretFrame()) {
if (mBuilder.MarkFrameModifiedDuringBuilding(mBuilder.GetCaretFrame())) {
modifiedFrames.Frames().AppendElement(mBuilder.GetCaretFrame());
}
}
mPreviousCaret = mBuilder.GetCaretFrame();
if (shouldBuildPartial) {
InvalidateCaretFramesIfNeeded(modifiedFrames.Frames());
}
nsRect modifiedDirty;
@ -1465,6 +1482,7 @@ auto RetainedDisplayListBuilder::AttemptPartialUpdate(
mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), nullptr);
mList.DeleteAll(&mBuilder);
modifiedDL.DeleteAll(&mBuilder);
Metrics()->mPartialUpdateFailReason = PartialUpdateFailReason::Content;
return PartialUpdateResult::Failed;
}

Просмотреть файл

@ -92,6 +92,77 @@ RetainedDisplayListData* GetRetainedDisplayListData(nsIFrame* aRootFrame);
*/
RetainedDisplayListData* GetOrSetRetainedDisplayListData(nsIFrame* aRootFrame);
enum class PartialUpdateResult { Failed, NoChange, Updated };
enum class PartialUpdateFailReason {
NA,
EmptyList,
RebuildLimit,
FrameType,
Disabled,
Content
};
struct RetainedDisplayListMetrics {
RetainedDisplayListMetrics() { Reset(); }
void Reset() {
mNewItems = 0;
mRebuiltItems = 0;
mRemovedItems = 0;
mReusedItems = 0;
mTotalItems = 0;
mPartialBuildDuration = 0;
mFullBuildDuration = 0;
mPartialUpdateFailReason = PartialUpdateFailReason::NA;
mPartialUpdateResult = PartialUpdateResult::NoChange;
}
void StartBuild() { mStartTime = mozilla::TimeStamp::Now(); }
void EndFullBuild() { mFullBuildDuration = Elapsed(); }
void EndPartialBuild(PartialUpdateResult aResult) {
mPartialBuildDuration = Elapsed();
mPartialUpdateResult = aResult;
}
double Elapsed() {
return (mozilla::TimeStamp::Now() - mStartTime).ToMilliseconds();
}
const char* FailReasonString() const {
switch (mPartialUpdateFailReason) {
case PartialUpdateFailReason::NA:
return "N/A";
case PartialUpdateFailReason::EmptyList:
return "Empty list";
case PartialUpdateFailReason::RebuildLimit:
return "Rebuild limit";
case PartialUpdateFailReason::FrameType:
return "Frame type";
case PartialUpdateFailReason::Disabled:
return "Disabled";
case PartialUpdateFailReason::Content:
return "Content";
default:
MOZ_ASSERT_UNREACHABLE("Enum value not handled!");
}
}
unsigned int mNewItems;
unsigned int mRebuiltItems;
unsigned int mRemovedItems;
unsigned int mReusedItems;
unsigned int mTotalItems;
mozilla::TimeStamp mStartTime;
double mPartialBuildDuration;
double mFullBuildDuration;
PartialUpdateFailReason mPartialUpdateFailReason;
PartialUpdateResult mPartialUpdateResult;
};
struct RetainedDisplayListBuilder {
RetainedDisplayListBuilder(nsIFrame* aReferenceFrame,
nsDisplayListBuilderMode aMode, bool aBuildCaret)
@ -102,7 +173,7 @@ struct RetainedDisplayListBuilder {
nsDisplayList* List() { return &mList; }
enum class PartialUpdateResult { Failed, NoChange, Updated };
RetainedDisplayListMetrics* Metrics() { return &mMetrics; }
PartialUpdateResult AttemptPartialUpdate(
nscolor aBackstop, mozilla::DisplayListChecker* aChecker);
@ -118,6 +189,19 @@ struct RetainedDisplayListBuilder {
NS_DECLARE_FRAME_PROPERTY_DELETABLE(Cached, RetainedDisplayListBuilder)
private:
void IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem);
/**
* Invalidates the current and previous caret frame if they have changed.
*/
void InvalidateCaretFramesIfNeeded(nsTArray<nsIFrame*>& aModifiedFrames);
/**
* A simple early exit heuristic to avoid slow partial display list rebuilds.
* Returns true if a partial display list build should be attempted.
*/
bool ShouldBuildPartial(nsTArray<nsIFrame*>& aModifiedFrames);
/**
* Recursively pre-processes the old display list tree before building the
* new partial display lists, and serializes the old list into an array,
@ -156,19 +240,19 @@ struct RetainedDisplayListBuilder {
nsRect* aOutDirty,
AnimatedGeometryRoot** aOutModifiedAGR,
nsTArray<nsIFrame*>& aOutFramesWithProps);
bool ProcessFrame(nsIFrame* aFrame, nsDisplayListBuilder* aBuilder,
nsIFrame* aStopAtFrame,
nsTArray<nsIFrame*>& aOutFramesWithProps,
const bool aStopAtStackingContext, nsRect* aOutDirty,
AnimatedGeometryRoot** aOutModifiedAGR);
void IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem);
friend class MergeState;
nsDisplayListBuilder mBuilder;
RetainedDisplayList mList;
WeakFrame mPreviousCaret;
RetainedDisplayListMetrics mMetrics;
};
#endif // RETAINEDDISPLAYLISTBUILDER_H_