Bug 1866777 - Disable test_race_cache_with_network.js on windows opt for frequent...
[gecko.git] / gfx / layers / ImageContainer.cpp
blob51bdcf7801ad6bff60fc39076199b1845fd13172
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
17 #include "libyuv.h"
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
37 #ifdef XP_MACOSX
38 # include "MacIOSurfaceImage.h"
39 #endif
41 #ifdef XP_WIN
42 # include <d3d10_1.h>
44 # include "gfxWindowsPlatform.h"
45 # include "mozilla/gfx/DeviceManagerDx.h"
46 # include "mozilla/layers/D3D11ShareHandleImage.h"
47 # include "mozilla/layers/D3D11YCbCrImage.h"
48 #endif
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,
61 int32_t aSkip);
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,
77 uint32_t aSize) {
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);
140 EnsureImageClient();
141 RefPtr<ImageClient> imageClient = mImageClient;
142 return imageClient.forget();
145 void ImageContainer::DropImageClient() {
146 RecursiveMutexAutoLock mon(mRecursiveMutex);
147 if (mImageClient) {
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.
157 if (!mIsAsync) {
158 return;
160 if (mImageClient &&
161 mImageClient->GetForwarder()->GetLayersIPCActor()->IPCOpen()) {
162 return;
165 RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
166 if (imageBridge) {
167 mImageClient =
168 imageBridge->CreateImageClient(CompositableType::IMAGE, this);
169 if (mImageClient) {
170 mAsyncContainerHandle = mImageClient->GetAsyncHandle();
171 } else {
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),
182 mPaintCount(0),
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);
191 EnsureImageClient();
195 ImageContainer::ImageContainer(const CompositableHandle& aHandle)
196 : mRecursiveMutex("ImageContainer.mRecursiveMutex"),
197 mGenerationCounter(++sGenerationCounter),
198 mPaintCount(0),
199 mDroppedImageCount(0),
200 mImageFactory(nullptr),
201 mRotation(VideoRotation::kDegree_0),
202 mRecycleBin(nullptr),
203 mIsAsync(true),
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());
239 break;
240 case layers::MemoryOrShmem::TShmem:
241 aOutBuffer = memOrShmem.get_Shmem().get<uint8_t>();
242 break;
243 default:
244 return NS_ERROR_OUT_OF_MEMORY;
247 MOZ_ASSERT(aOutBuffer);
248 return NS_OK;
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;
262 if (!tcOverride) {
263 tc = GetTextureClient(nullptr);
265 if (!tc) {
266 return {};
269 const auto& tcd = tc->GetInternalData();
271 SurfaceDescriptor ret;
272 if (!tcd->Serialize(ret)) {
273 return {};
275 return Some(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);
286 EnsureImageClient();
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);
298 EnsureImageClient();
299 if (mImageClient && mImageClient->AsImageClientSingle()) {
300 return new SharedRGBImage(mImageClient);
302 if (mRecycleAllocator) {
303 return new SharedRGBImage(mRecycleAllocator);
305 return nullptr;
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");
330 if (i > 0) {
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;
347 break;
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);
364 if (mIsAsync) {
365 if (RefPtr<ImageBridgeChild> imageBridge =
366 ImageBridgeChild::GetSingleton()) {
367 imageBridge->UpdateImageClient(this);
370 SetCurrentImageInternal(aImages);
373 void ImageContainer::ClearAllImages() {
374 mRecursiveMutex.Lock();
375 if (mImageClient) {
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);
385 return;
388 SetCurrentImageInternal(nsTArray<NonOwningImage>());
389 mRecursiveMutex.Unlock();
392 void ImageContainer::ClearCachedResources() {
393 RecursiveMutexAutoLock lock(mRecursiveMutex);
394 if (mImageClient && mImageClient->AsImageClientSingle()) {
395 if (!mImageClient->HasTextureClientRecycler()) {
396 return;
398 mImageClient->GetTextureClientRecycler()->ShrinkToMinimumSize();
399 return;
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");
426 EnsureImageClient();
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.
463 ++mPaintCount;
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()) {
493 return;
496 if (!StaticPrefs::layers_recycle_allocator_rdd_AtStartup()) {
497 return;
500 static const uint32_t MAX_POOLED_VIDEO_COUNT = 5;
502 mRecycleAllocator =
503 new layers::TextureClientRecycleAllocator(aKnowsCompositor);
504 mRecycleAllocator->SetMaxPoolSize(MAX_POOLED_VIDEO_COUNT);
507 #ifdef XP_WIN
508 already_AddRefed<D3D11RecycleAllocator>
509 ImageContainer::GetD3D11RecycleAllocator(KnowsCompositor* aKnowsCompositor,
510 gfx::SurfaceFormat aPreferredFormat) {
511 MOZ_ASSERT(aKnowsCompositor);
513 if (!aKnowsCompositor->SupportsD3D11()) {
514 return nullptr;
517 RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetImageDevice();
518 if (!device) {
519 return nullptr;
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,
535 device);
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()) {
547 return nullptr;
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);
560 #endif
562 #ifdef XP_MACOSX
563 already_AddRefed<MacIOSurfaceRecycleAllocator>
564 ImageContainer::GetMacIOSurfaceRecycleAllocator() {
565 RecursiveMutexAutoLock lock(mRecursiveMutex);
566 if (!mMacIOSurfaceRecycleAllocator) {
567 mMacIOSurfaceRecycleAllocator = new MacIOSurfaceRecycleAllocator();
570 return do_AddRef(mMacIOSurfaceRecycleAllocator);
572 #endif
574 // -
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) {
580 return {};
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.";
590 break;
591 case MemoryOrShmem::TShmem:
592 buffer.emplace(memOrShmem.get_Shmem().Range<uint8_t>());
593 break;
594 default:
595 MOZ_ASSERT(false, "Unknown MemoryOrShmem type");
596 break;
598 if (!buffer) {
599 return {};
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()) {
622 gfxCriticalError()
623 << "PlanarYCbCrData::From asked for out-of-bounds plane data.";
624 return nullptr;
626 return (buffer->begin() + beginOffset).get();
628 yuvData.mYChannel =
629 GetPlanePtr(yuvDesc.yOffset(), yuvDesc.ySize(), yuvData.mYStride);
630 yuvData.mCbChannel =
631 GetPlanePtr(yuvDesc.cbOffset(), yuvDesc.cbCrSize(), yuvData.mCbCrStride);
632 yuvData.mCrChannel =
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;
648 return {};
651 return Some(yuvData);
654 // -
656 PlanarYCbCrImage::PlanarYCbCrImage()
657 : Image(nullptr, ImageFormat::PLANAR_YCBCR),
658 mOffscreenFormat(SurfaceFormat::UNKNOWN),
659 mBufferSize(0) {}
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;
675 int32_t stride = 0;
676 nsresult rv = AllocateSurfaceDescriptorBufferRgb(
677 size, format, buffer, aSdBuffer, stride, aAllocate);
678 if (NS_WARN_IF(NS_FAILED(rv))) {
679 return 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)) {
689 return NS_OK;
693 gfx::ConvertYCbCrToRGB(mData, format, size, buffer, stride);
694 return NS_OK;
697 auto ySize = pdata->YDataSize();
698 auto cbcrSize = pdata->CbCrDataSize();
699 uint32_t yOffset;
700 uint32_t cbOffset;
701 uint32_t crOffset;
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,
708 crOffset);
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());
717 break;
718 case MemoryOrShmem::TShmem:
719 buffer = memOrShmem.get_Shmem().get<uint8_t>();
720 break;
721 default:
722 buffer = nullptr;
723 break;
725 if (!buffer) {
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,
735 pdata->mYSkip);
736 CopyPlane(buffer + cbOffset, pdata->mCbChannel, cbcrSize, pdata->mCbCrStride,
737 pdata->mCbSkip);
738 CopyPlane(buffer + crOffset, pdata->mCrChannel, cbcrSize, pdata->mCbCrStride,
739 pdata->mCrSkip);
740 return NS_OK;
743 RecyclingPlanarYCbCrImage::~RecyclingPlanarYCbCrImage() {
744 if (mBuffer) {
745 mRecycleBin->RecycleBuffer(std::move(mBuffer), mBufferSize);
749 size_t RecyclingPlanarYCbCrImage::SizeOfExcludingThis(
750 MallocSizeOf aMallocSizeOf) const {
751 // Ignoring:
752 // - mData - just wraps mBuffer
753 // - Surfaces should be reported under gfx-surfaces-*:
754 // - mSourceSurface
755 // - Base class:
756 // - mImplData is not used
757 // Not owned:
758 // - mRecycleBin
759 size_t size = aMallocSizeOf(mBuffer.get());
761 // Could add in the future:
762 // - mBackendData (from base class)
764 return size;
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,
773 int32_t aSkip) {
774 int32_t height = aSize.height;
775 int32_t width = aSize.width;
777 MOZ_RELEASE_ASSERT(width <= aStride);
779 if (!aSkip) {
780 // Fast path: planar input.
781 memcpy(aDst, aSrc, height * aStride);
782 } else {
783 for (int y = 0; y < height; ++y) {
784 const uint8_t* src = aSrc;
785 uint8_t* dst = aDst;
786 // Slow path
787 for (int x = 0; x < width; ++x) {
788 *dst++ = *src++;
789 src += aSkip;
791 aSrc += aStride;
792 aDst += aStride;
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();
811 // get new buffer
812 mBuffer = AllocateBuffer(size);
813 if (!mBuffer) return false;
815 // update buffer size
816 mBufferSize = 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,
825 aData.mYSkip);
826 CopyPlane(mData.mCbChannel, aData.mCbChannel, cbcrSize, aData.mCbCrStride,
827 aData.mCbSkip);
828 CopyPlane(mData.mCrChannel, aData.mCrChannel, cbcrSize, aData.mCbCrStride,
829 aData.mCrSkip);
830 if (aData.mAlpha) {
831 CopyPlane(mData.mAlpha->mChannel, aData.mAlpha->mChannel, ySize,
832 aData.mYStride, aData.mYSkip);
835 mSize = aData.mPictureRect.Size();
836 mOrigin = aData.mPictureRect.TopLeft();
837 return true;
840 gfxImageFormat PlanarYCbCrImage::GetOffscreenFormat() const {
841 return mOffscreenFormat == SurfaceFormat::UNKNOWN ? gfxVars::OffscreenFormat()
842 : mOffscreenFormat;
845 bool PlanarYCbCrImage::AdoptData(const Data& aData) {
846 mData = aData;
847 mSize = aData.mPictureRect.Size();
848 mOrigin = aData.mPictureRect.TopLeft();
849 return true;
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");
865 return nullptr;
868 RefPtr<gfx::DataSourceSurface> surface =
869 gfx::Factory::CreateDataSourceSurface(size, format);
870 if (NS_WARN_IF(!surface)) {
871 return nullptr;
874 DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
875 if (NS_WARN_IF(!mapping.IsMapped())) {
876 return nullptr;
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]);
916 Data aData = mData;
917 aData.mCbCrStride = cbcrSize.width;
918 aData.mCbSkip = 0;
919 aData.mCrSkip = 0;
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);
929 } else { // NV21
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");
944 return nullptr;
947 RefPtr<gfx::DataSourceSurface> surface =
948 gfx::Factory::CreateDataSourceSurface(size, format);
949 if (NS_WARN_IF(!surface)) {
950 return nullptr;
953 DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
954 if (NS_WARN_IF(!mapping.IsMapped())) {
955 return nullptr;
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();
974 Data aData = mData;
975 aData.mCbCrStride = cbcrSize.width;
976 aData.mCbSkip = 0;
977 aData.mCrSkip = 0;
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);
994 } else { // NV21
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;
1018 int32_t stride = 0;
1019 nsresult rv = AllocateSurfaceDescriptorBufferRgb(
1020 size, format, output, aSdBuffer, stride, aAllocate);
1021 if (NS_WARN_IF(NS_FAILED(rv))) {
1022 return rv;
1025 if (!mSourceSurface) {
1026 gfx::ConvertYCbCrToRGB(aData, format, size, output, stride);
1027 return NS_OK;
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;
1040 return NS_OK;
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);
1065 if (!mBuffer) {
1066 return false;
1069 // Update mBufferSize.
1070 mBufferSize = size;
1072 // Update mData.
1073 mData = aData;
1074 mData.mYChannel = mBuffer.get();
1075 mData.mCbChannel = mData.mYChannel + (aData.mCbChannel - aData.mYChannel);
1076 mData.mCrChannel = mData.mYChannel + (aData.mCrChannel - aData.mYChannel);
1078 // Update mSize.
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);
1085 return true;
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]);
1092 return buffer;
1095 SourceSurfaceImage::SourceSurfaceImage(const gfx::IntSize& aSize,
1096 gfx::SourceSurface* aSourceSurface)
1097 : Image(nullptr, ImageFormat::MOZ2D_SURFACE),
1098 mSize(aSize),
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) {
1116 return nullptr;
1119 return mTextureClients.WithEntryHandle(
1120 aKnowsCompositor->GetSerial(), [&](auto&& entry) -> TextureClient* {
1121 if (entry) {
1122 return entry->get();
1125 RefPtr<TextureClient> textureClient;
1126 RefPtr<SourceSurface> surface = GetAsSourceSurface();
1127 MOZ_ASSERT(surface);
1128 if (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();
1139 return nullptr;
1143 ImageContainer::ProducerID ImageContainer::AllocateProducerID() {
1144 // Callable on all threads.
1145 static Atomic<ImageContainer::ProducerID> sProducerID(0u);
1146 return ++sProducerID;
1149 } // namespace mozilla::layers