Bug 1874523. r=tnikkel
Differential Revision: https://phabricator.services.mozilla.com/D227304
This commit is contained in:
@@ -432,32 +432,38 @@ RawAccessFrameRef AnimationFrameRecyclingQueue::RecycleFrame(
|
||||
|
||||
RawAccessFrameRef recycledFrame;
|
||||
if (mRecycle.front().mFrame) {
|
||||
recycledFrame = mRecycle.front().mFrame->RawAccessRef();
|
||||
MOZ_ASSERT(recycledFrame);
|
||||
recycledFrame = mRecycle.front().mFrame->RawAccessRef(
|
||||
gfx::DataSourceSurface::READ_WRITE);
|
||||
mRecycle.pop_front();
|
||||
|
||||
if (mForceUseFirstFrameRefreshArea) {
|
||||
// We are still crossing the loop boundary and cannot rely upon the dirty
|
||||
// rects of entries in mDisplay to be representative. E.g. The first frame
|
||||
// is probably has a full frame dirty rect.
|
||||
aRecycleRect = mFirstFrameRefreshArea;
|
||||
} else {
|
||||
// Calculate the recycle rect for the recycled frame. This is the
|
||||
// cumulative dirty rect of all of the frames ahead of us to be displayed,
|
||||
// and to be used for recycling. Or in other words, the dirty rect between
|
||||
// the recycled frame and the decoded frame which reuses the buffer.
|
||||
//
|
||||
// We know at this point that mRecycle contains either frames from the end
|
||||
// of the animation with the first frame refresh area as the dirty rect
|
||||
// (plus the first frame likewise) and frames with their actual dirty rect
|
||||
// from the start. mDisplay should also only contain frames from the start
|
||||
// of the animation onwards.
|
||||
aRecycleRect.SetRect(0, 0, 0, 0);
|
||||
for (const RefPtr<imgFrame>& frame : mDisplay) {
|
||||
aRecycleRect = aRecycleRect.Union(frame->GetDirtyRect());
|
||||
}
|
||||
for (const RecycleEntry& entry : mRecycle) {
|
||||
aRecycleRect = aRecycleRect.Union(entry.mDirtyRect);
|
||||
// If we couldn't map in the surface, it is probably because the frame was
|
||||
// finalized and we did not expect to need to write into it again. This
|
||||
// happens for the first frames produced during an animation.
|
||||
if (recycledFrame) {
|
||||
if (mForceUseFirstFrameRefreshArea) {
|
||||
// We are still crossing the loop boundary and cannot rely upon the
|
||||
// dirty rects of entries in mDisplay to be representative. E.g. The
|
||||
// first frame is probably has a full frame dirty rect.
|
||||
aRecycleRect = mFirstFrameRefreshArea;
|
||||
} else {
|
||||
// Calculate the recycle rect for the recycled frame. This is the
|
||||
// cumulative dirty rect of all of the frames ahead of us to be
|
||||
// displayed, and to be used for recycling. Or in other words, the dirty
|
||||
// rect between the recycled frame and the decoded frame which reuses
|
||||
// the buffer.
|
||||
//
|
||||
// We know at this point that mRecycle contains either frames from the
|
||||
// end of the animation with the first frame refresh area as the dirty
|
||||
// rect (plus the first frame likewise) and frames with their actual
|
||||
// dirty rect from the start. mDisplay should also only contain frames
|
||||
// from the start of the animation onwards.
|
||||
aRecycleRect.SetRect(0, 0, 0, 0);
|
||||
for (const RefPtr<imgFrame>& frame : mDisplay) {
|
||||
aRecycleRect = aRecycleRect.Union(frame->GetDirtyRect());
|
||||
}
|
||||
for (const RecycleEntry& entry : mRecycle) {
|
||||
aRecycleRect = aRecycleRect.Union(entry.mDirtyRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -316,8 +316,7 @@ nsresult Decoder::AllocateFrame(const gfx::IntSize& aOutputSize,
|
||||
if (mCurrentFrame) {
|
||||
mHasFrameToTake = true;
|
||||
|
||||
// Gather the raw pointers the decoders will use.
|
||||
mCurrentFrame->GetImageData(&mImageData, &mImageDataLength);
|
||||
mImageData = mCurrentFrame.Data();
|
||||
|
||||
// We should now be on |aFrameNum|. (Note that we're comparing the frame
|
||||
// number, which is zero-based, with the frame count, which is one-based.)
|
||||
@@ -329,6 +328,9 @@ nsresult Decoder::AllocateFrame(const gfx::IntSize& aOutputSize,
|
||||
// Update our state to reflect the new frame.
|
||||
MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
|
||||
mInFrame = true;
|
||||
} else {
|
||||
mImageData = nullptr;
|
||||
mImageDataLength = 0;
|
||||
}
|
||||
|
||||
return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE;
|
||||
@@ -389,7 +391,8 @@ RawAccessFrameRef Decoder::AllocateFrameInternal(
|
||||
// animation parameters elsewhere. For now we just drop it.
|
||||
bool blocked = ref.get() == mRestoreFrame.get();
|
||||
if (!blocked) {
|
||||
blocked = NS_FAILED(ref->InitForDecoderRecycle(aAnimParams.ref()));
|
||||
blocked = NS_FAILED(
|
||||
ref->InitForDecoderRecycle(aAnimParams.ref(), &mImageDataLength));
|
||||
}
|
||||
|
||||
if (blocked) {
|
||||
@@ -408,12 +411,13 @@ RawAccessFrameRef Decoder::AllocateFrameInternal(
|
||||
bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
|
||||
auto frame = MakeNotNull<RefPtr<imgFrame>>();
|
||||
if (NS_FAILED(frame->InitForDecoder(aOutputSize, aFormat, nonPremult,
|
||||
aAnimParams, bool(mFrameRecycler)))) {
|
||||
aAnimParams, bool(mFrameRecycler),
|
||||
&mImageDataLength))) {
|
||||
NS_WARNING("imgFrame::Init should succeed");
|
||||
return RawAccessFrameRef();
|
||||
}
|
||||
|
||||
ref = frame->RawAccessRef();
|
||||
ref = frame->RawAccessRef(gfx::DataSourceSurface::READ_WRITE);
|
||||
if (!ref) {
|
||||
frame->Abort();
|
||||
return RawAccessFrameRef();
|
||||
|
||||
@@ -145,7 +145,8 @@ imgFrame::~imgFrame() {
|
||||
nsresult imgFrame::InitForDecoder(const nsIntSize& aImageSize,
|
||||
SurfaceFormat aFormat, bool aNonPremult,
|
||||
const Maybe<AnimationParams>& aAnimParams,
|
||||
bool aShouldRecycle) {
|
||||
bool aShouldRecycle,
|
||||
uint32_t* aImageDataLength) {
|
||||
// Assert for properties that should be verified by decoders,
|
||||
// warn for properties related to bad content.
|
||||
if (!SurfaceCache::IsLegalSize(aImageSize)) {
|
||||
@@ -217,10 +218,15 @@ nsresult imgFrame::InitForDecoder(const nsIntSize& aImageSize,
|
||||
}
|
||||
}
|
||||
|
||||
if (aImageDataLength) {
|
||||
*aImageDataLength = GetImageDataLength();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult imgFrame::InitForDecoderRecycle(const AnimationParams& aAnimParams) {
|
||||
nsresult imgFrame::InitForDecoderRecycle(const AnimationParams& aAnimParams,
|
||||
uint32_t* aImageDataLength) {
|
||||
// We want to recycle this frame, but there is no guarantee that consumers are
|
||||
// done with it in a timely manner. Let's ensure they are done with it first.
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
@@ -287,6 +293,10 @@ nsresult imgFrame::InitForDecoderRecycle(const AnimationParams& aAnimParams) {
|
||||
mDisposalMethod = aAnimParams.mDisposalMethod;
|
||||
mDirtyRect = GetRect();
|
||||
|
||||
if (aImageDataLength) {
|
||||
*aImageDataLength = GetImageDataLength();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -391,7 +401,10 @@ nsresult imgFrame::InitWithDrawable(gfxDrawable* aDrawable,
|
||||
|
||||
DrawableFrameRef imgFrame::DrawableRef() { return DrawableFrameRef(this); }
|
||||
|
||||
RawAccessFrameRef imgFrame::RawAccessRef() { return RawAccessFrameRef(this); }
|
||||
RawAccessFrameRef imgFrame::RawAccessRef(
|
||||
gfx::DataSourceSurface::MapType aMapType) {
|
||||
return RawAccessFrameRef(this, aMapType);
|
||||
}
|
||||
|
||||
imgFrame::SurfaceWithFormat imgFrame::SurfaceForDrawing(
|
||||
bool aDoPartialDecode, bool aDoTile, ImageRegion& aRegion,
|
||||
@@ -586,36 +599,6 @@ uint32_t imgFrame::GetImageDataLength() const {
|
||||
return GetImageBytesPerRow() * mImageSize.height;
|
||||
}
|
||||
|
||||
void imgFrame::GetImageData(uint8_t** aData, uint32_t* aLength) const {
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
GetImageDataInternal(aData, aLength);
|
||||
}
|
||||
|
||||
void imgFrame::GetImageDataInternal(uint8_t** aData, uint32_t* aLength) const {
|
||||
mMonitor.AssertCurrentThreadOwns();
|
||||
MOZ_ASSERT(mRawSurface);
|
||||
|
||||
if (mRawSurface) {
|
||||
// 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 = mRawSurface->GetData();
|
||||
MOZ_ASSERT(*aData,
|
||||
"mRawSurface is non-null, but GetData is null in GetImageData");
|
||||
} else {
|
||||
*aData = nullptr;
|
||||
}
|
||||
|
||||
*aLength = GetImageDataLength();
|
||||
}
|
||||
|
||||
uint8_t* imgFrame::GetImageData() const {
|
||||
uint8_t* data;
|
||||
uint32_t length;
|
||||
GetImageData(&data, &length);
|
||||
return data;
|
||||
}
|
||||
|
||||
void imgFrame::FinalizeSurface() {
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
FinalizeSurfaceInternal();
|
||||
|
||||
@@ -55,7 +55,8 @@ class imgFrame {
|
||||
nsresult InitForDecoder(const nsIntSize& aImageSize, SurfaceFormat aFormat,
|
||||
bool aNonPremult,
|
||||
const Maybe<AnimationParams>& aAnimParams,
|
||||
bool aShouldRecycle);
|
||||
bool aShouldRecycle,
|
||||
uint32_t* aImageDataLength = nullptr);
|
||||
|
||||
/**
|
||||
* Reinitialize this imgFrame with the new parameters, but otherwise retain
|
||||
@@ -65,7 +66,8 @@ class imgFrame {
|
||||
* given an IDecoderFrameRecycler object which may yield a recycled imgFrame
|
||||
* that was discarded to save memory.
|
||||
*/
|
||||
nsresult InitForDecoderRecycle(const AnimationParams& aAnimParams);
|
||||
nsresult InitForDecoderRecycle(const AnimationParams& aAnimParams,
|
||||
uint32_t* aImageDataLength = nullptr);
|
||||
|
||||
/**
|
||||
* Initialize this imgFrame with a new surface and draw the provided
|
||||
@@ -90,7 +92,8 @@ class imgFrame {
|
||||
/**
|
||||
* Create a RawAccessFrameRef for the frame.
|
||||
*/
|
||||
RawAccessFrameRef RawAccessRef();
|
||||
RawAccessFrameRef RawAccessRef(
|
||||
gfx::DataSourceSurface::MapType aMapType = gfx::DataSourceSurface::READ);
|
||||
|
||||
bool Draw(gfxContext* aContext, const ImageRegion& aRegion,
|
||||
SamplingFilter aSamplingFilter, uint32_t aImageFlags,
|
||||
@@ -160,8 +163,6 @@ class imgFrame {
|
||||
BlendMethod GetBlendMethod() const { return mBlendMethod; }
|
||||
DisposalMethod GetDisposalMethod() const { return mDisposalMethod; }
|
||||
bool FormatHasAlpha() const { return mFormat == SurfaceFormat::OS_RGBA; }
|
||||
void GetImageData(uint8_t** aData, uint32_t* length) const;
|
||||
uint8_t* GetImageData() const;
|
||||
|
||||
const IntRect& GetDirtyRect() const { return mDirtyRect; }
|
||||
void SetDirtyRect(const IntRect& aDirtyRect) { mDirtyRect = aDirtyRect; }
|
||||
@@ -186,7 +187,6 @@ class imgFrame {
|
||||
|
||||
bool AreAllPixelsWritten() const MOZ_REQUIRES(mMonitor);
|
||||
nsresult ImageUpdatedInternal(const nsIntRect& aUpdateRect);
|
||||
void GetImageDataInternal(uint8_t** aData, uint32_t* length) const;
|
||||
uint32_t GetImageBytesPerRow() const;
|
||||
uint32_t GetImageDataLength() const;
|
||||
void FinalizeSurfaceInternal();
|
||||
@@ -356,13 +356,25 @@ class DrawableFrameRef final {
|
||||
*/
|
||||
class RawAccessFrameRef final {
|
||||
public:
|
||||
RawAccessFrameRef() : mData(nullptr) {}
|
||||
RawAccessFrameRef() = default;
|
||||
|
||||
explicit RawAccessFrameRef(imgFrame* aFrame)
|
||||
: mFrame(aFrame), mData(nullptr) {
|
||||
explicit RawAccessFrameRef(imgFrame* aFrame,
|
||||
gfx::DataSourceSurface::MapType aMapType)
|
||||
: mFrame(aFrame) {
|
||||
MOZ_ASSERT(mFrame, "Need a frame");
|
||||
|
||||
mData = mFrame->GetImageData();
|
||||
// Note that we do not use ScopedMap here because it holds a strong
|
||||
// reference to the underlying surface. This affects the reuse logic for
|
||||
// recycling in imgFrame::InitForDecoderRecycle.
|
||||
{
|
||||
MonitorAutoLock lock(mFrame->mMonitor);
|
||||
gfx::DataSourceSurface::MappedSurface map;
|
||||
if (mFrame->mRawSurface && mFrame->mRawSurface->Map(aMapType, &map)) {
|
||||
MOZ_ASSERT(map.mData);
|
||||
mData = map.mData;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mData) {
|
||||
mFrame = nullptr;
|
||||
}
|
||||
@@ -373,11 +385,15 @@ class RawAccessFrameRef final {
|
||||
aOther.mData = nullptr;
|
||||
}
|
||||
|
||||
~RawAccessFrameRef() = default;
|
||||
~RawAccessFrameRef() { reset(); }
|
||||
|
||||
RawAccessFrameRef& operator=(RawAccessFrameRef&& aOther) {
|
||||
MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
|
||||
|
||||
if (mFrame) {
|
||||
MonitorAutoLock lock(mFrame->mMonitor);
|
||||
mFrame->mRawSurface->Unmap();
|
||||
}
|
||||
mFrame = std::move(aOther.mFrame);
|
||||
mData = aOther.mData;
|
||||
aOther.mData = nullptr;
|
||||
@@ -401,6 +417,10 @@ class RawAccessFrameRef final {
|
||||
const imgFrame* get() const { return mFrame; }
|
||||
|
||||
void reset() {
|
||||
if (mFrame) {
|
||||
MonitorAutoLock lock(mFrame->mMonitor);
|
||||
mFrame->mRawSurface->Unmap();
|
||||
}
|
||||
mFrame = nullptr;
|
||||
mData = nullptr;
|
||||
}
|
||||
@@ -412,7 +432,7 @@ class RawAccessFrameRef final {
|
||||
RawAccessFrameRef& operator=(const RawAccessFrameRef& aOther) = delete;
|
||||
|
||||
RefPtr<imgFrame> mFrame;
|
||||
uint8_t* mData;
|
||||
uint8_t* mData = nullptr;
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
|
||||
@@ -759,8 +759,7 @@ TEST_F(ImageAnimationFrameBuffer, RecyclingResetBeforeComplete) {
|
||||
while (!buffer.Recycle().empty()) {
|
||||
gfx::IntRect recycleRect;
|
||||
RawAccessFrameRef frameRef = buffer.RecycleFrame(recycleRect);
|
||||
EXPECT_TRUE(frameRef);
|
||||
EXPECT_FALSE(ReinitForRecycle(frameRef));
|
||||
EXPECT_FALSE(frameRef);
|
||||
}
|
||||
|
||||
// Reinsert the first two frames as recyclable and reset again.
|
||||
@@ -829,8 +828,7 @@ TEST_F(ImageAnimationFrameBuffer, RecyclingRect) {
|
||||
gfx::IntRect recycleRect;
|
||||
EXPECT_FALSE(buffer.Recycle().empty());
|
||||
RawAccessFrameRef frameRef = buffer.RecycleFrame(recycleRect);
|
||||
EXPECT_TRUE(frameRef);
|
||||
EXPECT_FALSE(ReinitForRecycle(frameRef));
|
||||
EXPECT_FALSE(frameRef);
|
||||
EXPECT_TRUE(buffer.Recycle().empty());
|
||||
|
||||
// Insert a recyclable partial frame. Its dirty rect shouldn't matter since
|
||||
@@ -842,8 +840,7 @@ TEST_F(ImageAnimationFrameBuffer, RecyclingRect) {
|
||||
VerifyAdvance(buffer, 5, true);
|
||||
EXPECT_FALSE(buffer.Recycle().empty());
|
||||
frameRef = buffer.RecycleFrame(recycleRect);
|
||||
EXPECT_TRUE(frameRef);
|
||||
EXPECT_FALSE(ReinitForRecycle(frameRef));
|
||||
EXPECT_FALSE(frameRef);
|
||||
EXPECT_TRUE(buffer.Recycle().empty());
|
||||
|
||||
// Insert a recyclable partial frame. Its dirty rect should match the recycle
|
||||
|
||||
Reference in New Issue
Block a user