Bug 1238328: Purge stored command lists by calling EndDraw/BeginDraw on a regular basis when they're used. r=jrmuizel draft
authorBas Schouten <bschouten@mozilla.com>
Mon, 11 Jan 2016 16:38:10 +0100
changeset 320515 5041fa82907837c50e657c87afeda85be40b25d7
parent 320358 359f86fecbc2a6531d9018fb81a74fd26502f5a1
child 512757 b5d5fca3db8c90440952e82c87ce88ebacd42318
push id9218
push userbschouten@mozilla.com
push dateMon, 11 Jan 2016 15:38:41 +0000
reviewersjrmuizel
bugs1238328
milestone46.0a1
Bug 1238328: Purge stored command lists by calling EndDraw/BeginDraw on a regular basis when they're used. r=jrmuizel
gfx/2d/DrawTargetD2D1.cpp
gfx/2d/DrawTargetD2D1.h
--- 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_ */