diff --git a/gfx/2d/DrawTargetD2D.cpp b/gfx/2d/DrawTargetD2D.cpp index 5f3818eefed0..f88782aa7af3 100644 --- a/gfx/2d/DrawTargetD2D.cpp +++ b/gfx/2d/DrawTargetD2D.cpp @@ -152,7 +152,8 @@ private: }; DrawTargetD2D::DrawTargetD2D() - : mClipsArePushed(false) + : mCurrentCachedLayer(0) + , mClipsArePushed(false) , mPrivateData(NULL) { } @@ -183,6 +184,13 @@ DrawTargetD2D::~DrawTargetD2D() // mSnapshot will be cleared now. } + for (int i = 0; i < kLayerCacheSize; i++) { + if (mCachedLayers[i]) { + mCachedLayers[i] = NULL; + mVRAMUsageDT -= GetByteSize(); + } + } + // Targets depending on us can break that dependency, since we're obviously not going to // be modified in the future. for (TargetSet::iterator iter = mDependentTargets.begin(); @@ -914,7 +922,9 @@ DrawTargetD2D::Mask(const Pattern &aSource, RefPtr maskBrush = CreateBrushForPattern(aMask, 1.0f); RefPtr layer; - rt->CreateLayer(byRef(layer)); + + layer = GetCachedLayer(); + rt->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), NULL, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, D2D1::IdentityMatrix(), @@ -926,7 +936,7 @@ DrawTargetD2D::Mask(const Pattern &aSource, mat.Invert(); rt->FillRectangle(D2DRect(mat.TransformBounds(rect)), brush); - rt->PopLayer(); + PopCachedLayer(rt); FinalizeRTForOperation(aOptions.mCompositionOp, aSource, Rect(0, 0, (Float)mSize.width, (Float)mSize.height)); } @@ -948,12 +958,10 @@ DrawTargetD2D::PushClip(const Path *aPath) clip.mTransform = D2DMatrix(mTransform); clip.mPath = pathD2D; - RefPtr layer; pathD2D->mGeometry->GetBounds(clip.mTransform, &clip.mBounds); + + clip.mLayer = GetCachedLayer(); - mRT->CreateLayer( byRef(layer)); - - clip.mLayer = layer; mPushedClips.push_back(clip); // The transform of clips is relative to the world matrix, since we use the total @@ -971,7 +979,7 @@ DrawTargetD2D::PushClip(const Path *aPath) mRT->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), pathD2D->mGeometry, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, clip.mTransform, 1.0f, NULL, - options), layer); + options), clip.mLayer); } } @@ -1015,7 +1023,7 @@ DrawTargetD2D::PopClip() mCurrentClippedGeometry = NULL; if (mClipsArePushed) { if (mPushedClips.back().mLayer) { - mRT->PopLayer(); + PopCachedLayer(mRT); } else { mRT->PopAxisAlignedClip(); } @@ -1286,6 +1294,32 @@ DrawTargetD2D::GetByteSize() const return mSize.width * mSize.height * BytesPerPixel(mFormat); } +TemporaryRef +DrawTargetD2D::GetCachedLayer() +{ + RefPtr layer; + + if (mCurrentCachedLayer < 5) { + if (!mCachedLayers[mCurrentCachedLayer]) { + mRT->CreateLayer(byRef(mCachedLayers[mCurrentCachedLayer])); + mVRAMUsageDT += GetByteSize(); + } + layer = mCachedLayers[mCurrentCachedLayer]; + } else { + mRT->CreateLayer(byRef(layer)); + } + + mCurrentCachedLayer++; + return layer; +} + +void +DrawTargetD2D::PopCachedLayer(ID2D1RenderTarget *aRT) +{ + aRT->PopLayer(); + mCurrentCachedLayer--; +} + bool DrawTargetD2D::InitD2DRenderTarget() { diff --git a/gfx/2d/DrawTargetD2D.h b/gfx/2d/DrawTargetD2D.h index 46bf20f59fd1..65793316f375 100644 --- a/gfx/2d/DrawTargetD2D.h +++ b/gfx/2d/DrawTargetD2D.h @@ -30,6 +30,8 @@ class SourceSurfaceD2D; class GradientStopsD2D; class ScaledFontDWrite; +const int32_t kLayerCacheSize = 5; + struct PrivateD3D10DataD2D { RefPtr mEffect; @@ -122,6 +124,8 @@ public: bool Init(ID3D10Texture2D *aTexture, SurfaceFormat aFormat); bool InitD3D10Data(); uint32_t GetByteSize() const; + TemporaryRef GetCachedLayer(); + void PopCachedLayer(ID2D1RenderTarget *aRT); static ID2D1Factory *factory(); static TemporaryRef CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions); @@ -219,6 +223,13 @@ private: RefPtr mPath; }; std::vector mPushedClips; + + // We cache ID2D1Layer objects as it causes D2D to keep around textures that + // serve as the temporary surfaces for these operations. As texture creation + // is quite expensive this considerably improved performance. + // Careful here, RAII will not ensure destruction of the RefPtrs. + RefPtr mCachedLayers[kLayerCacheSize]; + uint32_t mCurrentCachedLayer; // The latest snapshot of this surface. This needs to be told when this // target is modified. We keep it alive as a cache.