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
--- 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;