Bug 1238328: Purge stored command lists by calling EndDraw/BeginDraw on a regular basis when they're used. r=jrmuizel
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -27,16 +27,17 @@ ID2D1Factory1* DrawTargetD2D1::mFactory
ID2D1Factory1 *D2DFactory1()
{
return DrawTargetD2D1::factory();
}
DrawTargetD2D1::DrawTargetD2D1()
: mPushedLayers(1)
+ , mPushedLayersSincePurge(0)
{
}
DrawTargetD2D1::~DrawTargetD2D1()
{
PopAllClips();
if (mSnapshot) {
@@ -82,20 +83,38 @@ DrawTargetD2D1::Snapshot()
mDC->Flush();
mSnapshot = new SourceSurfaceD2D1(mBitmap, mDC, mFormat, mSize, this);
RefPtr<SourceSurface> snapshot(mSnapshot);
return snapshot.forget();
}
+// Command lists are kept around by device contexts until EndDraw is called,
+// this can cause issues with memory usage (see bug 1238328). EndDraw/BeginDraw
+// are expensive though, especially relatively when little work is done, so
+// we try to reduce the amount of times we execute these purges.
+static const uint32_t kPushedLayersBeforePurge = 25;
+
void
DrawTargetD2D1::Flush()
{
- mDC->Flush();
+ if ((mPushedLayersSincePurge >= kPushedLayersBeforePurge) &&
+ mPushedLayers.size() == 1) {
+ // It's important to pop all clips as otherwise layers can forget about
+ // their clip when doing an EndDraw. When we have layers pushed we cannot
+ // easily pop all underlying clips to delay the purge until we have no
+ // layers pushed.
+ PopAllClips();
+ mPushedLayersSincePurge = 0;
+ mDC->EndDraw();
+ mDC->BeginDraw();
+ } else {
+ mDC->Flush();
+ }
// We no longer depend on any target.
for (TargetSet::iterator iter = mDependingOnTargets.begin();
iter != mDependingOnTargets.end(); iter++) {
(*iter)->mDependentTargets.erase(this);
}
mDependingOnTargets.clear();
}
@@ -778,16 +797,18 @@ DrawTargetD2D1::PushLayer(bool aOpaque,
pushedLayer.mIsOpaque = aOpaque;
pushedLayer.mOldPermitSubpixelAA = mPermitSubpixelAA;
mPermitSubpixelAA = aOpaque;
mDC->CreateCommandList(getter_AddRefs(pushedLayer.mCurrentList));
mPushedLayers.push_back(pushedLayer);
mDC->SetTarget(CurrentTarget());
+
+ mPushedLayersSincePurge++;
}
void
DrawTargetD2D1::PopLayer()
{
MOZ_ASSERT(CurrentLayer().mPushedClips.size() == 0);
RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -261,16 +261,18 @@ private:
// The latest snapshot of this surface. This needs to be told when this
// target is modified. We keep it alive as a cache.
RefPtr<SourceSurfaceD2D1> mSnapshot;
// A list of targets we need to flush when we're modified.
TargetSet mDependentTargets;
// A list of targets which have this object in their mDependentTargets set
TargetSet mDependingOnTargets;
+ uint32_t mPushedLayersSincePurge;
+
static ID2D1Factory1 *mFactory;
static IDWriteFactory *mDWriteFactory;
};
}
}
#endif /* MOZILLA_GFX_DRAWTARGETD2D_H_ */