From c03ed5beaf75dc10316acda74ee1036002ff9247 Mon Sep 17 00:00:00 2001 From: Andrew Osmond Date: Wed, 8 Feb 2017 15:48:59 -0500 Subject: [PATCH] Bug 1339202 - Decode images to shared surfaces for WebRender. r=tnikkel --- image/Decoder.cpp | 3 +- image/Decoder.h | 5 +++ image/DecoderFactory.cpp | 1 + image/RasterImage.cpp | 9 +++- image/decoders/nsICODecoder.cpp | 7 ++++ image/imgFrame.cpp | 74 ++++++++++++++++++++++++++++++--- image/imgFrame.h | 9 +++- 7 files changed, 98 insertions(+), 10 deletions(-) diff --git a/image/Decoder.cpp b/image/Decoder.cpp index da24766c069f..edfe3dfaf3a9 100644 --- a/image/Decoder.cpp +++ b/image/Decoder.cpp @@ -64,6 +64,7 @@ Decoder::Decoder(RasterImage* aImage) , mDecodeDone(false) , mError(false) , mShouldReportError(false) + , mFinalizeFrames(true) { } Decoder::~Decoder() @@ -455,7 +456,7 @@ Decoder::PostFrameStop(Opacity aFrameOpacity mFinishedNewFrame = true; mCurrentFrame->Finish(aFrameOpacity, aDisposalMethod, aTimeout, - aBlendMethod, aBlendRect); + aBlendMethod, aBlendRect, mFinalizeFrames); mProgress |= FLAG_FRAME_COMPLETE; diff --git a/image/Decoder.h b/image/Decoder.h index 6217a69ff47a..a5ab9c3fe4e7 100644 --- a/image/Decoder.h +++ b/image/Decoder.h @@ -262,6 +262,10 @@ public: bool HasError() const { return mError; } bool ShouldReportError() const { return mShouldReportError; } + // Finalize frames + void SetFinalizeFrames(bool aFinalize) { mFinalizeFrames = aFinalize; } + bool GetFinalizeFrames() const { return mFinalizeFrames; } + /// Did we finish decoding enough that calling Decode() again would be useless? bool GetDecodeDone() const { @@ -546,6 +550,7 @@ private: bool mDecodeDone : 1; bool mError : 1; bool mShouldReportError : 1; + bool mFinalizeFrames : 1; }; } // namespace image diff --git a/image/DecoderFactory.cpp b/image/DecoderFactory.cpp index f8a4f600825d..2b5dd5f28f85 100644 --- a/image/DecoderFactory.cpp +++ b/image/DecoderFactory.cpp @@ -266,6 +266,7 @@ DecoderFactory::CreateDecoderForICOResource(DecoderType aType, decoder->SetOutputSize(aICODecoder->OutputSize()); decoder->SetDecoderFlags(aICODecoder->GetDecoderFlags()); decoder->SetSurfaceFlags(aICODecoder->GetSurfaceFlags()); + decoder->SetFinalizeFrames(false); if (NS_FAILED(decoder->Init())) { return nullptr; diff --git a/image/RasterImage.cpp b/image/RasterImage.cpp index 03d2b581e980..dd957eeaafbc 100644 --- a/image/RasterImage.cpp +++ b/image/RasterImage.cpp @@ -479,7 +479,7 @@ NS_IMETHODIMP_(already_AddRefed) RasterImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags) { - return GetFrameInternal(mSize, aWhichFrame, aFlags).second().forget(); + return GetFrameAtSize(mSize, aWhichFrame, aFlags); } NS_IMETHODIMP_(already_AddRefed) @@ -487,7 +487,12 @@ RasterImage::GetFrameAtSize(const IntSize& aSize, uint32_t aWhichFrame, uint32_t aFlags) { - return GetFrameInternal(aSize, aWhichFrame, aFlags).second().forget(); + RefPtr surf = + GetFrameInternal(aSize, aWhichFrame, aFlags).second().forget(); + // If we are here, it suggests the image is embedded in a canvas or some + // other path besides layers, and we won't need the file handle. + MarkSurfaceShared(surf); + return surf.forget(); } Pair> diff --git a/image/decoders/nsICODecoder.cpp b/image/decoders/nsICODecoder.cpp index f56f800a0f95..85b3c139b0d7 100644 --- a/image/decoders/nsICODecoder.cpp +++ b/image/decoders/nsICODecoder.cpp @@ -593,6 +593,13 @@ nsICODecoder::FinishResource() return Transition::TerminateFailure(); } + // Finalize the frame which we deferred to ensure we could modify the final + // result (e.g. to apply the BMP mask). + MOZ_ASSERT(!mContainedDecoder->GetFinalizeFrames()); + if (mCurrentFrame) { + mCurrentFrame->FinalizeSurface(); + } + return Transition::TerminateSuccess(); } diff --git a/image/imgFrame.cpp b/image/imgFrame.cpp index 335869d5c050..1fb15cc35028 100644 --- a/image/imgFrame.cpp +++ b/image/imgFrame.cpp @@ -12,7 +12,6 @@ #include "gfx2DGlue.h" #include "gfxPlatform.h" -#include "gfxPrefs.h" #include "gfxUtils.h" #include "gfxAlphaRecovery.h" @@ -20,6 +19,8 @@ #include "MainThreadUtils.h" #include "mozilla/CheckedInt.h" #include "mozilla/gfx/Tools.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/layers/SourceSurfaceSharedData.h" #include "mozilla/layers/SourceSurfaceVolatileData.h" #include "mozilla/Likely.h" #include "mozilla/MemoryReporting.h" @@ -51,6 +52,12 @@ CreateLockedSurface(DataSourceSurface *aSurface, const IntSize& size, SurfaceFormat format) { + // Shared memory is never released until the surface itself is released + if (aSurface->GetType() == SurfaceType::DATA_SHARED) { + RefPtr surf(aSurface); + return surf.forget(); + } + DataSourceSurface::ScopedMap* smap = new DataSourceSurface::ScopedMap(aSurface, DataSourceSurface::READ_WRITE); if (smap->IsMapped()) { @@ -77,11 +84,17 @@ AllocateBufferForImage(const IntSize& size, bool aIsAnimated = false) { int32_t stride = VolatileSurfaceStride(size, format); - RefPtr newSurf = new SourceSurfaceVolatileData(); - if (newSurf->Init(size, stride, format)) { - return newSurf.forget(); + if (!aIsAnimated && gfxVars::UseWebRender()) { + RefPtr newSurf = new SourceSurfaceSharedData(); + if (newSurf->Init(size, stride, format)) { + return newSurf.forget(); + } + } else { + RefPtr newSurf= new SourceSurfaceVolatileData(); + if (newSurf->Init(size, stride, format)) { + return newSurf.forget(); + } } - return nullptr; } @@ -108,6 +121,19 @@ ClearSurface(DataSourceSurface* aSurface, const IntSize& aSize, SurfaceFormat aF return true; } +void +MarkSurfaceShared(SourceSurface* aSurface) +{ + // Depending on what requested the image decoding, the buffer may or may not + // end up being shared with another process (e.g. put in a painted layer, + // used inside a canvas). If not shared, we should ensure are not keeping the + // handle only because we have yet to share it. + if (aSurface && aSurface->GetType() == SurfaceType::DATA_SHARED) { + auto sharedSurface = static_cast(aSurface); + sharedSurface->FinishedSharing(); + } +} + // Returns true if an image of aWidth x aHeight is allowed and legal. static bool AllowedImageSize(int32_t aWidth, int32_t aHeight) @@ -359,6 +385,8 @@ imgFrame::InitWithDrawable(gfxDrawable* aDrawable, // We used an offscreen surface, which is an "optimized" surface from // imgFrame's perspective. mOptSurface = target->Snapshot(); + } else { + FinalizeSurface(); } // If we reach this point, we should regard ourselves as complete. @@ -551,6 +579,10 @@ bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion, imageRect.Size(), region, surfaceResult.mFormat, aSamplingFilter, aImageFlags, aOpacity); } + + // Image got put into a painted layer, it will not be shared with another + // process. + MarkSurfaceShared(surf); return true; } @@ -581,7 +613,8 @@ imgFrame::Finish(Opacity aFrameOpacity /* = Opacity::SOME_TRANSPARENCY */, FrameTimeout aTimeout /* = FrameTimeout::FromRawMilliseconds(0) */, BlendMethod aBlendMethod /* = BlendMethod::OVER */, - const Maybe& aBlendRect /* = Nothing() */) + const Maybe& aBlendRect /* = Nothing() */, + bool aFinalize /* = true */) { MonitorAutoLock lock(mMonitor); MOZ_ASSERT(mLockCount > 0, "Image data should be locked"); @@ -591,6 +624,11 @@ imgFrame::Finish(Opacity aFrameOpacity /* = Opacity::SOME_TRANSPARENCY */, mBlendMethod = aBlendMethod; mBlendRect = aBlendRect; ImageUpdatedInternal(GetRect()); + + if (aFinalize) { + FinalizeSurfaceInternal(); + } + mFinished = true; // The image is now complete, wake up anyone who's waiting. @@ -633,6 +671,9 @@ imgFrame::GetImageDataInternal(uint8_t** aData, uint32_t* aLength) const MOZ_ASSERT(mLockCount > 0, "Image data should be locked"); if (mLockedSurface) { + // TODO: This is okay for now because we only realloc shared surfaces on + // the main thread after decoding has finished, but if animations want to + // read frame data off the main thread, we will need to reconsider this. *aData = mLockedSurface->GetData(); MOZ_ASSERT(*aData, "mLockedSurface is non-null, but GetData is null in GetImageData"); @@ -753,6 +794,27 @@ imgFrame::SetOptimizable() mOptimizable = true; } +void +imgFrame::FinalizeSurface() +{ + MonitorAutoLock lock(mMonitor); + FinalizeSurfaceInternal(); +} + +void +imgFrame::FinalizeSurfaceInternal() +{ + mMonitor.AssertCurrentThreadOwns(); + + // Not all images will have mRawSurface to finalize (i.e. paletted images). + if (!mRawSurface || mRawSurface->GetType() != SurfaceType::DATA_SHARED) { + return; + } + + auto sharedSurf = static_cast(mRawSurface.get()); + sharedSurf->Finalize(); +} + already_AddRefed imgFrame::GetSourceSurface() { diff --git a/image/imgFrame.h b/image/imgFrame.h index a19ff146fa51..b940777fa81e 100644 --- a/image/imgFrame.h +++ b/image/imgFrame.h @@ -279,12 +279,15 @@ public: * @param aBlendRect For animation frames, if present, the subrect in * which @aBlendMethod applies. Outside of this * subrect, BlendMethod::OVER is always used. + * @param aFinalize Finalize the underlying surface (e.g. so that it + * may be marked as read only if possible). */ void Finish(Opacity aFrameOpacity = Opacity::SOME_TRANSPARENCY, DisposalMethod aDisposalMethod = DisposalMethod::KEEP, FrameTimeout aTimeout = FrameTimeout::FromRawMilliseconds(0), BlendMethod aBlendMethod = BlendMethod::OVER, - const Maybe& aBlendRect = Nothing()); + const Maybe& aBlendRect = Nothing(), + bool aFinalize = true); /** * Mark this imgFrame as aborted. This informs the imgFrame that if it isn't @@ -341,6 +344,7 @@ public: void SetOptimizable(); + void FinalizeSurface(); already_AddRefed GetSourceSurface(); void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, size_t& aHeapSizeOut, @@ -361,6 +365,7 @@ private: // methods void GetImageDataInternal(uint8_t** aData, uint32_t* length) const; uint32_t GetImageBytesPerRow() const; uint32_t GetImageDataLength() const; + void FinalizeSurfaceInternal(); already_AddRefed GetSourceSurfaceInternal(); uint32_t PaletteDataLength() const @@ -617,6 +622,8 @@ private: RefPtr mFrame; }; +void MarkSurfaceShared(gfx::SourceSurface* aSurface); + } // namespace image } // namespace mozilla