Bug 1474883 - Ensure D2D glyph cache is pruned after rendering 1000 transformed glyphs. r?Bas draft
authorJamie Nicol <jnicol@mozilla.com>
Thu, 26 Jul 2018 15:23:19 +0100
changeset 824273 f685d3ce04eb848a5c28c2d72ed91e9ae78d83ae
parent 824238 dead9fcddd4a25fd36d54ab7eb782d7d9b8bb7a1
push id117853
push userbmo:jnicol@mozilla.com
push dateMon, 30 Jul 2018 13:51:13 +0000
reviewersBas
bugs1474883
milestone63.0a1
Bug 1474883 - Ensure D2D glyph cache is pruned after rendering 1000 transformed glyphs. r?Bas Rendering glyphs at many different rotations was causing the D2D glyph cache to grow very large. Calling EndDraw/BeginDraw will prune the cache, but is costly, so only do it for every 1000 glyphs. MozReview-Commit-ID: HUFpxDvYAzQ
gfx/2d/DrawTargetD2D1.cpp
gfx/2d/DrawTargetD2D1.h
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -43,16 +43,17 @@ RefPtr<ID2D1Factory1> D2DFactory()
 {
   return DrawTargetD2D1::factory();
 }
 
 DrawTargetD2D1::DrawTargetD2D1()
   : mPushedLayers(1)
   , mSnapshotLock(make_shared<Mutex>("DrawTargetD2D1::mSnapshotLock"))
   , mUsedCommandListsSincePurge(0)
+  , mTransformedGlyphsSinceLastPurge(0)
   , mComplexBlendsWithListInList(0)
   , mDeviceSeq(0)
 {
 }
 
 DrawTargetD2D1::~DrawTargetD2D1()
 {
   PopAllClips();
@@ -159,16 +160,19 @@ DrawTargetD2D1::IntoLuminanceSource(Lumi
  return MakeAndAddRef<SourceSurfaceD2D1>(luminanceOutput, mDC, SurfaceFormat::B8G8R8A8, mSize);
 }
 
 // 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;
+// Rendering glyphs with different transforms causes the glyph cache to grow
+// very large (see bug 1474883) so we must call EndDraw every so often.
+static const uint32_t kTransformedGlyphsBeforePurge = 1000;
 
 void
 DrawTargetD2D1::Flush()
 {
   FlushInternal();
 }
 
 void
@@ -725,16 +729,20 @@ DrawTargetD2D1::FillGlyphs(ScaledFont *a
       mDC->SetTransform(D2DMatrix(mTransform));
     }
   }
 
   if (brush) {
     mDC->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush);
   }
 
+  if (mTransform.HasNonTranslation()) {
+    mTransformedGlyphsSinceLastPurge += aBuffer.mNumGlyphs;
+  }
+
   if (needsRepushedLayers) {
     PopClipsFromDC(mDC);
 
     if (!mTransform.IsRectilinear()) {
       mDC->PopLayer();
     }
   }
 
@@ -1280,24 +1288,26 @@ DrawTargetD2D1::CleanupD2D()
     mFactory = nullptr;
   }
 }
 
 void
 DrawTargetD2D1::FlushInternal(bool aHasDependencyMutex /* = false */)
 {
   if (IsDeviceContextValid()) {
-    if ((mUsedCommandListsSincePurge >= kPushedLayersBeforePurge) &&
+    if ((mUsedCommandListsSincePurge >= kPushedLayersBeforePurge ||
+         mTransformedGlyphsSinceLastPurge >= kTransformedGlyphsBeforePurge) &&
       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();
       mUsedCommandListsSincePurge = 0;
+      mTransformedGlyphsSinceLastPurge = 0;
       mDC->EndDraw();
       mDC->BeginDraw();
     }
     else {
       mDC->Flush();
     }
   }
 
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -284,16 +284,17 @@ private:
   RefPtr<SourceSurfaceD2D1> mSnapshot;
   std::shared_ptr<Mutex> mSnapshotLock;
   // 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 mUsedCommandListsSincePurge;
+  uint32_t mTransformedGlyphsSinceLastPurge;
   // When a BlendEffect has been drawn to a command list, and that command list is
   // subsequently used -again- as an input to a blend effect for a command list,
   // this causes an infinite recursion inside D2D as it tries to resolve the bounds.
   // If we resolve the current command list before this happens
   // we can avoid the subsequent hang. (See bug 1293586)
   uint32_t mComplexBlendsWithListInList;
 
   static StaticRefPtr<ID2D1Factory1> mFactory;