Bug 1405359 - Make ScrollingLayersHelper a more stateful class. r=jrmuizel

This makes ScrollingLayersHelper a non-RAII type class, and instead adds
methods to notify it of when we start processing a new transaction or a
new display item within the transaction. This patch has no functional
changes, it's non-obvious refactoring.

MozReview-Commit-ID: 3yq9sPiHMge

--HG--
extra : rebase_source : 286423f56de59211e320f015cb1004a1e98332b8
This commit is contained in:
Kartikaya Gupta 2017-10-24 16:15:00 -04:00
parent 7feb6820bd
commit 1ef110fd03
5 changed files with 83 additions and 47 deletions

View File

@ -5,21 +5,41 @@
#include "mozilla/layers/ScrollingLayersHelper.h" #include "mozilla/layers/ScrollingLayersHelper.h"
#include "DisplayItemClipChain.h"
#include "FrameMetrics.h" #include "FrameMetrics.h"
#include "mozilla/layers/StackingContextHelper.h" #include "mozilla/layers/StackingContextHelper.h"
#include "mozilla/webrender/WebRenderAPI.h" #include "mozilla/webrender/WebRenderAPI.h"
#include "nsDisplayList.h"
#include "UnitTransforms.h" #include "UnitTransforms.h"
namespace mozilla { namespace mozilla {
namespace layers { namespace layers {
ScrollingLayersHelper::ScrollingLayersHelper(nsDisplayItem* aItem, ScrollingLayersHelper::ScrollingLayersHelper()
wr::DisplayListBuilder& aBuilder, : mBuilder(nullptr)
const StackingContextHelper& aStackingContext, {
WebRenderCommandBuilder::ClipIdMap& aCache, }
bool aApzEnabled)
: mBuilder(&aBuilder) void
, mCache(aCache) ScrollingLayersHelper::BeginBuild(wr::DisplayListBuilder& aBuilder)
{
MOZ_ASSERT(!mBuilder);
mBuilder = &aBuilder;
MOZ_ASSERT(mCache.empty());
MOZ_ASSERT(mItemClipStack.empty());
}
void
ScrollingLayersHelper::EndBuild()
{
mBuilder = nullptr;
mCache.clear();
MOZ_ASSERT(mItemClipStack.empty());
}
void
ScrollingLayersHelper::BeginItem(nsDisplayItem* aItem,
const StackingContextHelper& aStackingContext)
{ {
int32_t auPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel(); int32_t auPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
@ -58,16 +78,17 @@ ScrollingLayersHelper::ScrollingLayersHelper(nsDisplayItem* aItem,
// the item's ASR. So for those cases we need to use the ClipAndScroll API. // the item's ASR. So for those cases we need to use the ClipAndScroll API.
bool needClipAndScroll = (leafmostId != scrollId); bool needClipAndScroll = (leafmostId != scrollId);
ItemClips clips;
// If we don't need a ClipAndScroll, ensure the item's ASR is at the top of // If we don't need a ClipAndScroll, ensure the item's ASR is at the top of
// the scroll stack // the scroll stack
if (!needClipAndScroll && mBuilder->TopmostScrollId() != scrollId) { if (!needClipAndScroll && mBuilder->TopmostScrollId() != scrollId) {
MOZ_ASSERT(leafmostId == scrollId); // because !needClipAndScroll MOZ_ASSERT(leafmostId == scrollId); // because !needClipAndScroll
mItemClips.mScrollId = Some(scrollId); clips.mScrollId = Some(scrollId);
} }
// And ensure the leafmost clip, if scrolled by that ASR, is at the top of the // And ensure the leafmost clip, if scrolled by that ASR, is at the top of the
// stack. // stack.
if (ids.second && aItem->GetClipChain()->mASR == leafmostASR) { if (ids.second && aItem->GetClipChain()->mASR == leafmostASR) {
mItemClips.mClipId = ids.second; clips.mClipId = ids.second;
} }
// If we need the ClipAndScroll, we want to replace the topmost scroll layer // If we need the ClipAndScroll, we want to replace the topmost scroll layer
// with the item's ASR but preseve the topmost clip (which is scrolled by // with the item's ASR but preseve the topmost clip (which is scrolled by
@ -76,14 +97,15 @@ ScrollingLayersHelper::ScrollingLayersHelper(nsDisplayItem* aItem,
// If mClipId is set that means we want to push it such that it's going // If mClipId is set that means we want to push it such that it's going
// to be the TopmostClipId(), but we haven't actually pushed it yet. // to be the TopmostClipId(), but we haven't actually pushed it yet.
// But we still want to take that instead of the actual current TopmostClipId(). // But we still want to take that instead of the actual current TopmostClipId().
Maybe<wr::WrClipId> clipId = mItemClips.mClipId; Maybe<wr::WrClipId> clipId = clips.mClipId;
if (!clipId) { if (!clipId) {
clipId = mBuilder->TopmostClipId(); clipId = mBuilder->TopmostClipId();
} }
mItemClips.mClipAndScroll = Some(std::make_pair(scrollId, clipId)); clips.mClipAndScroll = Some(std::make_pair(scrollId, clipId));
} }
mItemClips.Apply(mBuilder); clips.Apply(mBuilder);
mItemClipStack.push_back(clips);
} }
std::pair<Maybe<FrameMetrics::ViewID>, Maybe<wr::WrClipId>> std::pair<Maybe<FrameMetrics::ViewID>, Maybe<wr::WrClipId>>
@ -354,9 +376,20 @@ ScrollingLayersHelper::RecurseAndDefineAsr(nsDisplayItem* aItem,
return ids; return ids;
} }
void
ScrollingLayersHelper::EndItem(nsDisplayItem* aItem)
{
MOZ_ASSERT(!mItemClipStack.empty());
ItemClips& clips = mItemClipStack.back();
clips.Unapply(mBuilder);
mItemClipStack.pop_back();
}
ScrollingLayersHelper::~ScrollingLayersHelper() ScrollingLayersHelper::~ScrollingLayersHelper()
{ {
mItemClips.Unapply(mBuilder); MOZ_ASSERT(!mBuilder);
MOZ_ASSERT(mCache.empty());
MOZ_ASSERT(mItemClipStack.empty());
} }
void void

View File

@ -7,10 +7,12 @@
#define GFX_SCROLLINGLAYERSHELPER_H #define GFX_SCROLLINGLAYERSHELPER_H
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/layers/WebRenderCommandBuilder.h"
class nsDisplayItem;
namespace mozilla { namespace mozilla {
struct ActiveScrolledRoot;
struct DisplayItemClipChain; struct DisplayItemClipChain;
namespace wr { namespace wr {
@ -22,14 +24,17 @@ namespace layers {
struct FrameMetrics; struct FrameMetrics;
class StackingContextHelper; class StackingContextHelper;
class MOZ_RAII ScrollingLayersHelper class ScrollingLayersHelper
{ {
public: public:
ScrollingLayersHelper(nsDisplayItem* aItem, ScrollingLayersHelper();
wr::DisplayListBuilder& aBuilder,
const StackingContextHelper& aStackingContext, void BeginBuild(wr::DisplayListBuilder& aBuilder);
WebRenderCommandBuilder::ClipIdMap& aCache, void EndBuild();
bool aApzEnabled);
void BeginItem(nsDisplayItem* aItem,
const StackingContextHelper& aStackingContext);
void EndItem(nsDisplayItem* aItem);
~ScrollingLayersHelper(); ~ScrollingLayersHelper();
private: private:
@ -54,8 +59,20 @@ private:
int32_t aAppUnitsPerDevPixel, int32_t aAppUnitsPerDevPixel,
const StackingContextHelper& aSc); const StackingContextHelper& aSc);
// Note: two DisplayItemClipChain* A and B might actually be "equal" (as per
// DisplayItemClipChain::Equal(A, B)) even though they are not the same pointer
// (A != B). In this hopefully-rare case, they will get separate entries
// in this map when in fact we could collapse them. However, to collapse
// them involves writing a custom hash function for the pointer type such that
// A and B hash to the same things whenever DisplayItemClipChain::Equal(A, B)
// is true, and that will incur a performance penalty for all the hashmap
// operations, so is probably not worth it. With the current code we might
// end up creating multiple clips in WR that are effectively identical but
// have separate clip ids. Hopefully this won't happen very often.
typedef std::unordered_map<const DisplayItemClipChain*, wr::WrClipId> ClipIdMap;
wr::DisplayListBuilder* mBuilder; wr::DisplayListBuilder* mBuilder;
WebRenderCommandBuilder::ClipIdMap& mCache; ClipIdMap mCache;
struct ItemClips { struct ItemClips {
Maybe<FrameMetrics::ViewID> mScrollId; Maybe<FrameMetrics::ViewID> mScrollId;
@ -66,7 +83,7 @@ private:
void Unapply(wr::DisplayListBuilder* aBuilder); void Unapply(wr::DisplayListBuilder* aBuilder);
}; };
ItemClips mItemClips; std::vector<ItemClips> mItemClipStack;
}; };
} // namespace layers } // namespace layers

View File

@ -64,6 +64,7 @@ WebRenderCommandBuilder::BuildWebRenderCommands(wr::DisplayListBuilder& aBuilder
MOZ_ASSERT(mLayerScrollData.empty()); MOZ_ASSERT(mLayerScrollData.empty());
mLastCanvasDatas.Clear(); mLastCanvasDatas.Clear();
mLastAsr = nullptr; mLastAsr = nullptr;
mScrollingHelper.BeginBuild(aBuilder);
{ {
StackingContextHelper pageRootSc(sc, aBuilder); StackingContextHelper pageRootSc(sc, aBuilder);
@ -94,7 +95,7 @@ WebRenderCommandBuilder::BuildWebRenderCommands(wr::DisplayListBuilder& aBuilder
aScrollData.AddLayerData(*i); aScrollData.AddLayerData(*i);
} }
mLayerScrollData.clear(); mLayerScrollData.clear();
mClipIdCache.clear(); mScrollingHelper.EndBuild();
// Remove the user data those are not displayed on the screen and // Remove the user data those are not displayed on the screen and
// also reset the data to unused for next transaction. // also reset the data to unused for next transaction.
@ -213,16 +214,14 @@ WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(nsDisplayList* a
} }
} }
{ // ensure the scope of ScrollingLayersHelper is maintained mScrollingHelper.BeginItem(item, aSc);
ScrollingLayersHelper clip(item, aBuilder, aSc, mClipIdCache, apzEnabled); // Note: this call to CreateWebRenderCommands can recurse back into
// this function if the |item| is a wrapper for a sublist.
// Note: this call to CreateWebRenderCommands can recurse back into if (!item->CreateWebRenderCommands(aBuilder, aResources, aSc, mManager,
// this function if the |item| is a wrapper for a sublist. aDisplayListBuilder)) {
if (!item->CreateWebRenderCommands(aBuilder, aResources, aSc, mManager, PushItemAsImage(item, aBuilder, aResources, aSc, aDisplayListBuilder);
aDisplayListBuilder)) {
PushItemAsImage(item, aBuilder, aResources, aSc, aDisplayListBuilder);
}
} }
mScrollingHelper.EndItem(item);
if (apzEnabled) { if (apzEnabled) {
if (forceNewLayerData) { if (forceNewLayerData) {

View File

@ -7,6 +7,7 @@
#define GFX_WEBRENDERCOMMANDBUILDER_H #define GFX_WEBRENDERCOMMANDBUILDER_H
#include "mozilla/webrender/WebRenderAPI.h" #include "mozilla/webrender/WebRenderAPI.h"
#include "mozilla/layers/ScrollingLayersHelper.h"
#include "mozilla/layers/WebRenderMessages.h" #include "mozilla/layers/WebRenderMessages.h"
#include "mozilla/layers/WebRenderScrollData.h" #include "mozilla/layers/WebRenderScrollData.h"
#include "mozilla/layers/WebRenderUserData.h" #include "mozilla/layers/WebRenderUserData.h"
@ -145,22 +146,9 @@ public:
return res.forget(); return res.forget();
} }
public:
// Note: two DisplayItemClipChain* A and B might actually be "equal" (as per
// DisplayItemClipChain::Equal(A, B)) even though they are not the same pointer
// (A != B). In this hopefully-rare case, they will get separate entries
// in this map when in fact we could collapse them. However, to collapse
// them involves writing a custom hash function for the pointer type such that
// A and B hash to the same things whenever DisplayItemClipChain::Equal(A, B)
// is true, and that will incur a performance penalty for all the hashmap
// operations, so is probably not worth it. With the current code we might
// end up creating multiple clips in WR that are effectively identical but
// have separate clip ids. Hopefully this won't happen very often.
typedef std::unordered_map<const DisplayItemClipChain*, wr::WrClipId> ClipIdMap;
private: private:
WebRenderLayerManager* mManager; WebRenderLayerManager* mManager;
ClipIdMap mClipIdCache; ScrollingLayersHelper mScrollingHelper;
// These fields are used to save a copy of the display list for // These fields are used to save a copy of the display list for
// empty transactions in layers-free mode. // empty transactions in layers-free mode.

View File

@ -12,7 +12,6 @@
#include "mozilla/gfx/DrawEventRecorder.h" #include "mozilla/gfx/DrawEventRecorder.h"
#include "mozilla/layers/CompositorBridgeChild.h" #include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/layers/IpcResourceUpdateQueue.h" #include "mozilla/layers/IpcResourceUpdateQueue.h"
#include "mozilla/layers/ScrollingLayersHelper.h"
#include "mozilla/layers/StackingContextHelper.h" #include "mozilla/layers/StackingContextHelper.h"
#include "mozilla/layers/TextureClient.h" #include "mozilla/layers/TextureClient.h"
#include "mozilla/layers/WebRenderBridgeChild.h" #include "mozilla/layers/WebRenderBridgeChild.h"