Bug 1488599 - Part 1: Add RetainedDisplayListData that will store frame invalidation information r=mattwoodrow

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Miko Mynttinen 2018-09-17 14:41:08 +00:00
Родитель d3a9739ed5
Коммит f0b6dd8c41
7 изменённых файлов: 161 добавлений и 108 удалений

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

@ -479,6 +479,14 @@ nsLayoutUtils::AreRetainedDisplayListsEnabled()
return false;
}
bool
nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(nsIFrame* aFrame)
{
const nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
MOZ_ASSERT(displayRoot);
return displayRoot->HasProperty(RetainedDisplayListBuilder::Cached());
}
bool
nsLayoutUtils::GPUImageScalingEnabled()
{
@ -1188,15 +1196,8 @@ nsLayoutUtils::InvalidateForDisplayPortChange(nsIContent* aContent,
// rect properties on so we can find the frame later to remove the properties.
frame->SchedulePaint();
if (!nsLayoutUtils::AreRetainedDisplayListsEnabled()) {
return;
}
nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(frame);
RetainedDisplayListBuilder* retainedBuilder =
displayRoot->GetProperty(RetainedDisplayListBuilder::Cached());
if (!retainedBuilder) {
if (!nsLayoutUtils::AreRetainedDisplayListsEnabled() ||
!nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(frame)) {
return;
}
@ -1208,18 +1209,12 @@ nsLayoutUtils::InvalidateForDisplayPortChange(nsIContent* aContent,
frame->SetProperty(nsDisplayListBuilder::DisplayListBuildingDisplayPortRect(), rect);
frame->SetHasOverrideDirtyRegion(true);
nsIFrame* rootFrame = frame->PresContext()->PresShell()->GetRootFrame();
nsIFrame* rootFrame = frame->PresShell()->GetRootFrame();
MOZ_ASSERT(rootFrame);
nsTArray<nsIFrame*>* frames =
rootFrame->GetProperty(nsIFrame::OverriddenDirtyRectFrameList());
if (!frames) {
frames = new nsTArray<nsIFrame*>();
rootFrame->SetProperty(nsIFrame::OverriddenDirtyRectFrameList(), frames);
}
frames->AppendElement(frame);
RetainedDisplayListData* data =
GetOrSetRetainedDisplayListData(rootFrame);
data->Flags(frame) |= RetainedDisplayListData::FrameFlags::HasProps;
}
if (aHadDisplayPort) {

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

@ -2375,6 +2375,8 @@ public:
*/
static bool AreRetainedDisplayListsEnabled();
static bool DisplayRootHasRetainedDisplayListBuilder(nsIFrame* aFrame);
/**
* Find a suitable scale for a element (aFrame's content) over the course of any
* animations and transitions of the CSS transform property on the

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

@ -829,12 +829,14 @@ nsFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData)
nsIFrame* rootFrame = shell->GetRootFrame();
MOZ_ASSERT(rootFrame);
if (this != rootFrame) {
nsTArray<nsIFrame*>* modifiedFrames =
rootFrame->GetProperty(nsIFrame::ModifiedFrameList());
if (modifiedFrames) {
MOZ_ASSERT(!modifiedFrames->Contains(this),
"A dtor added this frame to ModifiedFrameList");
}
const RetainedDisplayListData* data =
GetRetainedDisplayListData(rootFrame);
const bool inModifiedList = data &&
(data->GetFlags(this) & RetainedDisplayListData::FrameFlags::Modified);
MOZ_ASSERT(!inModifiedList,
"A dtor added this frame to modified frames list!");
}
}
#endif
@ -965,36 +967,30 @@ nsIFrame::RemoveDisplayItemDataForDeletion()
delete items;
}
if (IsFrameModified()) {
nsIFrame* rootFrame = PresShell()->GetRootFrame();
MOZ_ASSERT(rootFrame);
nsTArray<nsIFrame*>* modifiedFrames =
rootFrame->GetProperty(nsIFrame::ModifiedFrameList());
MOZ_ASSERT(modifiedFrames);
for (auto& frame : *modifiedFrames) {
if (frame == this) {
frame = nullptr;
break;
}
}
if (!nsLayoutUtils::AreRetainedDisplayListsEnabled()) {
// Retained display lists are disabled, no need to update
// RetainedDisplayListData.
return;
}
if (HasOverrideDirtyRegion()) {
nsIFrame* rootFrame = PresShell()->GetRootFrame();
MOZ_ASSERT(rootFrame);
const bool updateData =
IsFrameModified() || HasOverrideDirtyRegion() || MayHaveWillChangeBudget();
nsTArray<nsIFrame*>* frames =
rootFrame->GetProperty(nsIFrame::OverriddenDirtyRectFrameList());
MOZ_ASSERT(frames);
if (!updateData) {
// No RetainedDisplayListData to update.
return;
}
for (auto& frame : *frames) {
if (frame == this) {
frame = nullptr;
break;
}
}
nsIFrame* rootFrame = PresShell()->GetRootFrame();
MOZ_ASSERT(rootFrame);
RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
if (IsFrameModified() || HasOverrideDirtyRegion()) {
// Remove deleted frames from RetainedDisplayListData.
DebugOnly<bool> removed = data->Remove(this);
MOZ_ASSERT(removed,
"Frame had flags set, but it was not found in DisplayListData!");
}
}
@ -1017,13 +1013,7 @@ nsIFrame::MarkNeedsDisplayItemRebuild()
return;
}
nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
MOZ_ASSERT(displayRoot);
RetainedDisplayListBuilder* retainedBuilder =
displayRoot->GetProperty(RetainedDisplayListBuilder::Cached());
if (!retainedBuilder) {
if (!nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(this)) {
return;
}
@ -1034,36 +1024,13 @@ nsIFrame::MarkNeedsDisplayItemRebuild()
return;
}
nsTArray<nsIFrame*>* modifiedFrames =
rootFrame->GetProperty(nsIFrame::ModifiedFrameList());
if (!modifiedFrames) {
modifiedFrames = new nsTArray<nsIFrame*>();
rootFrame->SetProperty(nsIFrame::ModifiedFrameList(), modifiedFrames);
}
if (this == rootFrame) {
// If this is the root frame, then marking us as needing a display
// item rebuild implies the same for all our descendents. Clear them
// all out to reduce the number of modified frames we keep around.
for (nsIFrame* f : *modifiedFrames) {
if (f) {
f->SetFrameIsModified(false);
}
}
modifiedFrames->Clear();
} else if (modifiedFrames->Length() > gfxPrefs::LayoutRebuildFrameLimit()) {
// If the list starts getting too big, then just mark the root frame
// as needing a rebuild.
rootFrame->MarkNeedsDisplayItemRebuild();
return;
}
modifiedFrames->AppendElement(this);
MOZ_ASSERT(PresContext()->LayoutPhaseCount(eLayoutPhase_DisplayListBuilding) == 0);
RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
data->Flags(this) |= RetainedDisplayListData::FrameFlags::Modified;
SetFrameIsModified(true);
MOZ_ASSERT(
PresContext()->LayoutPhaseCount(eLayoutPhase_DisplayListBuilding) == 0);
// Hopefully this is cheap, but we could use a frame state bit to note
// the presence of dependencies to speed it up.
DisplayItemArray* items = GetProperty(DisplayItems());

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

@ -1251,8 +1251,6 @@ public:
NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(IBaselinePadProperty, nscoord)
NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BBaselinePadProperty, nscoord)
NS_DECLARE_FRAME_PROPERTY_DELETABLE(ModifiedFrameList, nsTArray<nsIFrame*>)
NS_DECLARE_FRAME_PROPERTY_DELETABLE(OverriddenDirtyRectFrameList, nsTArray<nsIFrame*>)
NS_DECLARE_FRAME_PROPERTY_DELETABLE(DisplayItems, DisplayItemArray)
NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BidiDataProperty, mozilla::FrameBidiData)

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

@ -41,6 +41,29 @@
using namespace mozilla;
RetainedDisplayListData*
GetRetainedDisplayListData(nsIFrame* aRootFrame)
{
RetainedDisplayListData* data =
aRootFrame->GetProperty(RetainedDisplayListData::DisplayListData());
return data;
}
RetainedDisplayListData*
GetOrSetRetainedDisplayListData(nsIFrame* aRootFrame)
{
RetainedDisplayListData* data = GetRetainedDisplayListData(aRootFrame);
if (!data) {
data = new RetainedDisplayListData();
aRootFrame->SetProperty(RetainedDisplayListData::DisplayListData(), data);
}
MOZ_ASSERT(data);
return data;
}
static void
MarkFramesWithItemsAndImagesModified(nsDisplayList* aList)
{
@ -643,30 +666,26 @@ TakeAndAddModifiedAndFramesWithPropsFromRootFrame(
{
MOZ_ASSERT(aRootFrame);
nsTArray<nsIFrame*>* frames =
aRootFrame->GetProperty(nsIFrame::ModifiedFrameList());
RetainedDisplayListData* data = GetRetainedDisplayListData(aRootFrame);
if (frames) {
for (nsIFrame* f : *frames) {
if (f) {
aModifiedFrames->AppendElement(f);
}
}
frames->Clear();
if (!data) {
return;
}
frames = aRootFrame->GetProperty(nsIFrame::OverriddenDirtyRectFrameList());
for (auto it = data->Iterator(); !it.Done(); it.Next()) {
nsIFrame* frame = it.Key();
const RetainedDisplayListData::FrameFlags& flags = it.Data();
if (frames) {
for (nsIFrame* f : *frames) {
if (f) {
aFramesWithProps->AppendElement(f);
}
if (flags & RetainedDisplayListData::FrameFlags::Modified) {
aModifiedFrames->AppendElement(frame);
}
frames->Clear();
if (flags & RetainedDisplayListData::FrameFlags::HasProps) {
aFramesWithProps->AppendElement(frame);
}
}
data->Clear();
}
struct CbData
@ -1230,6 +1249,11 @@ ClearFrameProps(nsTArray<nsIFrame*>& aFrames)
class AutoClearFramePropsArray
{
public:
explicit AutoClearFramePropsArray(size_t aCapacity)
: mFrames(aCapacity)
{
}
AutoClearFramePropsArray() = default;
~AutoClearFramePropsArray() { ClearFrameProps(mFrames); }
@ -1268,7 +1292,7 @@ RetainedDisplayListBuilder::AttemptPartialUpdate(
// We set the override dirty regions during ComputeRebuildRegion or in
// nsLayoutUtils::InvalidateForDisplayPortChange. The display port change also
// marks the frame modified, so those regions are cleared here as well.
AutoClearFramePropsArray modifiedFrames;
AutoClearFramePropsArray modifiedFrames(64);
AutoClearFramePropsArray framesWithProps;
GetModifiedAndFramesWithProps(
&mBuilder, &modifiedFrames.Frames(), &framesWithProps.Frames());

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

@ -9,11 +9,78 @@
#include "nsDisplayList.h"
#include "mozilla/Maybe.h"
#include "mozilla/TypedEnumBits.h"
namespace mozilla {
class DisplayListChecker;
} // namespace mozilla
/**
* RetainedDisplayListData contains frame invalidation information. It is stored
* in root frames, and used by RetainedDisplayListBuilder.
* Currently this is implemented as a map of frame pointers to flags.
*/
struct RetainedDisplayListData
{
NS_DECLARE_FRAME_PROPERTY_DELETABLE(DisplayListData, RetainedDisplayListData)
enum class FrameFlags : uint8_t
{
None = 0,
Modified = 1 << 0,
HasProps = 1 << 1,
HadWillChange = 1 << 2
};
/**
* Removes all the frames from this RetainedDisplayListData.
*/
void Clear() { mFrames.Clear(); }
/**
* Returns a mutable reference to flags set for the given |aFrame|. If the
* frame does not exist in this RetainedDisplayListData, it is added with
* default constructible flags FrameFlags::None.
*/
FrameFlags& Flags(nsIFrame* aFrame) { return mFrames.GetOrInsert(aFrame); }
/**
* Returns flags set for the given |aFrame|, or FrameFlags::None if the frame
* is not in this RetainedDisplayListData.
*/
FrameFlags GetFlags(nsIFrame* aFrame) const { return mFrames.Get(aFrame); }
/**
* Returns an iterator to the underlying frame storage.
*/
auto Iterator() { return mFrames.Iter(); }
/**
* Removes the given |aFrame| from this RetainedDisplayListData.
*/
bool Remove(nsIFrame* aFrame) { return mFrames.Remove(aFrame); }
private:
nsDataHashtable<nsPtrHashKey<nsIFrame>, FrameFlags> mFrames;
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(RetainedDisplayListData::FrameFlags)
/**
* Returns RetainedDisplayListData property for the given |aRootFrame|, or
* nullptr if the property is not set.
*/
RetainedDisplayListData*
GetRetainedDisplayListData(nsIFrame* aRootFrame);
/**
* Returns RetainedDisplayListData property for the given |aRootFrame|. Creates
* and sets a new RetainedDisplayListData property if it is not already set.
*/
RetainedDisplayListData*
GetOrSetRetainedDisplayListData(nsIFrame* aRootFrame);
struct RetainedDisplayListBuilder
{
RetainedDisplayListBuilder(nsIFrame* aReferenceFrame,

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

@ -3359,7 +3359,7 @@ public:
/**
* Remove all items from the list and call their destructors.
*/
void DeleteAll(nsDisplayListBuilder* aBuilder);
virtual void DeleteAll(nsDisplayListBuilder* aBuilder);
/**
* @return the item at the top of the list, or null if the list is empty
@ -3754,7 +3754,7 @@ public:
return *this;
}
void DeleteAll(nsDisplayListBuilder* aBuilder)
void DeleteAll(nsDisplayListBuilder* aBuilder) override
{
for (OldItemInfo& i : mOldItems) {
if (i.mItem) {