1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=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/. */
7 #include "ImageContainer.h"
9 #include <string.h> // for memcpy, memset
11 #include "GLImages.h" // for SurfaceTextureImage
12 #include "MediaInfo.h" // VideoInfo::Rotation
13 #include "YCbCrUtils.h" // for YCbCr conversions
14 #include "gfx2DGlue.h"
15 #include "gfxPlatform.h" // for gfxPlatform
16 #include "gfxUtils.h" // for gfxUtils
18 #include "mozilla/CheckedInt.h"
19 #include "mozilla/ProfilerLabels.h"
20 #include "mozilla/RefPtr.h" // for already_AddRefed
21 #include "mozilla/StaticPrefs_layers.h"
22 #include "mozilla/gfx/2D.h"
23 #include "mozilla/gfx/gfxVars.h"
24 #include "mozilla/gfx/Swizzle.h"
25 #include "mozilla/ipc/CrossProcessMutex.h" // for CrossProcessMutex, etc
26 #include "mozilla/layers/CompositorTypes.h"
27 #include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
28 #include "mozilla/layers/ImageClient.h" // for ImageClient
29 #include "mozilla/layers/ImageDataSerializer.h" // for SurfaceDescriptorBuffer
30 #include "mozilla/layers/LayersMessages.h"
31 #include "mozilla/layers/SharedPlanarYCbCrImage.h"
32 #include "mozilla/layers/SharedRGBImage.h"
33 #include "mozilla/layers/TextureClientRecycleAllocator.h"
34 #include "nsProxyRelease.h"
35 #include "nsISupportsUtils.h" // for NS_IF_ADDREF
38 # include "MacIOSurfaceImage.h"
44 # include "gfxWindowsPlatform.h"
45 # include "mozilla/gfx/DeviceManagerDx.h"
46 # include "mozilla/layers/D3D11ShareHandleImage.h"
47 # include "mozilla/layers/D3D11YCbCrImage.h"
50 namespace mozilla::layers
{
52 using namespace mozilla::gfx
;
53 using namespace mozilla::ipc
;
55 Atomic
<int32_t> Image::sSerialCounter(0);
57 Atomic
<uint32_t> ImageContainer::sGenerationCounter(0);
59 static void CopyPlane(uint8_t* aDst
, const uint8_t* aSrc
,
60 const gfx::IntSize
& aSize
, int32_t aStride
,
63 RefPtr
<PlanarYCbCrImage
> ImageFactory::CreatePlanarYCbCrImage(
64 const gfx::IntSize
& aScaleHint
, BufferRecycleBin
* aRecycleBin
) {
65 return new RecyclingPlanarYCbCrImage(aRecycleBin
);
68 BufferRecycleBin::BufferRecycleBin()
69 : mLock("mozilla.layers.BufferRecycleBin.mLock")
70 // This member is only valid when the bin is not empty and will be
71 // properly initialized in RecycleBuffer, but initializing it here avoids
72 // static analysis noise.
74 mRecycledBufferSize(0) {}
76 void BufferRecycleBin::RecycleBuffer(UniquePtr
<uint8_t[]> aBuffer
,
78 MutexAutoLock
lock(mLock
);
80 if (!mRecycledBuffers
.IsEmpty() && aSize
!= mRecycledBufferSize
) {
81 mRecycledBuffers
.Clear();
83 mRecycledBufferSize
= aSize
;
84 mRecycledBuffers
.AppendElement(std::move(aBuffer
));
87 UniquePtr
<uint8_t[]> BufferRecycleBin::GetBuffer(uint32_t aSize
) {
88 MutexAutoLock
lock(mLock
);
90 if (mRecycledBuffers
.IsEmpty() || mRecycledBufferSize
!= aSize
) {
91 return UniquePtr
<uint8_t[]>(new (fallible
) uint8_t[aSize
]);
94 return mRecycledBuffers
.PopLastElement();
97 void BufferRecycleBin::ClearRecycledBuffers() {
98 MutexAutoLock
lock(mLock
);
99 if (!mRecycledBuffers
.IsEmpty()) {
100 mRecycledBuffers
.Clear();
102 mRecycledBufferSize
= 0;
105 ImageContainerListener::ImageContainerListener(ImageContainer
* aImageContainer
)
106 : mLock("mozilla.layers.ImageContainerListener.mLock"),
107 mImageContainer(aImageContainer
) {}
109 ImageContainerListener::~ImageContainerListener() = default;
111 void ImageContainerListener::NotifyComposite(
112 const ImageCompositeNotification
& aNotification
) {
113 MutexAutoLock
lock(mLock
);
114 if (mImageContainer
) {
115 mImageContainer
->NotifyComposite(aNotification
);
119 void ImageContainerListener::NotifyDropped(uint32_t aDropped
) {
120 MutexAutoLock
lock(mLock
);
121 if (mImageContainer
) {
122 mImageContainer
->NotifyDropped(aDropped
);
126 void ImageContainerListener::ClearImageContainer() {
127 MutexAutoLock
lock(mLock
);
128 mImageContainer
= nullptr;
131 void ImageContainerListener::DropImageClient() {
132 MutexAutoLock
lock(mLock
);
133 if (mImageContainer
) {
134 mImageContainer
->DropImageClient();
138 already_AddRefed
<ImageClient
> ImageContainer::GetImageClient() {
139 RecursiveMutexAutoLock
mon(mRecursiveMutex
);
141 RefPtr
<ImageClient
> imageClient
= mImageClient
;
142 return imageClient
.forget();
145 void ImageContainer::DropImageClient() {
146 RecursiveMutexAutoLock
mon(mRecursiveMutex
);
148 mImageClient
->ClearCachedResources();
149 mImageClient
= nullptr;
153 void ImageContainer::EnsureImageClient() {
154 // If we're not forcing a new ImageClient, then we can skip this if we don't
155 // have an existing ImageClient, or if the existing one belongs to an IPC
156 // actor that is still open.
161 mImageClient
->GetForwarder()->GetLayersIPCActor()->IPCOpen()) {
165 RefPtr
<ImageBridgeChild
> imageBridge
= ImageBridgeChild::GetSingleton();
168 imageBridge
->CreateImageClient(CompositableType::IMAGE
, this);
170 mAsyncContainerHandle
= mImageClient
->GetAsyncHandle();
172 // It's okay to drop the async container handle since the ImageBridgeChild
173 // is going to die anyway.
174 mAsyncContainerHandle
= CompositableHandle();
179 ImageContainer::ImageContainer(Mode flag
)
180 : mRecursiveMutex("ImageContainer.mRecursiveMutex"),
181 mGenerationCounter(++sGenerationCounter
),
183 mDroppedImageCount(0),
184 mImageFactory(new ImageFactory()),
185 mRotation(VideoRotation::kDegree_0
),
186 mRecycleBin(new BufferRecycleBin()),
187 mIsAsync(flag
== ASYNCHRONOUS
),
188 mCurrentProducerID(-1) {
189 if (flag
== ASYNCHRONOUS
) {
190 mNotifyCompositeListener
= new ImageContainerListener(this);
195 ImageContainer::ImageContainer(const CompositableHandle
& aHandle
)
196 : mRecursiveMutex("ImageContainer.mRecursiveMutex"),
197 mGenerationCounter(++sGenerationCounter
),
199 mDroppedImageCount(0),
200 mImageFactory(nullptr),
201 mRotation(VideoRotation::kDegree_0
),
202 mRecycleBin(nullptr),
204 mAsyncContainerHandle(aHandle
),
205 mCurrentProducerID(-1) {
206 MOZ_ASSERT(mAsyncContainerHandle
);
209 ImageContainer::~ImageContainer() {
210 if (mNotifyCompositeListener
) {
211 mNotifyCompositeListener
->ClearImageContainer();
213 if (mAsyncContainerHandle
) {
214 if (RefPtr
<ImageBridgeChild
> imageBridge
=
215 ImageBridgeChild::GetSingleton()) {
216 imageBridge
->ForgetImageContainer(mAsyncContainerHandle
);
221 /* static */ nsresult
Image::AllocateSurfaceDescriptorBufferRgb(
222 const gfx::IntSize
& aSize
, gfx::SurfaceFormat aFormat
, uint8_t*& aOutBuffer
,
223 SurfaceDescriptorBuffer
& aSdBuffer
, int32_t& aStride
,
224 const std::function
<layers::MemoryOrShmem(uint32_t)>& aAllocate
) {
225 aStride
= ImageDataSerializer::ComputeRGBStride(aFormat
, aSize
.width
);
226 size_t length
= ImageDataSerializer::ComputeRGBBufferSize(aSize
, aFormat
);
228 if (aStride
<= 0 || length
== 0) {
229 return NS_ERROR_INVALID_ARG
;
232 aSdBuffer
.desc() = RGBDescriptor(aSize
, aFormat
);
233 aSdBuffer
.data() = aAllocate(length
);
235 const layers::MemoryOrShmem
& memOrShmem
= aSdBuffer
.data();
236 switch (memOrShmem
.type()) {
237 case layers::MemoryOrShmem::Tuintptr_t
:
238 aOutBuffer
= reinterpret_cast<uint8_t*>(memOrShmem
.get_uintptr_t());
240 case layers::MemoryOrShmem::TShmem
:
241 aOutBuffer
= memOrShmem
.get_Shmem().get
<uint8_t>();
244 return NS_ERROR_OUT_OF_MEMORY
;
247 MOZ_ASSERT(aOutBuffer
);
251 nsresult
Image::BuildSurfaceDescriptorBuffer(
252 SurfaceDescriptorBuffer
& aSdBuffer
, BuildSdbFlags aFlags
,
253 const std::function
<MemoryOrShmem(uint32_t)>& aAllocate
) {
254 return NS_ERROR_NOT_IMPLEMENTED
;
257 Maybe
<SurfaceDescriptor
> Image::GetDesc() { return GetDescFromTexClient(); }
259 Maybe
<SurfaceDescriptor
> Image::GetDescFromTexClient(
260 TextureClient
* const tcOverride
) {
261 RefPtr
<TextureClient
> tc
= tcOverride
;
263 tc
= GetTextureClient(nullptr);
269 const auto& tcd
= tc
->GetInternalData();
271 SurfaceDescriptor ret
;
272 if (!tcd
->Serialize(ret
)) {
278 already_AddRefed
<ImageContainerListener
>
279 ImageContainer::GetImageContainerListener() const {
280 MOZ_ASSERT(InImageBridgeChildThread());
281 return do_AddRef(mNotifyCompositeListener
);
284 RefPtr
<PlanarYCbCrImage
> ImageContainer::CreatePlanarYCbCrImage() {
285 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
287 if (mImageClient
&& mImageClient
->AsImageClientSingle()) {
288 return new SharedPlanarYCbCrImage(mImageClient
);
290 if (mRecycleAllocator
) {
291 return new SharedPlanarYCbCrImage(mRecycleAllocator
);
293 return mImageFactory
->CreatePlanarYCbCrImage(mScaleHint
, mRecycleBin
);
296 RefPtr
<SharedRGBImage
> ImageContainer::CreateSharedRGBImage() {
297 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
299 if (mImageClient
&& mImageClient
->AsImageClientSingle()) {
300 return new SharedRGBImage(mImageClient
);
302 if (mRecycleAllocator
) {
303 return new SharedRGBImage(mRecycleAllocator
);
308 void ImageContainer::SetCurrentImageInternal(
309 const nsTArray
<NonOwningImage
>& aImages
) {
310 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
312 mGenerationCounter
= ++sGenerationCounter
;
314 if (!aImages
.IsEmpty()) {
315 NS_ASSERTION(mCurrentImages
.IsEmpty() ||
316 mCurrentImages
[0].mProducerID
!= aImages
[0].mProducerID
||
317 mCurrentImages
[0].mFrameID
<= aImages
[0].mFrameID
,
318 "frame IDs shouldn't go backwards");
319 if (aImages
[0].mProducerID
!= mCurrentProducerID
) {
320 mCurrentProducerID
= aImages
[0].mProducerID
;
324 nsTArray
<OwningImage
> newImages
;
326 for (uint32_t i
= 0; i
< aImages
.Length(); ++i
) {
327 NS_ASSERTION(aImages
[i
].mImage
, "image can't be null");
328 NS_ASSERTION(!aImages
[i
].mTimeStamp
.IsNull() || aImages
.Length() == 1,
329 "Multiple images require timestamps");
331 NS_ASSERTION(aImages
[i
].mTimeStamp
>= aImages
[i
- 1].mTimeStamp
,
332 "Timestamps must not decrease");
333 NS_ASSERTION(aImages
[i
].mFrameID
> aImages
[i
- 1].mFrameID
,
334 "FrameIDs must increase");
335 NS_ASSERTION(aImages
[i
].mProducerID
== aImages
[i
- 1].mProducerID
,
336 "ProducerIDs must be the same");
338 OwningImage
* img
= newImages
.AppendElement();
339 img
->mImage
= aImages
[i
].mImage
;
340 img
->mTimeStamp
= aImages
[i
].mTimeStamp
;
341 img
->mFrameID
= aImages
[i
].mFrameID
;
342 img
->mProducerID
= aImages
[i
].mProducerID
;
343 for (const auto& oldImg
: mCurrentImages
) {
344 if (oldImg
.mFrameID
== img
->mFrameID
&&
345 oldImg
.mProducerID
== img
->mProducerID
) {
346 img
->mComposited
= oldImg
.mComposited
;
352 mCurrentImages
= std::move(newImages
);
355 void ImageContainer::ClearImagesFromImageBridge() {
356 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
357 SetCurrentImageInternal(nsTArray
<NonOwningImage
>());
360 void ImageContainer::SetCurrentImages(const nsTArray
<NonOwningImage
>& aImages
) {
361 AUTO_PROFILER_LABEL("ImageContainer::SetCurrentImages", GRAPHICS
);
362 MOZ_ASSERT(!aImages
.IsEmpty());
363 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
365 if (RefPtr
<ImageBridgeChild
> imageBridge
=
366 ImageBridgeChild::GetSingleton()) {
367 imageBridge
->UpdateImageClient(this);
370 SetCurrentImageInternal(aImages
);
373 void ImageContainer::ClearAllImages() {
374 mRecursiveMutex
.Lock();
376 RefPtr
<ImageClient
> imageClient
= mImageClient
;
377 mRecursiveMutex
.Unlock();
379 // Let ImageClient release all TextureClients. This doesn't return
380 // until ImageBridge has called ClearCurrentImageFromImageBridge.
381 if (RefPtr
<ImageBridgeChild
> imageBridge
=
382 ImageBridgeChild::GetSingleton()) {
383 imageBridge
->FlushAllImages(imageClient
, this);
388 SetCurrentImageInternal(nsTArray
<NonOwningImage
>());
389 mRecursiveMutex
.Unlock();
392 void ImageContainer::ClearCachedResources() {
393 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
394 if (mImageClient
&& mImageClient
->AsImageClientSingle()) {
395 if (!mImageClient
->HasTextureClientRecycler()) {
398 mImageClient
->GetTextureClientRecycler()->ShrinkToMinimumSize();
401 return mRecycleBin
->ClearRecycledBuffers();
404 void ImageContainer::SetCurrentImageInTransaction(Image
* aImage
) {
405 AutoTArray
<NonOwningImage
, 1> images
;
406 images
.AppendElement(NonOwningImage(aImage
));
407 SetCurrentImagesInTransaction(images
);
410 void ImageContainer::SetCurrentImagesInTransaction(
411 const nsTArray
<NonOwningImage
>& aImages
) {
412 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
413 NS_ASSERTION(!HasImageClient(),
414 "Should use async image transfer with ImageBridge.");
416 SetCurrentImageInternal(aImages
);
419 bool ImageContainer::IsAsync() const { return mIsAsync
; }
421 CompositableHandle
ImageContainer::GetAsyncContainerHandle() {
422 NS_ASSERTION(IsAsync(),
423 "Shared image ID is only relevant to async ImageContainers");
424 RecursiveMutexAutoLock
mon(mRecursiveMutex
);
425 NS_ASSERTION(mAsyncContainerHandle
, "Should have a shared image ID");
427 return mAsyncContainerHandle
;
430 bool ImageContainer::HasCurrentImage() {
431 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
433 return !mCurrentImages
.IsEmpty();
436 void ImageContainer::GetCurrentImages(nsTArray
<OwningImage
>* aImages
,
437 uint32_t* aGenerationCounter
) {
438 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
440 *aImages
= mCurrentImages
.Clone();
441 if (aGenerationCounter
) {
442 *aGenerationCounter
= mGenerationCounter
;
446 gfx::IntSize
ImageContainer::GetCurrentSize() {
447 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
449 if (mCurrentImages
.IsEmpty()) {
450 return gfx::IntSize(0, 0);
453 return mCurrentImages
[0].mImage
->GetSize();
456 void ImageContainer::NotifyComposite(
457 const ImageCompositeNotification
& aNotification
) {
458 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
460 // An image composition notification is sent the first time a particular
461 // image is composited by an ImageHost. Thus, every time we receive such
462 // a notification, a new image has been painted.
465 if (aNotification
.producerID() == mCurrentProducerID
) {
466 for (auto& img
: mCurrentImages
) {
467 if (img
.mFrameID
== aNotification
.frameID()) {
468 img
.mComposited
= true;
473 if (!aNotification
.imageTimeStamp().IsNull()) {
474 mPaintDelay
= aNotification
.firstCompositeTimeStamp() -
475 aNotification
.imageTimeStamp();
479 void ImageContainer::NotifyDropped(uint32_t aDropped
) {
480 mDroppedImageCount
+= aDropped
;
483 void ImageContainer::EnsureRecycleAllocatorForRDD(
484 KnowsCompositor
* aKnowsCompositor
) {
485 MOZ_ASSERT(!mIsAsync
);
486 MOZ_ASSERT(XRE_IsRDDProcess());
488 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
489 MOZ_ASSERT(!mImageClient
);
491 if (mRecycleAllocator
&&
492 aKnowsCompositor
== mRecycleAllocator
->GetKnowsCompositor()) {
496 if (!StaticPrefs::layers_recycle_allocator_rdd_AtStartup()) {
500 static const uint32_t MAX_POOLED_VIDEO_COUNT
= 5;
503 new layers::TextureClientRecycleAllocator(aKnowsCompositor
);
504 mRecycleAllocator
->SetMaxPoolSize(MAX_POOLED_VIDEO_COUNT
);
508 already_AddRefed
<D3D11RecycleAllocator
>
509 ImageContainer::GetD3D11RecycleAllocator(KnowsCompositor
* aKnowsCompositor
,
510 gfx::SurfaceFormat aPreferredFormat
) {
511 MOZ_ASSERT(aKnowsCompositor
);
513 if (!aKnowsCompositor
->SupportsD3D11()) {
517 RefPtr
<ID3D11Device
> device
= gfx::DeviceManagerDx::Get()->GetImageDevice();
522 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
523 if (mD3D11RecycleAllocator
&& mD3D11RecycleAllocator
->mDevice
== device
&&
524 mD3D11RecycleAllocator
->GetKnowsCompositor() == aKnowsCompositor
) {
525 return do_AddRef(mD3D11RecycleAllocator
);
528 mD3D11RecycleAllocator
=
529 new D3D11RecycleAllocator(aKnowsCompositor
, device
, aPreferredFormat
);
531 if (device
!= DeviceManagerDx::Get()->GetCompositorDevice()) {
532 RefPtr
<SyncObjectClient
> syncObject
=
533 SyncObjectClient::CreateSyncObjectClient(
534 aKnowsCompositor
->GetTextureFactoryIdentifier().mSyncHandle
,
536 mD3D11RecycleAllocator
->SetSyncObject(syncObject
);
539 return do_AddRef(mD3D11RecycleAllocator
);
542 already_AddRefed
<D3D11YCbCrRecycleAllocator
>
543 ImageContainer::GetD3D11YCbCrRecycleAllocator(
544 KnowsCompositor
* aKnowsCompositor
) {
545 if (!aKnowsCompositor
->SupportsD3D11() ||
546 !gfx::DeviceManagerDx::Get()->GetImageDevice()) {
550 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
551 if (mD3D11YCbCrRecycleAllocator
&&
552 aKnowsCompositor
== mD3D11YCbCrRecycleAllocator
->GetKnowsCompositor()) {
553 return do_AddRef(mD3D11YCbCrRecycleAllocator
);
556 mD3D11YCbCrRecycleAllocator
=
557 new D3D11YCbCrRecycleAllocator(aKnowsCompositor
);
558 return do_AddRef(mD3D11YCbCrRecycleAllocator
);
563 already_AddRefed
<MacIOSurfaceRecycleAllocator
>
564 ImageContainer::GetMacIOSurfaceRecycleAllocator() {
565 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
566 if (!mMacIOSurfaceRecycleAllocator
) {
567 mMacIOSurfaceRecycleAllocator
= new MacIOSurfaceRecycleAllocator();
570 return do_AddRef(mMacIOSurfaceRecycleAllocator
);
575 // https://searchfox.org/mozilla-central/source/dom/media/ipc/RemoteImageHolder.cpp#46
577 Maybe
<PlanarYCbCrData
> PlanarYCbCrData::From(
578 const SurfaceDescriptorBuffer
& sdb
) {
579 if (sdb
.desc().type() != BufferDescriptor::TYCbCrDescriptor
) {
582 const YCbCrDescriptor
& yuvDesc
= sdb
.desc().get_YCbCrDescriptor();
584 Maybe
<Range
<uint8_t>> buffer
;
585 const MemoryOrShmem
& memOrShmem
= sdb
.data();
586 switch (memOrShmem
.type()) {
587 case MemoryOrShmem::Tuintptr_t
:
588 gfxCriticalError() << "PlanarYCbCrData::From SurfaceDescriptorBuffer "
589 "w/uintptr_t unsupported.";
591 case MemoryOrShmem::TShmem
:
592 buffer
.emplace(memOrShmem
.get_Shmem().Range
<uint8_t>());
595 MOZ_ASSERT(false, "Unknown MemoryOrShmem type");
602 PlanarYCbCrData yuvData
;
603 yuvData
.mYStride
= AssertedCast
<int32_t>(yuvDesc
.yStride());
604 yuvData
.mCbCrStride
= AssertedCast
<int32_t>(yuvDesc
.cbCrStride());
605 // default mYSkip, mCbSkip, mCrSkip because not held in YCbCrDescriptor
606 yuvData
.mYSkip
= yuvData
.mCbSkip
= yuvData
.mCrSkip
= 0;
607 yuvData
.mPictureRect
= yuvDesc
.display();
608 yuvData
.mStereoMode
= yuvDesc
.stereoMode();
609 yuvData
.mColorDepth
= yuvDesc
.colorDepth();
610 yuvData
.mYUVColorSpace
= yuvDesc
.yUVColorSpace();
611 yuvData
.mColorRange
= yuvDesc
.colorRange();
612 yuvData
.mChromaSubsampling
= yuvDesc
.chromaSubsampling();
614 const auto GetPlanePtr
= [&](const uint32_t beginOffset
,
615 const gfx::IntSize size
,
616 const int32_t stride
) -> uint8_t* {
617 if (size
.width
> stride
) return nullptr;
618 auto bytesNeeded
= CheckedInt
<uintptr_t>(stride
) *
619 size
.height
; // Don't accept `stride*(h-1)+w`.
620 bytesNeeded
+= beginOffset
;
621 if (!bytesNeeded
.isValid() || bytesNeeded
.value() > buffer
->length()) {
623 << "PlanarYCbCrData::From asked for out-of-bounds plane data.";
626 return (buffer
->begin() + beginOffset
).get();
629 GetPlanePtr(yuvDesc
.yOffset(), yuvDesc
.ySize(), yuvData
.mYStride
);
631 GetPlanePtr(yuvDesc
.cbOffset(), yuvDesc
.cbCrSize(), yuvData
.mCbCrStride
);
633 GetPlanePtr(yuvDesc
.crOffset(), yuvDesc
.cbCrSize(), yuvData
.mCbCrStride
);
635 if (yuvData
.mYSkip
|| yuvData
.mCbSkip
|| yuvData
.mCrSkip
||
636 yuvDesc
.ySize().width
< 0 || yuvDesc
.ySize().height
< 0 ||
637 yuvDesc
.cbCrSize().width
< 0 || yuvDesc
.cbCrSize().height
< 0 ||
638 yuvData
.mYStride
< 0 || yuvData
.mCbCrStride
< 0 || !yuvData
.mYChannel
||
639 !yuvData
.mCbChannel
|| !yuvData
.mCrChannel
) {
640 gfxCriticalError() << "Unusual PlanarYCbCrData: " << yuvData
.mYSkip
<< ","
641 << yuvData
.mCbSkip
<< "," << yuvData
.mCrSkip
<< ", "
642 << yuvDesc
.ySize().width
<< "," << yuvDesc
.ySize().height
643 << ", " << yuvDesc
.cbCrSize().width
<< ","
644 << yuvDesc
.cbCrSize().height
<< ", " << yuvData
.mYStride
645 << "," << yuvData
.mCbCrStride
<< ", "
646 << yuvData
.mYChannel
<< "," << yuvData
.mCbChannel
<< ","
647 << yuvData
.mCrChannel
;
651 return Some(yuvData
);
656 PlanarYCbCrImage::PlanarYCbCrImage()
657 : Image(nullptr, ImageFormat::PLANAR_YCBCR
),
658 mOffscreenFormat(SurfaceFormat::UNKNOWN
),
661 nsresult
PlanarYCbCrImage::BuildSurfaceDescriptorBuffer(
662 SurfaceDescriptorBuffer
& aSdBuffer
, BuildSdbFlags aFlags
,
663 const std::function
<MemoryOrShmem(uint32_t)>& aAllocate
) {
664 const PlanarYCbCrData
* pdata
= GetData();
665 MOZ_ASSERT(pdata
, "must have PlanarYCbCrData");
666 MOZ_ASSERT(pdata
->mYSkip
== 0 && pdata
->mCbSkip
== 0 && pdata
->mCrSkip
== 0,
667 "YCbCrDescriptor doesn't hold skip values");
669 if (aFlags
& BuildSdbFlags::RgbOnly
) {
670 gfx::IntSize
size(mSize
);
671 auto format
= gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());
672 gfx::GetYCbCrToRGBDestFormatAndSize(mData
, format
, size
);
674 uint8_t* buffer
= nullptr;
676 nsresult rv
= AllocateSurfaceDescriptorBufferRgb(
677 size
, format
, buffer
, aSdBuffer
, stride
, aAllocate
);
678 if (NS_WARN_IF(NS_FAILED(rv
))) {
682 // If we can copy directly from the surface, let's do that to avoid the YUV
683 // to RGB conversion.
684 if (mSourceSurface
&& mSourceSurface
->GetSize() == size
) {
685 DataSourceSurface::ScopedMap
map(mSourceSurface
, DataSourceSurface::READ
);
686 if (map
.IsMapped() && SwizzleData(map
.GetData(), map
.GetStride(),
687 mSourceSurface
->GetFormat(), buffer
,
688 stride
, format
, size
)) {
693 gfx::ConvertYCbCrToRGB(mData
, format
, size
, buffer
, stride
);
697 auto ySize
= pdata
->YDataSize();
698 auto cbcrSize
= pdata
->CbCrDataSize();
702 ImageDataSerializer::ComputeYCbCrOffsets(pdata
->mYStride
, ySize
.height
,
703 pdata
->mCbCrStride
, cbcrSize
.height
,
704 yOffset
, cbOffset
, crOffset
);
706 uint32_t bufferSize
= ImageDataSerializer::ComputeYCbCrBufferSize(
707 ySize
, pdata
->mYStride
, cbcrSize
, pdata
->mCbCrStride
, yOffset
, cbOffset
,
710 aSdBuffer
.data() = aAllocate(bufferSize
);
712 uint8_t* buffer
= nullptr;
713 const MemoryOrShmem
& memOrShmem
= aSdBuffer
.data();
714 switch (memOrShmem
.type()) {
715 case MemoryOrShmem::Tuintptr_t
:
716 buffer
= reinterpret_cast<uint8_t*>(memOrShmem
.get_uintptr_t());
718 case MemoryOrShmem::TShmem
:
719 buffer
= memOrShmem
.get_Shmem().get
<uint8_t>();
726 return NS_ERROR_OUT_OF_MEMORY
;
729 aSdBuffer
.desc() = YCbCrDescriptor(
730 pdata
->mPictureRect
, ySize
, pdata
->mYStride
, cbcrSize
, pdata
->mCbCrStride
,
731 yOffset
, cbOffset
, crOffset
, pdata
->mStereoMode
, pdata
->mColorDepth
,
732 pdata
->mYUVColorSpace
, pdata
->mColorRange
, pdata
->mChromaSubsampling
);
734 CopyPlane(buffer
+ yOffset
, pdata
->mYChannel
, ySize
, pdata
->mYStride
,
736 CopyPlane(buffer
+ cbOffset
, pdata
->mCbChannel
, cbcrSize
, pdata
->mCbCrStride
,
738 CopyPlane(buffer
+ crOffset
, pdata
->mCrChannel
, cbcrSize
, pdata
->mCbCrStride
,
743 RecyclingPlanarYCbCrImage::~RecyclingPlanarYCbCrImage() {
745 mRecycleBin
->RecycleBuffer(std::move(mBuffer
), mBufferSize
);
749 size_t RecyclingPlanarYCbCrImage::SizeOfExcludingThis(
750 MallocSizeOf aMallocSizeOf
) const {
752 // - mData - just wraps mBuffer
753 // - Surfaces should be reported under gfx-surfaces-*:
756 // - mImplData is not used
759 size_t size
= aMallocSizeOf(mBuffer
.get());
761 // Could add in the future:
762 // - mBackendData (from base class)
767 UniquePtr
<uint8_t[]> RecyclingPlanarYCbCrImage::AllocateBuffer(uint32_t aSize
) {
768 return mRecycleBin
->GetBuffer(aSize
);
771 static void CopyPlane(uint8_t* aDst
, const uint8_t* aSrc
,
772 const gfx::IntSize
& aSize
, int32_t aStride
,
774 int32_t height
= aSize
.height
;
775 int32_t width
= aSize
.width
;
777 MOZ_RELEASE_ASSERT(width
<= aStride
);
780 // Fast path: planar input.
781 memcpy(aDst
, aSrc
, height
* aStride
);
783 for (int y
= 0; y
< height
; ++y
) {
784 const uint8_t* src
= aSrc
;
787 for (int x
= 0; x
< width
; ++x
) {
797 bool RecyclingPlanarYCbCrImage::CopyData(const Data
& aData
) {
798 // update buffer size
799 // Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
800 auto ySize
= aData
.YDataSize();
801 auto cbcrSize
= aData
.CbCrDataSize();
802 const auto checkedSize
=
803 CheckedInt
<uint32_t>(aData
.mCbCrStride
) * cbcrSize
.height
* 2 +
804 CheckedInt
<uint32_t>(aData
.mYStride
) * ySize
.height
*
805 (aData
.mAlpha
? 2 : 1);
807 if (!checkedSize
.isValid()) return false;
809 const auto size
= checkedSize
.value();
812 mBuffer
= AllocateBuffer(size
);
813 if (!mBuffer
) return false;
815 // update buffer size
818 mData
= aData
; // mAlpha will be set if aData has it
819 mData
.mYChannel
= mBuffer
.get();
820 mData
.mCbChannel
= mData
.mYChannel
+ mData
.mYStride
* ySize
.height
;
821 mData
.mCrChannel
= mData
.mCbChannel
+ mData
.mCbCrStride
* cbcrSize
.height
;
822 mData
.mYSkip
= mData
.mCbSkip
= mData
.mCrSkip
= 0;
824 CopyPlane(mData
.mYChannel
, aData
.mYChannel
, ySize
, aData
.mYStride
,
826 CopyPlane(mData
.mCbChannel
, aData
.mCbChannel
, cbcrSize
, aData
.mCbCrStride
,
828 CopyPlane(mData
.mCrChannel
, aData
.mCrChannel
, cbcrSize
, aData
.mCbCrStride
,
831 CopyPlane(mData
.mAlpha
->mChannel
, aData
.mAlpha
->mChannel
, ySize
,
832 aData
.mYStride
, aData
.mYSkip
);
835 mSize
= aData
.mPictureRect
.Size();
836 mOrigin
= aData
.mPictureRect
.TopLeft();
840 gfxImageFormat
PlanarYCbCrImage::GetOffscreenFormat() const {
841 return mOffscreenFormat
== SurfaceFormat::UNKNOWN
? gfxVars::OffscreenFormat()
845 bool PlanarYCbCrImage::AdoptData(const Data
& aData
) {
847 mSize
= aData
.mPictureRect
.Size();
848 mOrigin
= aData
.mPictureRect
.TopLeft();
852 already_AddRefed
<gfx::SourceSurface
> PlanarYCbCrImage::GetAsSourceSurface() {
853 if (mSourceSurface
) {
854 RefPtr
<gfx::SourceSurface
> surface(mSourceSurface
);
855 return surface
.forget();
858 gfx::IntSize
size(mSize
);
859 gfx::SurfaceFormat format
=
860 gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());
861 gfx::GetYCbCrToRGBDestFormatAndSize(mData
, format
, size
);
862 if (mSize
.width
> PlanarYCbCrImage::MAX_DIMENSION
||
863 mSize
.height
> PlanarYCbCrImage::MAX_DIMENSION
) {
864 NS_ERROR("Illegal image dest width or height");
868 RefPtr
<gfx::DataSourceSurface
> surface
=
869 gfx::Factory::CreateDataSourceSurface(size
, format
);
870 if (NS_WARN_IF(!surface
)) {
874 DataSourceSurface::ScopedMap
mapping(surface
, DataSourceSurface::WRITE
);
875 if (NS_WARN_IF(!mapping
.IsMapped())) {
879 gfx::ConvertYCbCrToRGB(mData
, format
, size
, mapping
.GetData(),
880 mapping
.GetStride());
882 mSourceSurface
= surface
;
884 return surface
.forget();
887 PlanarYCbCrImage::~PlanarYCbCrImage() {
888 NS_ReleaseOnMainThread("PlanarYCbCrImage::mSourceSurface",
889 mSourceSurface
.forget());
892 NVImage::NVImage() : Image(nullptr, ImageFormat::NV_IMAGE
), mBufferSize(0) {}
894 NVImage::~NVImage() {
895 NS_ReleaseOnMainThread("NVImage::mSourceSurface", mSourceSurface
.forget());
898 IntSize
NVImage::GetSize() const { return mSize
; }
900 IntRect
NVImage::GetPictureRect() const { return mData
.mPictureRect
; }
902 already_AddRefed
<SourceSurface
> NVImage::GetAsSourceSurface() {
903 if (mSourceSurface
) {
904 RefPtr
<gfx::SourceSurface
> surface(mSourceSurface
);
905 return surface
.forget();
908 // Convert the current NV12 or NV21 data to YUV420P so that we can follow the
909 // logics in PlanarYCbCrImage::GetAsSourceSurface().
910 auto ySize
= mData
.YDataSize();
911 auto cbcrSize
= mData
.CbCrDataSize();
912 const int bufferLength
=
913 ySize
.height
* mData
.mYStride
+ cbcrSize
.height
* cbcrSize
.width
* 2;
914 UniquePtr
<uint8_t[]> buffer(new uint8_t[bufferLength
]);
917 aData
.mCbCrStride
= cbcrSize
.width
;
920 aData
.mYChannel
= buffer
.get();
921 aData
.mCbChannel
= aData
.mYChannel
+ ySize
.height
* aData
.mYStride
;
922 aData
.mCrChannel
= aData
.mCbChannel
+ cbcrSize
.height
* aData
.mCbCrStride
;
924 if (mData
.mCbChannel
< mData
.mCrChannel
) { // NV12
925 libyuv::NV12ToI420(mData
.mYChannel
, mData
.mYStride
, mData
.mCbChannel
,
926 mData
.mCbCrStride
, aData
.mYChannel
, aData
.mYStride
,
927 aData
.mCbChannel
, aData
.mCbCrStride
, aData
.mCrChannel
,
928 aData
.mCbCrStride
, ySize
.width
, ySize
.height
);
930 libyuv::NV21ToI420(mData
.mYChannel
, mData
.mYStride
, mData
.mCrChannel
,
931 mData
.mCbCrStride
, aData
.mYChannel
, aData
.mYStride
,
932 aData
.mCbChannel
, aData
.mCbCrStride
, aData
.mCrChannel
,
933 aData
.mCbCrStride
, ySize
.width
, ySize
.height
);
936 // The logics in PlanarYCbCrImage::GetAsSourceSurface().
937 gfx::IntSize
size(mSize
);
938 gfx::SurfaceFormat format
= gfx::ImageFormatToSurfaceFormat(
939 gfxPlatform::GetPlatform()->GetOffscreenFormat());
940 gfx::GetYCbCrToRGBDestFormatAndSize(aData
, format
, size
);
941 if (mSize
.width
> PlanarYCbCrImage::MAX_DIMENSION
||
942 mSize
.height
> PlanarYCbCrImage::MAX_DIMENSION
) {
943 NS_ERROR("Illegal image dest width or height");
947 RefPtr
<gfx::DataSourceSurface
> surface
=
948 gfx::Factory::CreateDataSourceSurface(size
, format
);
949 if (NS_WARN_IF(!surface
)) {
953 DataSourceSurface::ScopedMap
mapping(surface
, DataSourceSurface::WRITE
);
954 if (NS_WARN_IF(!mapping
.IsMapped())) {
958 gfx::ConvertYCbCrToRGB(aData
, format
, size
, mapping
.GetData(),
959 mapping
.GetStride());
961 mSourceSurface
= surface
;
963 return surface
.forget();
966 nsresult
NVImage::BuildSurfaceDescriptorBuffer(
967 SurfaceDescriptorBuffer
& aSdBuffer
, BuildSdbFlags aFlags
,
968 const std::function
<MemoryOrShmem(uint32_t)>& aAllocate
) {
969 // Convert the current NV12 or NV21 data to YUV420P so that we can follow the
970 // logics in PlanarYCbCrImage::GetAsSourceSurface().
971 auto ySize
= mData
.YDataSize();
972 auto cbcrSize
= mData
.CbCrDataSize();
975 aData
.mCbCrStride
= cbcrSize
.width
;
978 aData
.mCbChannel
= aData
.mYChannel
+ ySize
.height
* aData
.mYStride
;
979 aData
.mCrChannel
= aData
.mCbChannel
+ cbcrSize
.height
* aData
.mCbCrStride
;
981 UniquePtr
<uint8_t[]> buffer
;
983 if (!mSourceSurface
) {
984 const int bufferLength
=
985 ySize
.height
* mData
.mYStride
+ cbcrSize
.height
* cbcrSize
.width
* 2;
986 buffer
= MakeUnique
<uint8_t[]>(bufferLength
);
987 aData
.mYChannel
= buffer
.get();
989 if (mData
.mCbChannel
< mData
.mCrChannel
) { // NV12
990 libyuv::NV12ToI420(mData
.mYChannel
, mData
.mYStride
, mData
.mCbChannel
,
991 mData
.mCbCrStride
, aData
.mYChannel
, aData
.mYStride
,
992 aData
.mCbChannel
, aData
.mCbCrStride
, aData
.mCrChannel
,
993 aData
.mCbCrStride
, ySize
.width
, ySize
.height
);
995 libyuv::NV21ToI420(mData
.mYChannel
, mData
.mYStride
, mData
.mCrChannel
,
996 mData
.mCbCrStride
, aData
.mYChannel
, aData
.mYStride
,
997 aData
.mCbChannel
, aData
.mCbCrStride
, aData
.mCrChannel
,
998 aData
.mCbCrStride
, ySize
.width
, ySize
.height
);
1002 // The logics in PlanarYCbCrImage::GetAsSourceSurface().
1003 gfx::IntSize
size(mSize
);
1004 gfx::SurfaceFormat format
= gfx::ImageFormatToSurfaceFormat(
1005 gfxPlatform::GetPlatform()->GetOffscreenFormat());
1006 gfx::GetYCbCrToRGBDestFormatAndSize(aData
, format
, size
);
1007 if (mSize
.width
> PlanarYCbCrImage::MAX_DIMENSION
||
1008 mSize
.height
> PlanarYCbCrImage::MAX_DIMENSION
) {
1009 NS_ERROR("Illegal image dest width or height");
1010 return NS_ERROR_FAILURE
;
1013 if (mSourceSurface
&& mSourceSurface
->GetSize() != size
) {
1014 return NS_ERROR_NOT_IMPLEMENTED
;
1017 uint8_t* output
= nullptr;
1019 nsresult rv
= AllocateSurfaceDescriptorBufferRgb(
1020 size
, format
, output
, aSdBuffer
, stride
, aAllocate
);
1021 if (NS_WARN_IF(NS_FAILED(rv
))) {
1025 if (!mSourceSurface
) {
1026 gfx::ConvertYCbCrToRGB(aData
, format
, size
, output
, stride
);
1030 DataSourceSurface::ScopedMap
map(mSourceSurface
, DataSourceSurface::WRITE
);
1031 if (NS_WARN_IF(!map
.IsMapped())) {
1032 return NS_ERROR_FAILURE
;
1035 if (!SwizzleData(map
.GetData(), map
.GetStride(), mSourceSurface
->GetFormat(),
1036 output
, stride
, format
, size
)) {
1037 return NS_ERROR_FAILURE
;
1043 bool NVImage::IsValid() const { return !!mBufferSize
; }
1045 uint32_t NVImage::GetBufferSize() const { return mBufferSize
; }
1047 NVImage
* NVImage::AsNVImage() { return this; };
1049 bool NVImage::SetData(const Data
& aData
) {
1050 MOZ_ASSERT(aData
.mCbSkip
== 1 && aData
.mCrSkip
== 1);
1051 MOZ_ASSERT((int)std::abs(aData
.mCbChannel
- aData
.mCrChannel
) == 1);
1053 // Calculate buffer size
1054 // Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
1055 const auto checkedSize
=
1056 CheckedInt
<uint32_t>(aData
.YDataSize().height
) * aData
.mYStride
+
1057 CheckedInt
<uint32_t>(aData
.CbCrDataSize().height
) * aData
.mCbCrStride
;
1059 if (!checkedSize
.isValid()) return false;
1061 const auto size
= checkedSize
.value();
1063 // Allocate a new buffer.
1064 mBuffer
= AllocateBuffer(size
);
1069 // Update mBufferSize.
1074 mData
.mYChannel
= mBuffer
.get();
1075 mData
.mCbChannel
= mData
.mYChannel
+ (aData
.mCbChannel
- aData
.mYChannel
);
1076 mData
.mCrChannel
= mData
.mYChannel
+ (aData
.mCrChannel
- aData
.mYChannel
);
1079 mSize
= aData
.mPictureRect
.Size();
1081 // Copy the input data into mBuffer.
1082 // This copies the y-channel and the interleaving CbCr-channel.
1083 memcpy(mData
.mYChannel
, aData
.mYChannel
, mBufferSize
);
1088 const NVImage::Data
* NVImage::GetData() const { return &mData
; }
1090 UniquePtr
<uint8_t[]> NVImage::AllocateBuffer(uint32_t aSize
) {
1091 UniquePtr
<uint8_t[]> buffer(new uint8_t[aSize
]);
1095 SourceSurfaceImage::SourceSurfaceImage(const gfx::IntSize
& aSize
,
1096 gfx::SourceSurface
* aSourceSurface
)
1097 : Image(nullptr, ImageFormat::MOZ2D_SURFACE
),
1099 mSourceSurface(aSourceSurface
),
1100 mTextureFlags(TextureFlags::DEFAULT
) {}
1102 SourceSurfaceImage::SourceSurfaceImage(gfx::SourceSurface
* aSourceSurface
)
1103 : Image(nullptr, ImageFormat::MOZ2D_SURFACE
),
1104 mSize(aSourceSurface
->GetSize()),
1105 mSourceSurface(aSourceSurface
),
1106 mTextureFlags(TextureFlags::DEFAULT
) {}
1108 SourceSurfaceImage::~SourceSurfaceImage() {
1109 NS_ReleaseOnMainThread("SourceSurfaceImage::mSourceSurface",
1110 mSourceSurface
.forget());
1113 TextureClient
* SourceSurfaceImage::GetTextureClient(
1114 KnowsCompositor
* aKnowsCompositor
) {
1115 if (!aKnowsCompositor
) {
1119 return mTextureClients
.WithEntryHandle(
1120 aKnowsCompositor
->GetSerial(), [&](auto&& entry
) -> TextureClient
* {
1122 return entry
->get();
1125 RefPtr
<TextureClient
> textureClient
;
1126 RefPtr
<SourceSurface
> surface
= GetAsSourceSurface();
1127 MOZ_ASSERT(surface
);
1129 // gfx::BackendType::NONE means default to content backend
1130 textureClient
= TextureClient::CreateFromSurface(
1131 aKnowsCompositor
, surface
, BackendSelector::Content
,
1132 mTextureFlags
, ALLOC_DEFAULT
);
1134 if (textureClient
) {
1135 textureClient
->SyncWithObject(aKnowsCompositor
->GetSyncObject());
1136 return entry
.Insert(std::move(textureClient
)).get();
1143 ImageContainer::ProducerID
ImageContainer::AllocateProducerID() {
1144 // Callable on all threads.
1145 static Atomic
<ImageContainer::ProducerID
> sProducerID(0u);
1146 return ++sProducerID
;
1149 } // namespace mozilla::layers