зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1420298 'layout.display-list.retain.verify' to debug retained-dl - r=mattwoodrow
Setting the 'layout.display-list.retain.verify' gfxPrefs implies 'layout.display-list.build-twice', and then compares the retained-built tree to the non-retained one, and outputs differences&trees to the terminal. MozReview-Commit-ID: 3dnyIUTbtH8 --HG-- extra : rebase_source : 36a6b8f029d0bd1339557e7c630906311ecf1254
This commit is contained in:
Родитель
23677e6410
Коммит
91a021aeb0
|
@ -650,6 +650,7 @@ private:
|
|||
|
||||
DECL_GFX_PREF(Live, "layout.display-list.build-twice", LayoutDisplayListBuildTwice, bool, false);
|
||||
DECL_GFX_PREF(Live, "layout.display-list.retain", LayoutRetainDisplayList, bool, true);
|
||||
DECL_GFX_PREF(Live, "layout.display-list.retain.verify", LayoutVerifyRetainDisplayList, bool, false);
|
||||
DECL_GFX_PREF(Live, "layout.display-list.rebuild-frame-limit", LayoutRebuildFrameLimit, uint32_t, 500);
|
||||
DECL_GFX_PREF(Live, "layout.display-list.dump", LayoutDumpDisplayList, bool, false);
|
||||
DECL_GFX_PREF(Live, "layout.display-list.dump-content", LayoutDumpDisplayListContent, bool, false);
|
||||
|
|
|
@ -128,6 +128,7 @@
|
|||
#include "mozilla/layers/WebRenderLayerManager.h"
|
||||
#include "prenv.h"
|
||||
#include "RetainedDisplayListBuilder.h"
|
||||
#include "DisplayListChecker.h"
|
||||
#include "TextDrawTarget.h"
|
||||
#include "nsDeckFrame.h"
|
||||
#include "nsIEffectiveTLDService.h" // for IsInStyloBlocklist
|
||||
|
@ -3845,16 +3846,26 @@ nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
|
|||
builder.IsBuildingLayerEventRegions() &&
|
||||
nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell));
|
||||
|
||||
DisplayListChecker beforeMergeChecker;
|
||||
DisplayListChecker afterMergeChecker;
|
||||
|
||||
// Attempt to do a partial build and merge into the existing list.
|
||||
// This calls BuildDisplayListForStacking context on a subset of the
|
||||
// viewport.
|
||||
bool merged = false;
|
||||
|
||||
if (useRetainedBuilder) {
|
||||
if (gfxPrefs::LayoutVerifyRetainDisplayList()) {
|
||||
beforeMergeChecker.Set(&list, "BM");
|
||||
}
|
||||
merged = retainedBuilder->AttemptPartialUpdate(aBackstop);
|
||||
if (merged && beforeMergeChecker) {
|
||||
afterMergeChecker.Set(&list, "AM");
|
||||
}
|
||||
}
|
||||
|
||||
if (merged && gfxPrefs::LayoutDisplayListBuildTwice()) {
|
||||
if (merged &&
|
||||
(gfxPrefs::LayoutDisplayListBuildTwice() || afterMergeChecker)) {
|
||||
merged = false;
|
||||
if (gfxPrefs::LayersDrawFPS()) {
|
||||
if (RefPtr<LayerManager> lm = builder.GetWidgetLayerManager()) {
|
||||
|
@ -3875,6 +3886,22 @@ nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
|
|||
AddExtraBackgroundItems(builder, list, aFrame, canvasArea, visibleRegion, aBackstop);
|
||||
|
||||
builder.LeavePresShell(aFrame, &list);
|
||||
|
||||
if (afterMergeChecker) {
|
||||
DisplayListChecker nonRetainedChecker(&list, "NR");
|
||||
std::stringstream ss;
|
||||
ss << "**** Differences between retained-after-merged (AM) and "
|
||||
<< "non-retained (NR) display lists:";
|
||||
if (!nonRetainedChecker.CompareList(afterMergeChecker, ss)) {
|
||||
ss << "\n\n*** non-retained display items:";
|
||||
nonRetainedChecker.Dump(ss);
|
||||
ss << "\n\n*** before-merge retained display items:";
|
||||
beforeMergeChecker.Dump(ss);
|
||||
ss << "\n\n*** after-merge retained display items:";
|
||||
afterMergeChecker.Dump(ss);
|
||||
fprintf(stderr, "%s\n\n", ss.str().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,371 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "DisplayListChecker.h"
|
||||
|
||||
#include "nsDisplayList.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class DisplayItemBlueprint;
|
||||
|
||||
// Stack node used during tree visits, to store the path to a display item.
|
||||
struct DisplayItemBlueprintStack
|
||||
{
|
||||
const DisplayItemBlueprintStack* mPrevious;
|
||||
const DisplayItemBlueprint* mItem;
|
||||
// Output stack to aSs, with format "name#index > ... > name#index".
|
||||
// Returns true if anything was output, false if empty.
|
||||
bool Output(std::stringstream& aSs) const;
|
||||
};
|
||||
|
||||
// Object representing a list of display items (either the top of the tree, or
|
||||
// an item's children), with just enough information to compare with another
|
||||
// tree and output useful debugging information.
|
||||
class DisplayListBlueprint
|
||||
{
|
||||
public:
|
||||
DisplayListBlueprint(nsDisplayList* aList, const char* aName)
|
||||
: DisplayListBlueprint(aList, 0, aName)
|
||||
{
|
||||
}
|
||||
|
||||
DisplayListBlueprint(nsDisplayList* aList,
|
||||
const char* aName,
|
||||
unsigned& aIndex)
|
||||
{
|
||||
processChildren(aList, aName, aIndex);
|
||||
}
|
||||
|
||||
// Find a display item with the given frame and per-frame key.
|
||||
// Returns empty string if not found.
|
||||
std::string Find(const nsIFrame* aFrame, uint32_t aPerFrameKey) const
|
||||
{
|
||||
const DisplayItemBlueprintStack stack{ nullptr, nullptr };
|
||||
return Find(aFrame, aPerFrameKey, stack);
|
||||
}
|
||||
|
||||
std::string Find(const nsIFrame* aFrame,
|
||||
uint32_t aPerFrameKey,
|
||||
const DisplayItemBlueprintStack& aStack) const;
|
||||
|
||||
// Compare this list with another one, output differences between the two
|
||||
// into aDiff.
|
||||
// Differences include: Display items from one tree for which a corresponding
|
||||
// item (same frame and per-frame key) cannot be found under corresponding
|
||||
// parent items.
|
||||
// Returns true if trees are similar, false if different.
|
||||
bool CompareList(const DisplayListBlueprint& aOther,
|
||||
std::stringstream& aDiff) const
|
||||
{
|
||||
const DisplayItemBlueprintStack stack{ nullptr, nullptr };
|
||||
const bool ab = CompareList(*this, aOther, aOther, aDiff, stack, stack);
|
||||
const bool ba =
|
||||
aOther.CompareList(aOther, *this, *this, aDiff, stack, stack);
|
||||
return ab && ba;
|
||||
}
|
||||
|
||||
bool CompareList(const DisplayListBlueprint& aRoot,
|
||||
const DisplayListBlueprint& aOther,
|
||||
const DisplayListBlueprint& aOtherRoot,
|
||||
std::stringstream& aDiff,
|
||||
const DisplayItemBlueprintStack& aStack,
|
||||
const DisplayItemBlueprintStack& aStackOther) const;
|
||||
|
||||
// Output this tree to aSs.
|
||||
void Dump(std::stringstream& aSs) const { Dump(aSs, 0); }
|
||||
|
||||
void Dump(std::stringstream& aSs, unsigned aDepth) const;
|
||||
|
||||
private:
|
||||
// Only used by first constructor, to call the 2nd constructor with an index
|
||||
// variable on the stack.
|
||||
DisplayListBlueprint(nsDisplayList* aList, unsigned aIndex, const char* aName)
|
||||
: DisplayListBlueprint(aList, aName, aIndex)
|
||||
{
|
||||
}
|
||||
|
||||
void processChildren(nsDisplayList* aList,
|
||||
const char* aName,
|
||||
unsigned& aIndex);
|
||||
|
||||
std::vector<DisplayItemBlueprint> mItems;
|
||||
};
|
||||
|
||||
// Object representing one display item, with just enough information to
|
||||
// compare with another item and output useful debugging information.
|
||||
class DisplayItemBlueprint
|
||||
{
|
||||
public:
|
||||
DisplayItemBlueprint(nsDisplayItem& aItem,
|
||||
const char* aName,
|
||||
unsigned& aIndex)
|
||||
: mListName(aName)
|
||||
, mIndex(++aIndex)
|
||||
, mIndexString(WriteIndex(aName, aIndex))
|
||||
, mIndexStringFW(WriteIndexFW(aName, aIndex))
|
||||
, mDisplayItemPointer(WriteDisplayItemPointer(aItem))
|
||||
, mDescription(WriteDescription(aName, aIndex, aItem))
|
||||
, mFrame(aItem.HasDeletedFrame() ? nullptr : aItem.Frame())
|
||||
, mPerFrameKey(aItem.GetPerFrameKey())
|
||||
, mChildren(aItem.GetChildren(), aName, aIndex)
|
||||
{
|
||||
}
|
||||
|
||||
// Compare this item with another one, based on frame and per-frame key.
|
||||
// Not recursive! I.e., children are not examined.
|
||||
bool CompareItem(const DisplayItemBlueprint& aOther,
|
||||
std::stringstream& aDiff) const
|
||||
{
|
||||
return mFrame == aOther.mFrame && mPerFrameKey == aOther.mPerFrameKey;
|
||||
}
|
||||
|
||||
void Dump(std::stringstream& aSs, unsigned aDepth) const;
|
||||
|
||||
const char* mListName;
|
||||
const unsigned mIndex;
|
||||
const std::string mIndexString;
|
||||
const std::string mIndexStringFW;
|
||||
const std::string mDisplayItemPointer;
|
||||
const std::string mDescription;
|
||||
|
||||
// For pointer comparison only, do not dereference!
|
||||
const nsIFrame* const mFrame;
|
||||
const uint32_t mPerFrameKey;
|
||||
|
||||
const DisplayListBlueprint mChildren;
|
||||
|
||||
private:
|
||||
static std::string WriteIndex(const char* aName, unsigned aIndex)
|
||||
{
|
||||
return nsPrintfCString("%s#%u", aName, aIndex).get();
|
||||
}
|
||||
|
||||
static std::string WriteIndexFW(const char* aName, unsigned aIndex)
|
||||
{
|
||||
return nsPrintfCString("%s#%4u", aName, aIndex).get();
|
||||
}
|
||||
|
||||
static std::string WriteDisplayItemPointer(nsDisplayItem& aItem)
|
||||
{
|
||||
return nsPrintfCString("0x%p", &aItem).get();
|
||||
}
|
||||
|
||||
static std::string WriteDescription(const char* aName,
|
||||
unsigned aIndex,
|
||||
nsDisplayItem& aItem)
|
||||
{
|
||||
if (aItem.HasDeletedFrame()) {
|
||||
return nsPrintfCString(
|
||||
"%s %s#%u 0x%p f=0x0", aItem.Name(), aName, aIndex, &aItem)
|
||||
.get();
|
||||
}
|
||||
|
||||
const nsIFrame* f = aItem.Frame();
|
||||
nsAutoString contentData;
|
||||
#ifdef DEBUG_FRAME_DUMP
|
||||
f->GetFrameName(contentData);
|
||||
#endif
|
||||
nsIContent* content = f->GetContent();
|
||||
if (content) {
|
||||
nsString tmp;
|
||||
if (content->GetID()) {
|
||||
content->GetID()->ToString(tmp);
|
||||
contentData.AppendLiteral(" id:");
|
||||
contentData.Append(tmp);
|
||||
}
|
||||
const nsAttrValue* classes =
|
||||
content->IsElement() ? content->AsElement()->GetClasses() : nullptr;
|
||||
if (classes) {
|
||||
classes->ToString(tmp);
|
||||
contentData.AppendLiteral(" class:");
|
||||
contentData.Append(tmp);
|
||||
}
|
||||
}
|
||||
return nsPrintfCString("%s %s#%u p=0x%p f=0x%p(%s) key=%" PRIu32,
|
||||
aItem.Name(),
|
||||
aName,
|
||||
aIndex,
|
||||
&aItem,
|
||||
f,
|
||||
NS_ConvertUTF16toUTF8(contentData).get(),
|
||||
aItem.GetPerFrameKey())
|
||||
.get();
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
DisplayListBlueprint::processChildren(nsDisplayList* aList,
|
||||
const char* aName,
|
||||
unsigned& aIndex)
|
||||
{
|
||||
if (!aList) {
|
||||
return;
|
||||
}
|
||||
const uint32_t n = aList->Count();
|
||||
if (n == 0) {
|
||||
return;
|
||||
}
|
||||
mItems.reserve(n);
|
||||
for (nsDisplayItem* item = aList->GetBottom(); item;
|
||||
item = item->GetAbove()) {
|
||||
mItems.emplace_back(*item, aName, aIndex);
|
||||
}
|
||||
MOZ_ASSERT(mItems.size() == n);
|
||||
}
|
||||
|
||||
bool
|
||||
DisplayItemBlueprintStack::Output(std::stringstream& aSs) const
|
||||
{
|
||||
const bool output = mPrevious ? mPrevious->Output(aSs) : false;
|
||||
if (mItem) {
|
||||
if (output) {
|
||||
aSs << " > ";
|
||||
}
|
||||
aSs << mItem->mIndexString;
|
||||
return true;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string
|
||||
DisplayListBlueprint::Find(const nsIFrame* aFrame,
|
||||
uint32_t aPerFrameKey,
|
||||
const DisplayItemBlueprintStack& aStack) const
|
||||
{
|
||||
for (const DisplayItemBlueprint& item : mItems) {
|
||||
if (item.mFrame == aFrame && item.mPerFrameKey == aPerFrameKey) {
|
||||
std::stringstream ss;
|
||||
if (aStack.Output(ss)) {
|
||||
ss << " > ";
|
||||
}
|
||||
ss << item.mDescription;
|
||||
return ss.str();
|
||||
}
|
||||
const DisplayItemBlueprintStack stack = { &aStack, &item };
|
||||
std::string s = item.mChildren.Find(aFrame, aPerFrameKey, stack);
|
||||
if (!s.empty()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool
|
||||
DisplayListBlueprint::CompareList(
|
||||
const DisplayListBlueprint& aRoot,
|
||||
const DisplayListBlueprint& aOther,
|
||||
const DisplayListBlueprint& aOtherRoot,
|
||||
std::stringstream& aDiff,
|
||||
const DisplayItemBlueprintStack& aStack,
|
||||
const DisplayItemBlueprintStack& aStackOther) const
|
||||
{
|
||||
bool same = true;
|
||||
for (const DisplayItemBlueprint& itemBefore : mItems) {
|
||||
bool found = false;
|
||||
for (const DisplayItemBlueprint& itemAfter : aOther.mItems) {
|
||||
if (itemBefore.CompareItem(itemAfter, aDiff)) {
|
||||
found = true;
|
||||
|
||||
const DisplayItemBlueprintStack stack = { &aStack, &itemBefore };
|
||||
const DisplayItemBlueprintStack stackOther = { &aStackOther,
|
||||
&itemAfter };
|
||||
if (!itemBefore.mChildren.CompareList(aRoot,
|
||||
itemAfter.mChildren,
|
||||
aOtherRoot,
|
||||
aDiff,
|
||||
stack,
|
||||
stackOther)) {
|
||||
same = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
same = false;
|
||||
aDiff << "\n";
|
||||
if (aStack.Output(aDiff)) {
|
||||
aDiff << " > ";
|
||||
}
|
||||
aDiff << itemBefore.mDescription;
|
||||
aDiff << "\n * Cannot find corresponding item under ";
|
||||
if (!aStackOther.Output(aDiff)) {
|
||||
if (!aOtherRoot.mItems.empty()) {
|
||||
aDiff << aOtherRoot.mItems[0].mListName;
|
||||
} else {
|
||||
aDiff << "other root";
|
||||
}
|
||||
}
|
||||
std::string elsewhere =
|
||||
aOtherRoot.Find(itemBefore.mFrame, itemBefore.mPerFrameKey);
|
||||
if (!elsewhere.empty()) {
|
||||
aDiff << "\n * But found: " << elsewhere;
|
||||
}
|
||||
}
|
||||
}
|
||||
return same;
|
||||
}
|
||||
|
||||
void
|
||||
DisplayListBlueprint::Dump(std::stringstream& aSs, unsigned aDepth) const
|
||||
{
|
||||
for (const DisplayItemBlueprint& item : mItems) {
|
||||
item.Dump(aSs, aDepth);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DisplayItemBlueprint::Dump(std::stringstream& aSs, unsigned aDepth) const
|
||||
{
|
||||
aSs << "\n" << mIndexStringFW << " ";
|
||||
for (unsigned i = 0; i < aDepth; ++i) {
|
||||
aSs << " ";
|
||||
}
|
||||
aSs << mDescription;
|
||||
mChildren.Dump(aSs, aDepth + 1);
|
||||
}
|
||||
|
||||
DisplayListChecker::DisplayListChecker()
|
||||
: mBlueprint(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
DisplayListChecker::DisplayListChecker(nsDisplayList* aList, const char* aName)
|
||||
: mBlueprint(MakeUnique<DisplayListBlueprint>(aList, aName))
|
||||
{
|
||||
}
|
||||
|
||||
DisplayListChecker::~DisplayListChecker() = default;
|
||||
|
||||
void
|
||||
DisplayListChecker::Set(nsDisplayList* aList, const char* aName)
|
||||
{
|
||||
mBlueprint = MakeUnique<DisplayListBlueprint>(aList, aName);
|
||||
}
|
||||
|
||||
// Compare this list with another one, output differences between the two
|
||||
// into aDiff.
|
||||
// Differences include: Display items from one tree for which a corresponding
|
||||
// item (same frame and per-frame key) cannot be found under corresponding
|
||||
// parent items.
|
||||
// Returns true if trees are similar, false if different.
|
||||
bool
|
||||
DisplayListChecker::CompareList(const DisplayListChecker& aOther,
|
||||
std::stringstream& aDiff) const
|
||||
{
|
||||
MOZ_ASSERT(mBlueprint);
|
||||
MOZ_ASSERT(aOther.mBlueprint);
|
||||
return mBlueprint->CompareList(*aOther.mBlueprint, aDiff);
|
||||
}
|
||||
|
||||
void
|
||||
DisplayListChecker::Dump(std::stringstream& aSs) const
|
||||
{
|
||||
MOZ_ASSERT(mBlueprint);
|
||||
mBlueprint->Dump(aSs);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,49 @@
|
|||
/* -*- 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 DisplayListChecker_h__
|
||||
#define DisplayListChecker_h__
|
||||
|
||||
#include <sstream>
|
||||
#include <mozilla/UniquePtr.h>
|
||||
|
||||
class nsDisplayList;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class DisplayListBlueprint;
|
||||
|
||||
class DisplayListChecker
|
||||
{
|
||||
public:
|
||||
DisplayListChecker();
|
||||
DisplayListChecker(nsDisplayList* aList, const char* aName);
|
||||
|
||||
~DisplayListChecker();
|
||||
|
||||
void Set(nsDisplayList* aList, const char* aName);
|
||||
|
||||
explicit operator bool() const { return mBlueprint.get(); }
|
||||
|
||||
// Compare this list with another one, output differences between the two
|
||||
// into aDiff.
|
||||
// Differences include: Display items from one tree for which a corresponding
|
||||
// item (same frame and per-frame key) cannot be found under corresponding
|
||||
// parent items.
|
||||
// Returns true if trees are similar, false if different.
|
||||
bool CompareList(const DisplayListChecker& aOther,
|
||||
std::stringstream& aDiff) const;
|
||||
|
||||
// Output this tree to aSs.
|
||||
void Dump(std::stringstream& aSs) const;
|
||||
|
||||
private:
|
||||
UniquePtr<DisplayListBlueprint> mBlueprint;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DisplayListChecker_h__
|
|
@ -35,6 +35,7 @@ UNIFIED_SOURCES += [
|
|||
'DisplayItemClip.cpp',
|
||||
'DisplayItemClipChain.cpp',
|
||||
'DisplayItemScrollClip.cpp',
|
||||
'DisplayListChecker.cpp',
|
||||
'DisplayListClipState.cpp',
|
||||
'DottedCornerFinder.cpp',
|
||||
'FrameLayerBuilder.cpp',
|
||||
|
|
Загрузка…
Ссылка в новой задаче