1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "ImageRegion.h"
9 #include "SurfaceCache.h"
13 #include "gfx2DGlue.h"
14 #include "gfxContext.h"
15 #include "gfxPlatform.h"
19 #include "MainThreadUtils.h"
20 #include "mozilla/CheckedInt.h"
21 #include "mozilla/gfx/Tools.h"
22 #include "mozilla/Likely.h"
23 #include "mozilla/MemoryReporting.h"
24 #include "mozilla/ProfilerLabels.h"
25 #include "mozilla/StaticPrefs_browser.h"
27 #include "nsRefreshDriver.h"
28 #include "nsThreadUtils.h"
30 #include <algorithm> // for min, max
39 * This class is identical to SourceSurfaceSharedData but returns a different
40 * type so that SharedSurfacesChild is aware imagelib wants to recycle this
41 * surface for future animation frames.
43 class RecyclingSourceSurfaceSharedData final
: public SourceSurfaceSharedData
{
45 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(RecyclingSourceSurfaceSharedData
,
48 SurfaceType
GetType() const override
{
49 return SurfaceType::DATA_RECYCLING_SHARED
;
53 static already_AddRefed
<SourceSurfaceSharedData
> AllocateBufferForImage(
54 const IntSize
& size
, SurfaceFormat format
, bool aShouldRecycle
= false) {
55 // Stride must be a multiple of four or cairo will complain.
56 int32_t stride
= (size
.width
* BytesPerPixel(format
) + 0x3) & ~0x3;
58 RefPtr
<SourceSurfaceSharedData
> newSurf
;
60 newSurf
= new RecyclingSourceSurfaceSharedData();
62 newSurf
= new SourceSurfaceSharedData();
64 if (!newSurf
->Init(size
, stride
, format
)) {
67 return newSurf
.forget();
70 static bool GreenSurface(SourceSurfaceSharedData
* aSurface
,
71 const IntSize
& aSize
, SurfaceFormat aFormat
) {
72 int32_t stride
= aSurface
->Stride();
73 uint32_t* surfaceData
= reinterpret_cast<uint32_t*>(aSurface
->GetData());
74 uint32_t surfaceDataLength
= (stride
* aSize
.height
) / sizeof(uint32_t);
76 // Start by assuming that GG is in the second byte and
77 // AA is in the final byte -- the most common case.
78 uint32_t color
= mozilla::NativeEndian::swapFromBigEndian(0x00FF00FF);
80 // We are only going to handle this type of test under
81 // certain circumstances.
82 MOZ_ASSERT(surfaceData
);
83 MOZ_ASSERT(aFormat
== SurfaceFormat::B8G8R8A8
||
84 aFormat
== SurfaceFormat::B8G8R8X8
||
85 aFormat
== SurfaceFormat::R8G8B8A8
||
86 aFormat
== SurfaceFormat::R8G8B8X8
||
87 aFormat
== SurfaceFormat::A8R8G8B8
||
88 aFormat
== SurfaceFormat::X8R8G8B8
);
89 MOZ_ASSERT((stride
* aSize
.height
) % sizeof(uint32_t));
91 if (aFormat
== SurfaceFormat::A8R8G8B8
||
92 aFormat
== SurfaceFormat::X8R8G8B8
) {
93 color
= mozilla::NativeEndian::swapFromBigEndian(0xFF00FF00);
96 for (uint32_t i
= 0; i
< surfaceDataLength
; i
++) {
97 surfaceData
[i
] = color
;
103 static bool ClearSurface(SourceSurfaceSharedData
* aSurface
,
104 const IntSize
& aSize
, SurfaceFormat aFormat
) {
105 int32_t stride
= aSurface
->Stride();
106 uint8_t* data
= aSurface
->GetData();
109 if (aFormat
== SurfaceFormat::OS_RGBX
) {
110 // Skia doesn't support RGBX surfaces, so ensure the alpha value is set
111 // to opaque white. While it would be nice to only do this for Skia,
112 // imgFrame can run off main thread and past shutdown where
113 // we might not have gfxPlatform, so just memset every time instead.
114 memset(data
, 0xFF, stride
* aSize
.height
);
115 } else if (aSurface
->OnHeap()) {
116 // We only need to memset it if the buffer was allocated on the heap.
117 // Otherwise, it's allocated via mmap and refers to a zeroed page and will
118 // be COW once it's written to.
119 memset(data
, 0, stride
* aSize
.height
);
126 : mMonitor("imgFrame"),
127 mDecoded(0, 0, 0, 0),
130 mShouldRecycle(false),
131 mTimeout(FrameTimeout::FromRawMilliseconds(100)),
132 mDisposalMethod(DisposalMethod::NOT_SPECIFIED
),
133 mBlendMethod(BlendMethod::OVER
),
134 mFormat(SurfaceFormat::UNKNOWN
),
135 mNonPremult(false) {}
137 imgFrame::~imgFrame() {
139 MonitorAutoLock
lock(mMonitor
);
140 MOZ_ASSERT(mAborted
|| AreAllPixelsWritten());
141 MOZ_ASSERT(mAborted
|| mFinished
);
145 nsresult
imgFrame::InitForDecoder(const nsIntSize
& aImageSize
,
146 SurfaceFormat aFormat
, bool aNonPremult
,
147 const Maybe
<AnimationParams
>& aAnimParams
,
149 uint32_t* aImageDataLength
) {
150 // Assert for properties that should be verified by decoders,
151 // warn for properties related to bad content.
152 if (!SurfaceCache::IsLegalSize(aImageSize
)) {
153 NS_WARNING("Should have legal image size");
154 MonitorAutoLock
lock(mMonitor
);
156 return NS_ERROR_FAILURE
;
159 mImageSize
= aImageSize
;
161 // May be updated shortly after InitForDecoder by BlendAnimationFilter
162 // because it needs to take into consideration the previous frames to
163 // properly calculate. We start with the whole frame as dirty.
164 mDirtyRect
= GetRect();
167 mBlendRect
= aAnimParams
->mBlendRect
;
168 mTimeout
= aAnimParams
->mTimeout
;
169 mBlendMethod
= aAnimParams
->mBlendMethod
;
170 mDisposalMethod
= aAnimParams
->mDisposalMethod
;
172 mBlendRect
= GetRect();
175 if (aShouldRecycle
) {
176 // If we are recycling then we should always use BGRA for the underlying
177 // surface because if we use BGRX, the next frame composited into the
178 // surface could be BGRA and cause rendering problems.
179 MOZ_ASSERT(aAnimParams
);
180 mFormat
= SurfaceFormat::OS_RGBA
;
185 mNonPremult
= aNonPremult
;
187 MonitorAutoLock
lock(mMonitor
);
188 mShouldRecycle
= aShouldRecycle
;
190 MOZ_ASSERT(!mRawSurface
, "Called imgFrame::InitForDecoder() twice?");
192 mRawSurface
= AllocateBufferForImage(mImageSize
, mFormat
, mShouldRecycle
);
195 return NS_ERROR_OUT_OF_MEMORY
;
198 if (StaticPrefs::browser_measurement_render_anims_and_video_solid() &&
200 mBlankRawSurface
= AllocateBufferForImage(mImageSize
, mFormat
);
201 if (!mBlankRawSurface
) {
203 return NS_ERROR_OUT_OF_MEMORY
;
207 if (!ClearSurface(mRawSurface
, mImageSize
, mFormat
)) {
208 NS_WARNING("Could not clear allocated buffer");
210 return NS_ERROR_OUT_OF_MEMORY
;
213 if (mBlankRawSurface
) {
214 if (!GreenSurface(mBlankRawSurface
, mImageSize
, mFormat
)) {
215 NS_WARNING("Could not clear allocated blank buffer");
217 return NS_ERROR_OUT_OF_MEMORY
;
221 if (aImageDataLength
) {
222 *aImageDataLength
= GetImageDataLength();
228 nsresult
imgFrame::InitForDecoderRecycle(const AnimationParams
& aAnimParams
,
229 uint32_t* aImageDataLength
) {
230 // We want to recycle this frame, but there is no guarantee that consumers are
231 // done with it in a timely manner. Let's ensure they are done with it first.
232 MonitorAutoLock
lock(mMonitor
);
234 MOZ_ASSERT(mRawSurface
);
236 if (!mShouldRecycle
) {
237 // This frame either was never marked as recyclable, or the flag was cleared
238 // for a caller which does not support recycling.
239 return NS_ERROR_NOT_AVAILABLE
;
242 // Ensure we account for all internal references to the surface.
243 MozRefCountType internalRefs
= 1;
244 if (mOptSurface
== mRawSurface
) {
248 if (mRawSurface
->refCount() > internalRefs
) {
249 if (NS_IsMainThread()) {
250 // We should never be both decoding and recycling on the main thread. Sync
251 // decoding can only be used to produce the first set of frames. Those
252 // either never use recycling because advancing was blocked (main thread
253 // is busy) or we were auto-advancing (to seek to a frame) and the frames
254 // were never accessed (and thus cannot have recycle locks).
255 MOZ_ASSERT_UNREACHABLE("Recycling/decoding on the main thread?");
256 return NS_ERROR_NOT_AVAILABLE
;
259 // We don't want to wait forever to reclaim the frame because we have no
260 // idea why it is still held. It is possibly due to OMTP. Since we are off
261 // the main thread, and we generally have frames already buffered for the
262 // animation, we can afford to wait a short period of time to hopefully
263 // complete the transaction and reclaim the buffer.
265 // We choose to wait for, at most, the refresh driver interval, so that we
266 // won't skip more than one frame. If the frame is still in use due to
267 // outstanding transactions, we are already skipping frames. If the frame
268 // is still in use for some other purpose, it won't be returned to the pool
269 // and its owner can hold onto it forever without additional impact here.
270 int32_t refreshInterval
=
271 std::clamp(nsRefreshDriver::DefaultInterval(), 4, 20);
272 TimeDuration waitInterval
=
273 TimeDuration::FromMilliseconds(refreshInterval
>> 2);
275 TimeStamp::Now() + TimeDuration::FromMilliseconds(refreshInterval
);
277 mMonitor
.Wait(waitInterval
);
278 if (mRawSurface
->refCount() <= internalRefs
) {
282 if (timeout
<= TimeStamp::Now()) {
283 // We couldn't secure the frame for recycling. It will allocate a new
285 return NS_ERROR_NOT_AVAILABLE
;
290 mBlendRect
= aAnimParams
.mBlendRect
;
291 mTimeout
= aAnimParams
.mTimeout
;
292 mBlendMethod
= aAnimParams
.mBlendMethod
;
293 mDisposalMethod
= aAnimParams
.mDisposalMethod
;
294 mDirtyRect
= GetRect();
296 if (aImageDataLength
) {
297 *aImageDataLength
= GetImageDataLength();
303 nsresult
imgFrame::InitWithDrawable(gfxDrawable
* aDrawable
,
304 const nsIntSize
& aSize
,
305 const SurfaceFormat aFormat
,
306 SamplingFilter aSamplingFilter
,
307 uint32_t aImageFlags
,
308 gfx::BackendType aBackend
) {
309 // Assert for properties that should be verified by decoders,
310 // warn for properties related to bad content.
311 if (!SurfaceCache::IsLegalSize(aSize
)) {
312 NS_WARNING("Should have legal image size");
313 MonitorAutoLock
lock(mMonitor
);
315 return NS_ERROR_FAILURE
;
321 RefPtr
<DrawTarget
> target
;
323 bool canUseDataSurface
= Factory::DoesBackendSupportDataDrawtarget(aBackend
);
324 if (canUseDataSurface
) {
325 MonitorAutoLock
lock(mMonitor
);
326 // It's safe to use data surfaces for content on this platform, so we can
327 // get away with using volatile buffers.
328 MOZ_ASSERT(!mRawSurface
, "Called imgFrame::InitWithDrawable() twice?");
330 mRawSurface
= AllocateBufferForImage(mImageSize
, mFormat
);
333 return NS_ERROR_OUT_OF_MEMORY
;
336 if (!ClearSurface(mRawSurface
, mImageSize
, mFormat
)) {
337 NS_WARNING("Could not clear allocated buffer");
339 return NS_ERROR_OUT_OF_MEMORY
;
342 target
= gfxPlatform::CreateDrawTargetForData(
343 mRawSurface
->GetData(), mImageSize
, mRawSurface
->Stride(), mFormat
);
345 // We can't use data surfaces for content, so we'll create an offscreen
346 // surface instead. This means if someone later calls RawAccessRef(), we
347 // may have to do an expensive readback, but we warned callers about that in
348 // the documentation for this method.
351 MonitorAutoLock
lock(mMonitor
);
352 MOZ_ASSERT(!mOptSurface
, "Called imgFrame::InitWithDrawable() twice?");
356 if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(aBackend
)) {
357 target
= gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(
358 aBackend
, mImageSize
, mFormat
);
360 target
= gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
361 mImageSize
, mFormat
);
365 if (!target
|| !target
->IsValid()) {
366 MonitorAutoLock
lock(mMonitor
);
368 return NS_ERROR_OUT_OF_MEMORY
;
371 // Draw using the drawable the caller provided.
372 gfxContext
ctx(target
);
374 gfxUtils::DrawPixelSnapped(&ctx
, aDrawable
, SizeDouble(mImageSize
),
375 ImageRegion::Create(ThebesRect(GetRect())),
376 mFormat
, aSamplingFilter
, aImageFlags
);
378 MonitorAutoLock
lock(mMonitor
);
379 if (canUseDataSurface
&& !mRawSurface
) {
380 NS_WARNING("Failed to create SourceSurfaceSharedData");
382 return NS_ERROR_OUT_OF_MEMORY
;
385 if (!canUseDataSurface
) {
386 // We used an offscreen surface, which is an "optimized" surface from
387 // imgFrame's perspective.
388 mOptSurface
= target
->Snapshot();
390 FinalizeSurfaceInternal();
393 // If we reach this point, we should regard ourselves as complete.
394 mDecoded
= GetRect();
397 MOZ_ASSERT(AreAllPixelsWritten());
402 DrawableFrameRef
imgFrame::DrawableRef() { return DrawableFrameRef(this); }
404 RawAccessFrameRef
imgFrame::RawAccessRef(
405 gfx::DataSourceSurface::MapType aMapType
) {
406 return RawAccessFrameRef(this, aMapType
);
409 imgFrame::SurfaceWithFormat
imgFrame::SurfaceForDrawing(
410 bool aDoPartialDecode
, bool aDoTile
, ImageRegion
& aRegion
,
411 SourceSurface
* aSurface
) {
412 MOZ_ASSERT(NS_IsMainThread());
413 mMonitor
.AssertCurrentThreadOwns();
415 if (!aDoPartialDecode
) {
416 return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface
, mImageSize
),
421 gfxRect(mDecoded
.X(), mDecoded
.Y(), mDecoded
.Width(), mDecoded
.Height());
424 // Create a temporary surface.
425 // Give this surface an alpha channel because there are
426 // transparent pixels in the padding or undecoded area
427 RefPtr
<DrawTarget
> target
=
428 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
429 mImageSize
, SurfaceFormat::OS_RGBA
);
431 return SurfaceWithFormat();
434 SurfacePattern
pattern(aSurface
, aRegion
.GetExtendMode(),
435 Matrix::Translation(mDecoded
.X(), mDecoded
.Y()));
436 target
->FillRect(ToRect(aRegion
.Intersect(available
).Rect()), pattern
);
438 RefPtr
<SourceSurface
> newsurf
= target
->Snapshot();
439 return SurfaceWithFormat(new gfxSurfaceDrawable(newsurf
, mImageSize
),
440 target
->GetFormat());
443 // Not tiling, and we have a surface, so we can account for
444 // a partial decode just by twiddling parameters.
445 aRegion
= aRegion
.Intersect(available
);
446 IntSize
availableSize(mDecoded
.Width(), mDecoded
.Height());
448 return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface
, availableSize
),
452 bool imgFrame::Draw(gfxContext
* aContext
, const ImageRegion
& aRegion
,
453 SamplingFilter aSamplingFilter
, uint32_t aImageFlags
,
455 AUTO_PROFILER_LABEL("imgFrame::Draw", GRAPHICS
);
457 MOZ_ASSERT(NS_IsMainThread());
458 NS_ASSERTION(!aRegion
.Rect().IsEmpty(), "Drawing empty region!");
459 NS_ASSERTION(!aRegion
.IsRestricted() ||
460 !aRegion
.Rect().Intersect(aRegion
.Restriction()).IsEmpty(),
461 "We must be allowed to sample *some* source pixels!");
463 // Perform the draw and freeing of the surface outside the lock. We want to
464 // avoid contention with the decoder if we can. The surface may also attempt
465 // to relock the monitor if it is freed (e.g. RecyclingSourceSurface).
466 RefPtr
<SourceSurface
> surf
;
467 SurfaceWithFormat surfaceResult
;
468 ImageRegion
region(aRegion
);
469 gfxRect
imageRect(0, 0, mImageSize
.width
, mImageSize
.height
);
472 MonitorAutoLock
lock(mMonitor
);
474 bool doPartialDecode
= !AreAllPixelsWritten();
476 // Most draw targets will just use the surface only during DrawPixelSnapped
477 // but captures/recordings will retain a reference outside this stack
478 // context. While in theory a decoder thread could be trying to recycle this
479 // frame at this very moment, in practice the only way we can get here is if
480 // this frame is the current frame of the animation. Since we can only
481 // advance on the main thread, we know nothing else will try to use it.
482 DrawTarget
* drawTarget
= aContext
->GetDrawTarget();
483 bool recording
= drawTarget
->GetBackendType() == BackendType::RECORDING
;
484 RefPtr
<SourceSurface
> surf
= GetSourceSurfaceInternal();
489 bool doTile
= !imageRect
.Contains(aRegion
.Rect()) &&
490 !(aImageFlags
& imgIContainer::FLAG_CLAMP
);
492 surfaceResult
= SurfaceForDrawing(doPartialDecode
, doTile
, region
, surf
);
494 // If we are recording, then we cannot recycle the surface. The blob
495 // rasterizer is not properly synchronized for recycling in the compositor
496 // process. The easiest thing to do is just mark the frames it consumes as
498 if (recording
&& surfaceResult
.IsValid()) {
499 mShouldRecycle
= false;
503 if (surfaceResult
.IsValid()) {
504 gfxUtils::DrawPixelSnapped(aContext
, surfaceResult
.mDrawable
,
505 imageRect
.Size(), region
, surfaceResult
.mFormat
,
506 aSamplingFilter
, aImageFlags
, aOpacity
);
512 nsresult
imgFrame::ImageUpdated(const nsIntRect
& aUpdateRect
) {
513 MonitorAutoLock
lock(mMonitor
);
514 return ImageUpdatedInternal(aUpdateRect
);
517 nsresult
imgFrame::ImageUpdatedInternal(const nsIntRect
& aUpdateRect
) {
518 mMonitor
.AssertCurrentThreadOwns();
520 // Clamp to the frame rect to ensure that decoder bugs don't result in a
521 // decoded rect that extends outside the bounds of the frame rect.
522 IntRect updateRect
= aUpdateRect
.Intersect(GetRect());
523 if (updateRect
.IsEmpty()) {
527 mDecoded
.UnionRect(mDecoded
, updateRect
);
529 // Update our invalidation counters for any consumers watching for changes
532 mRawSurface
->Invalidate(updateRect
);
537 void imgFrame::Finish(Opacity aFrameOpacity
/* = Opacity::SOME_TRANSPARENCY */,
538 bool aFinalize
/* = true */,
539 bool aOrientationSwapsWidthAndHeight
/* = false */) {
540 MonitorAutoLock
lock(mMonitor
);
542 IntRect
frameRect(GetRect());
543 if (!mDecoded
.IsEqualEdges(frameRect
)) {
544 // The decoder should have produced rows starting from either the bottom or
545 // the top of the image. We need to calculate the region for which we have
546 // not yet invalidated. And if the orientation swaps width and height then
547 // its from the left or right.
548 IntRect
delta(0, 0, frameRect
.width
, 0);
549 if (!aOrientationSwapsWidthAndHeight
) {
550 delta
.width
= frameRect
.width
;
551 if (mDecoded
.y
== 0) {
552 delta
.y
= mDecoded
.height
;
553 delta
.height
= frameRect
.height
- mDecoded
.height
;
554 } else if (mDecoded
.y
+ mDecoded
.height
== frameRect
.height
) {
555 delta
.height
= frameRect
.height
- mDecoded
.y
;
557 MOZ_ASSERT_UNREACHABLE("Decoder only updated middle of image!");
561 delta
.height
= frameRect
.height
;
562 if (mDecoded
.x
== 0) {
563 delta
.x
= mDecoded
.width
;
564 delta
.width
= frameRect
.width
- mDecoded
.width
;
565 } else if (mDecoded
.x
+ mDecoded
.width
== frameRect
.width
) {
566 delta
.width
= frameRect
.width
- mDecoded
.x
;
568 MOZ_ASSERT_UNREACHABLE("Decoder only updated middle of image!");
573 ImageUpdatedInternal(delta
);
576 MOZ_ASSERT(mDecoded
.IsEqualEdges(frameRect
));
579 FinalizeSurfaceInternal();
584 // The image is now complete, wake up anyone who's waiting.
585 mMonitor
.NotifyAll();
588 uint32_t imgFrame::GetImageBytesPerRow() const {
589 mMonitor
.AssertCurrentThreadOwns();
592 return mImageSize
.width
* BytesPerPixel(mFormat
);
598 uint32_t imgFrame::GetImageDataLength() const {
599 return GetImageBytesPerRow() * mImageSize
.height
;
602 void imgFrame::FinalizeSurface() {
603 MonitorAutoLock
lock(mMonitor
);
604 FinalizeSurfaceInternal();
607 void imgFrame::FinalizeSurfaceInternal() {
608 mMonitor
.AssertCurrentThreadOwns();
610 // Not all images will have mRawSurface to finalize (i.e. paletted images).
611 if (mShouldRecycle
|| !mRawSurface
||
612 mRawSurface
->GetType() != SurfaceType::DATA_SHARED
) {
616 auto* sharedSurf
= static_cast<SourceSurfaceSharedData
*>(mRawSurface
.get());
617 sharedSurf
->Finalize();
620 already_AddRefed
<SourceSurface
> imgFrame::GetSourceSurface() {
621 MonitorAutoLock
lock(mMonitor
);
622 return GetSourceSurfaceInternal();
625 already_AddRefed
<SourceSurface
> imgFrame::GetSourceSurfaceInternal() {
626 mMonitor
.AssertCurrentThreadOwns();
629 if (mOptSurface
->IsValid()) {
630 RefPtr
<SourceSurface
> surf(mOptSurface
);
631 return surf
.forget();
633 mOptSurface
= nullptr;
636 if (mBlankRawSurface
) {
637 // We are going to return the blank surface because of the flags.
638 // We are including comments here that are copied from below
639 // just so that we are on the same page!
640 RefPtr
<SourceSurface
> surf(mBlankRawSurface
);
641 return surf
.forget();
644 RefPtr
<SourceSurface
> surf(mRawSurface
);
645 return surf
.forget();
648 void imgFrame::Abort() {
649 MonitorAutoLock
lock(mMonitor
);
653 // Wake up anyone who's waiting.
654 mMonitor
.NotifyAll();
657 bool imgFrame::IsAborted() const {
658 MonitorAutoLock
lock(mMonitor
);
662 bool imgFrame::IsFinished() const {
663 MonitorAutoLock
lock(mMonitor
);
667 void imgFrame::WaitUntilFinished() const {
668 MonitorAutoLock
lock(mMonitor
);
671 // Return if we're aborted or complete.
672 if (mAborted
|| mFinished
) {
676 // Not complete yet, so we'll have to wait.
681 bool imgFrame::AreAllPixelsWritten() const {
682 mMonitor
.AssertCurrentThreadOwns();
683 return mDecoded
.IsEqualInterior(GetRect());
686 void imgFrame::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf
,
687 const AddSizeOfCb
& aCallback
) const {
688 MonitorAutoLock
lock(mMonitor
);
690 AddSizeOfCbData metadata
;
691 metadata
.mFinished
= mFinished
;
694 metadata
.mHeapBytes
+= aMallocSizeOf(mOptSurface
);
696 SourceSurface::SizeOfInfo info
;
697 mOptSurface
->SizeOfExcludingThis(aMallocSizeOf
, info
);
698 metadata
.Accumulate(info
);
701 metadata
.mHeapBytes
+= aMallocSizeOf(mRawSurface
);
703 SourceSurface::SizeOfInfo info
;
704 mRawSurface
->SizeOfExcludingThis(aMallocSizeOf
, info
);
705 metadata
.Accumulate(info
);
712 } // namespace mozilla