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/. */
6 
7 #include "ImageContainer.h"
8 #include <string.h>    // for memcpy, memset
9 #include "GLImages.h"  // for SurfaceTextureImage
10 #include "gfx2DGlue.h"
11 #include "gfxPlatform.h"  // for gfxPlatform
12 #include "gfxUtils.h"     // for gfxUtils
13 #include "libyuv.h"
14 #include "mozilla/RefPtr.h"                 // for already_AddRefed
15 #include "mozilla/ipc/CrossProcessMutex.h"  // for CrossProcessMutex, etc
16 #include "mozilla/layers/CompositorTypes.h"
17 #include "mozilla/layers/ImageBridgeChild.h"     // for ImageBridgeChild
18 #include "mozilla/layers/ImageClient.h"          // for ImageClient
19 #include "mozilla/layers/ImageDataSerializer.h"  // for SurfaceDescriptorBuffer
20 #include "mozilla/layers/LayersMessages.h"
21 #include "mozilla/layers/SharedPlanarYCbCrImage.h"
22 #include "mozilla/layers/SharedSurfacesChild.h"  // for SharedSurfacesAnimation
23 #include "mozilla/layers/SharedRGBImage.h"
24 #include "mozilla/layers/TextureClientRecycleAllocator.h"
25 #include "mozilla/StaticPrefs_layers.h"
26 #include "mozilla/gfx/gfxVars.h"
27 #include "nsISupportsUtils.h"  // for NS_IF_ADDREF
28 #include "YCbCrUtils.h"        // for YCbCr conversions
29 #include "gfx2DGlue.h"
30 #include "mozilla/gfx/2D.h"
31 #include "mozilla/CheckedInt.h"
32 
33 #ifdef XP_MACOSX
34 #  include "mozilla/gfx/QuartzSupport.h"
35 #endif
36 
37 #ifdef XP_WIN
38 #  include "gfxWindowsPlatform.h"
39 #  include <d3d10_1.h>
40 #  include "mozilla/gfx/DeviceManagerDx.h"
41 #  include "mozilla/layers/D3D11YCbCrImage.h"
42 #endif
43 
44 namespace mozilla::layers {
45 
46 using namespace mozilla::gfx;
47 using namespace mozilla::ipc;
48 
49 Atomic<int32_t> Image::sSerialCounter(0);
50 
51 Atomic<uint32_t> ImageContainer::sGenerationCounter(0);
52 
53 static void CopyPlane(uint8_t* aDst, const uint8_t* aSrc,
54                       const gfx::IntSize& aSize, int32_t aStride,
55                       int32_t aSkip);
56 
CreatePlanarYCbCrImage(const gfx::IntSize & aScaleHint,BufferRecycleBin * aRecycleBin)57 RefPtr<PlanarYCbCrImage> ImageFactory::CreatePlanarYCbCrImage(
58     const gfx::IntSize& aScaleHint, BufferRecycleBin* aRecycleBin) {
59   return new RecyclingPlanarYCbCrImage(aRecycleBin);
60 }
61 
BufferRecycleBin()62 BufferRecycleBin::BufferRecycleBin()
63     : mLock("mozilla.layers.BufferRecycleBin.mLock")
64       // This member is only valid when the bin is not empty and will be
65       // properly initialized in RecycleBuffer, but initializing it here avoids
66       // static analysis noise.
67       ,
68       mRecycledBufferSize(0) {}
69 
RecycleBuffer(UniquePtr<uint8_t[]> aBuffer,uint32_t aSize)70 void BufferRecycleBin::RecycleBuffer(UniquePtr<uint8_t[]> aBuffer,
71                                      uint32_t aSize) {
72   MutexAutoLock lock(mLock);
73 
74   if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) {
75     mRecycledBuffers.Clear();
76   }
77   mRecycledBufferSize = aSize;
78   mRecycledBuffers.AppendElement(std::move(aBuffer));
79 }
80 
GetBuffer(uint32_t aSize)81 UniquePtr<uint8_t[]> BufferRecycleBin::GetBuffer(uint32_t aSize) {
82   MutexAutoLock lock(mLock);
83 
84   if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize) {
85     return UniquePtr<uint8_t[]>(new (fallible) uint8_t[aSize]);
86   }
87 
88   uint32_t last = mRecycledBuffers.Length() - 1;
89   UniquePtr<uint8_t[]> result = std::move(mRecycledBuffers[last]);
90   mRecycledBuffers.RemoveElementAt(last);
91   return result;
92 }
93 
ClearRecycledBuffers()94 void BufferRecycleBin::ClearRecycledBuffers() {
95   MutexAutoLock lock(mLock);
96   if (!mRecycledBuffers.IsEmpty()) {
97     mRecycledBuffers.Clear();
98   }
99   mRecycledBufferSize = 0;
100 }
101 
ImageContainerListener(ImageContainer * aImageContainer)102 ImageContainerListener::ImageContainerListener(ImageContainer* aImageContainer)
103     : mLock("mozilla.layers.ImageContainerListener.mLock"),
104       mImageContainer(aImageContainer) {}
105 
106 ImageContainerListener::~ImageContainerListener() = default;
107 
NotifyComposite(const ImageCompositeNotification & aNotification)108 void ImageContainerListener::NotifyComposite(
109     const ImageCompositeNotification& aNotification) {
110   MutexAutoLock lock(mLock);
111   if (mImageContainer) {
112     mImageContainer->NotifyComposite(aNotification);
113   }
114 }
115 
NotifyDropped(uint32_t aDropped)116 void ImageContainerListener::NotifyDropped(uint32_t aDropped) {
117   MutexAutoLock lock(mLock);
118   if (mImageContainer) {
119     mImageContainer->NotifyDropped(aDropped);
120   }
121 }
122 
ClearImageContainer()123 void ImageContainerListener::ClearImageContainer() {
124   MutexAutoLock lock(mLock);
125   mImageContainer = nullptr;
126 }
127 
DropImageClient()128 void ImageContainerListener::DropImageClient() {
129   MutexAutoLock lock(mLock);
130   if (mImageContainer) {
131     mImageContainer->DropImageClient();
132   }
133 }
134 
GetImageClient()135 already_AddRefed<ImageClient> ImageContainer::GetImageClient() {
136   RecursiveMutexAutoLock mon(mRecursiveMutex);
137   EnsureImageClient();
138   RefPtr<ImageClient> imageClient = mImageClient;
139   return imageClient.forget();
140 }
141 
DropImageClient()142 void ImageContainer::DropImageClient() {
143   RecursiveMutexAutoLock mon(mRecursiveMutex);
144   if (mImageClient) {
145     mImageClient->ClearCachedResources();
146     mImageClient = nullptr;
147   }
148 }
149 
EnsureImageClient()150 void ImageContainer::EnsureImageClient() {
151   // If we're not forcing a new ImageClient, then we can skip this if we don't
152   // have an existing ImageClient, or if the existing one belongs to an IPC
153   // actor that is still open.
154   if (!mIsAsync) {
155     return;
156   }
157   if (mImageClient &&
158       mImageClient->GetForwarder()->GetLayersIPCActor()->IPCOpen()) {
159     return;
160   }
161 
162   RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
163   if (imageBridge) {
164     mImageClient =
165         imageBridge->CreateImageClient(CompositableType::IMAGE, this);
166     if (mImageClient) {
167       mAsyncContainerHandle = mImageClient->GetAsyncHandle();
168     } else {
169       // It's okay to drop the async container handle since the ImageBridgeChild
170       // is going to die anyway.
171       mAsyncContainerHandle = CompositableHandle();
172     }
173   }
174 }
175 
EnsureSharedSurfacesAnimation()176 SharedSurfacesAnimation* ImageContainer::EnsureSharedSurfacesAnimation() {
177   if (!mSharedAnimation) {
178     mSharedAnimation = new SharedSurfacesAnimation();
179   }
180   return mSharedAnimation;
181 }
182 
ImageContainer(Mode flag)183 ImageContainer::ImageContainer(Mode flag)
184     : mRecursiveMutex("ImageContainer.mRecursiveMutex"),
185       mGenerationCounter(++sGenerationCounter),
186       mPaintCount(0),
187       mDroppedImageCount(0),
188       mImageFactory(new ImageFactory()),
189       mRecycleBin(new BufferRecycleBin()),
190       mIsAsync(flag == ASYNCHRONOUS),
191       mCurrentProducerID(-1) {
192   if (flag == ASYNCHRONOUS) {
193     mNotifyCompositeListener = new ImageContainerListener(this);
194     EnsureImageClient();
195   }
196 }
197 
ImageContainer(const CompositableHandle & aHandle)198 ImageContainer::ImageContainer(const CompositableHandle& aHandle)
199     : mRecursiveMutex("ImageContainer.mRecursiveMutex"),
200       mGenerationCounter(++sGenerationCounter),
201       mPaintCount(0),
202       mDroppedImageCount(0),
203       mImageFactory(nullptr),
204       mRecycleBin(nullptr),
205       mIsAsync(true),
206       mAsyncContainerHandle(aHandle),
207       mCurrentProducerID(-1) {
208   MOZ_ASSERT(mAsyncContainerHandle);
209 }
210 
~ImageContainer()211 ImageContainer::~ImageContainer() {
212   if (mNotifyCompositeListener) {
213     mNotifyCompositeListener->ClearImageContainer();
214   }
215   if (mAsyncContainerHandle) {
216     if (RefPtr<ImageBridgeChild> imageBridge =
217             ImageBridgeChild::GetSingleton()) {
218       imageBridge->ForgetImageContainer(mAsyncContainerHandle);
219     }
220   }
221   if (mSharedAnimation) {
222     mSharedAnimation->Destroy();
223   }
224 }
225 
CreatePlanarYCbCrImage()226 RefPtr<PlanarYCbCrImage> ImageContainer::CreatePlanarYCbCrImage() {
227   RecursiveMutexAutoLock lock(mRecursiveMutex);
228   EnsureImageClient();
229   if (mImageClient && mImageClient->AsImageClientSingle()) {
230     return new SharedPlanarYCbCrImage(mImageClient);
231   }
232   if (mRecycleAllocator) {
233     return new SharedPlanarYCbCrImage(mRecycleAllocator);
234   }
235   return mImageFactory->CreatePlanarYCbCrImage(mScaleHint, mRecycleBin);
236 }
237 
CreateSharedRGBImage()238 RefPtr<SharedRGBImage> ImageContainer::CreateSharedRGBImage() {
239   RecursiveMutexAutoLock lock(mRecursiveMutex);
240   EnsureImageClient();
241   if (!mImageClient || !mImageClient->AsImageClientSingle()) {
242     return nullptr;
243   }
244   return new SharedRGBImage(mImageClient);
245 }
246 
SetCurrentImageInternal(const nsTArray<NonOwningImage> & aImages)247 void ImageContainer::SetCurrentImageInternal(
248     const nsTArray<NonOwningImage>& aImages) {
249   RecursiveMutexAutoLock lock(mRecursiveMutex);
250 
251   mGenerationCounter = ++sGenerationCounter;
252 
253   if (!aImages.IsEmpty()) {
254     NS_ASSERTION(mCurrentImages.IsEmpty() ||
255                      mCurrentImages[0].mProducerID != aImages[0].mProducerID ||
256                      mCurrentImages[0].mFrameID <= aImages[0].mFrameID,
257                  "frame IDs shouldn't go backwards");
258     if (aImages[0].mProducerID != mCurrentProducerID) {
259       mCurrentProducerID = aImages[0].mProducerID;
260     }
261   }
262 
263   nsTArray<OwningImage> newImages;
264 
265   for (uint32_t i = 0; i < aImages.Length(); ++i) {
266     NS_ASSERTION(aImages[i].mImage, "image can't be null");
267     NS_ASSERTION(!aImages[i].mTimeStamp.IsNull() || aImages.Length() == 1,
268                  "Multiple images require timestamps");
269     if (i > 0) {
270       NS_ASSERTION(aImages[i].mTimeStamp >= aImages[i - 1].mTimeStamp,
271                    "Timestamps must not decrease");
272       NS_ASSERTION(aImages[i].mFrameID > aImages[i - 1].mFrameID,
273                    "FrameIDs must increase");
274       NS_ASSERTION(aImages[i].mProducerID == aImages[i - 1].mProducerID,
275                    "ProducerIDs must be the same");
276     }
277     OwningImage* img = newImages.AppendElement();
278     img->mImage = aImages[i].mImage;
279     img->mTimeStamp = aImages[i].mTimeStamp;
280     img->mFrameID = aImages[i].mFrameID;
281     img->mProducerID = aImages[i].mProducerID;
282     for (const auto& oldImg : mCurrentImages) {
283       if (oldImg.mFrameID == img->mFrameID &&
284           oldImg.mProducerID == img->mProducerID) {
285         img->mComposited = oldImg.mComposited;
286         break;
287       }
288     }
289   }
290 
291   mCurrentImages.SwapElements(newImages);
292 }
293 
ClearImagesFromImageBridge()294 void ImageContainer::ClearImagesFromImageBridge() {
295   RecursiveMutexAutoLock lock(mRecursiveMutex);
296   SetCurrentImageInternal(nsTArray<NonOwningImage>());
297 }
298 
SetCurrentImages(const nsTArray<NonOwningImage> & aImages)299 void ImageContainer::SetCurrentImages(const nsTArray<NonOwningImage>& aImages) {
300   AUTO_PROFILER_LABEL("ImageContainer::SetCurrentImages", GRAPHICS);
301   MOZ_ASSERT(!aImages.IsEmpty());
302   RecursiveMutexAutoLock lock(mRecursiveMutex);
303   if (mIsAsync) {
304     if (RefPtr<ImageBridgeChild> imageBridge =
305             ImageBridgeChild::GetSingleton()) {
306       imageBridge->UpdateImageClient(this);
307     }
308   }
309   SetCurrentImageInternal(aImages);
310 }
311 
ClearAllImages()312 void ImageContainer::ClearAllImages() {
313   if (mImageClient) {
314     // Let ImageClient release all TextureClients. This doesn't return
315     // until ImageBridge has called ClearCurrentImageFromImageBridge.
316     if (RefPtr<ImageBridgeChild> imageBridge =
317             ImageBridgeChild::GetSingleton()) {
318       imageBridge->FlushAllImages(mImageClient, this);
319     }
320     return;
321   }
322 
323   RecursiveMutexAutoLock lock(mRecursiveMutex);
324   SetCurrentImageInternal(nsTArray<NonOwningImage>());
325 }
326 
ClearCachedResources()327 void ImageContainer::ClearCachedResources() {
328   RecursiveMutexAutoLock lock(mRecursiveMutex);
329   if (mImageClient && mImageClient->AsImageClientSingle()) {
330     if (!mImageClient->HasTextureClientRecycler()) {
331       return;
332     }
333     mImageClient->GetTextureClientRecycler()->ShrinkToMinimumSize();
334     return;
335   }
336   return mRecycleBin->ClearRecycledBuffers();
337 }
338 
SetCurrentImageInTransaction(Image * aImage)339 void ImageContainer::SetCurrentImageInTransaction(Image* aImage) {
340   AutoTArray<NonOwningImage, 1> images;
341   images.AppendElement(NonOwningImage(aImage));
342   SetCurrentImagesInTransaction(images);
343 }
344 
SetCurrentImagesInTransaction(const nsTArray<NonOwningImage> & aImages)345 void ImageContainer::SetCurrentImagesInTransaction(
346     const nsTArray<NonOwningImage>& aImages) {
347   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
348   NS_ASSERTION(!mImageClient,
349                "Should use async image transfer with ImageBridge.");
350 
351   SetCurrentImageInternal(aImages);
352 }
353 
IsAsync() const354 bool ImageContainer::IsAsync() const { return mIsAsync; }
355 
GetAsyncContainerHandle()356 CompositableHandle ImageContainer::GetAsyncContainerHandle() {
357   NS_ASSERTION(IsAsync(),
358                "Shared image ID is only relevant to async ImageContainers");
359   NS_ASSERTION(mAsyncContainerHandle, "Should have a shared image ID");
360   RecursiveMutexAutoLock mon(mRecursiveMutex);
361   EnsureImageClient();
362   return mAsyncContainerHandle;
363 }
364 
HasCurrentImage()365 bool ImageContainer::HasCurrentImage() {
366   RecursiveMutexAutoLock lock(mRecursiveMutex);
367 
368   return !mCurrentImages.IsEmpty();
369 }
370 
GetCurrentImages(nsTArray<OwningImage> * aImages,uint32_t * aGenerationCounter)371 void ImageContainer::GetCurrentImages(nsTArray<OwningImage>* aImages,
372                                       uint32_t* aGenerationCounter) {
373   RecursiveMutexAutoLock lock(mRecursiveMutex);
374 
375   *aImages = mCurrentImages.Clone();
376   if (aGenerationCounter) {
377     *aGenerationCounter = mGenerationCounter;
378   }
379 }
380 
GetCurrentSize()381 gfx::IntSize ImageContainer::GetCurrentSize() {
382   RecursiveMutexAutoLock lock(mRecursiveMutex);
383 
384   if (mCurrentImages.IsEmpty()) {
385     return gfx::IntSize(0, 0);
386   }
387 
388   return mCurrentImages[0].mImage->GetSize();
389 }
390 
NotifyComposite(const ImageCompositeNotification & aNotification)391 void ImageContainer::NotifyComposite(
392     const ImageCompositeNotification& aNotification) {
393   RecursiveMutexAutoLock lock(mRecursiveMutex);
394 
395   // An image composition notification is sent the first time a particular
396   // image is composited by an ImageHost. Thus, every time we receive such
397   // a notification, a new image has been painted.
398   ++mPaintCount;
399 
400   if (aNotification.producerID() == mCurrentProducerID) {
401     for (auto& img : mCurrentImages) {
402       if (img.mFrameID == aNotification.frameID()) {
403         img.mComposited = true;
404       }
405     }
406   }
407 
408   if (!aNotification.imageTimeStamp().IsNull()) {
409     mPaintDelay = aNotification.firstCompositeTimeStamp() -
410                   aNotification.imageTimeStamp();
411   }
412 }
413 
NotifyDropped(uint32_t aDropped)414 void ImageContainer::NotifyDropped(uint32_t aDropped) {
415   mDroppedImageCount += aDropped;
416 }
417 
EnsureRecycleAllocatorForRDD(KnowsCompositor * aKnowsCompositor)418 void ImageContainer::EnsureRecycleAllocatorForRDD(
419     KnowsCompositor* aKnowsCompositor) {
420   MOZ_ASSERT(!mIsAsync);
421   MOZ_ASSERT(!mImageClient);
422   MOZ_ASSERT(XRE_IsRDDProcess());
423 
424   if (mRecycleAllocator &&
425       aKnowsCompositor == mRecycleAllocator->GetKnowsCompositor()) {
426     return;
427   }
428 
429   bool useRecycleAllocator =
430       StaticPrefs::layers_recycle_allocator_rdd_AtStartup();
431 #ifdef XP_MACOSX
432   // Disable RecycleAllocator for RDD on MacOS without WebRender.
433   // Recycling caused rendering artifact on a MacOS PC with OpenGL compositor.
434   if (!gfxVars::UseWebRender()) {
435     useRecycleAllocator = false;
436   }
437 #endif
438   if (!useRecycleAllocator) {
439     return;
440   }
441 
442   static const uint32_t MAX_POOLED_VIDEO_COUNT = 5;
443 
444   mRecycleAllocator =
445       new layers::TextureClientRecycleAllocator(aKnowsCompositor);
446   mRecycleAllocator->SetMaxPoolSize(MAX_POOLED_VIDEO_COUNT);
447 }
448 
449 #ifdef XP_WIN
GetD3D11YCbCrRecycleAllocator(KnowsCompositor * aKnowsCompositor)450 D3D11YCbCrRecycleAllocator* ImageContainer::GetD3D11YCbCrRecycleAllocator(
451     KnowsCompositor* aKnowsCompositor) {
452   if (mD3D11YCbCrRecycleAllocator &&
453       aKnowsCompositor == mD3D11YCbCrRecycleAllocator->GetKnowsCompositor()) {
454     return mD3D11YCbCrRecycleAllocator;
455   }
456 
457   if (!aKnowsCompositor->SupportsD3D11() ||
458       !gfx::DeviceManagerDx::Get()->GetImageDevice()) {
459     return nullptr;
460   }
461 
462   mD3D11YCbCrRecycleAllocator =
463       new D3D11YCbCrRecycleAllocator(aKnowsCompositor);
464   return mD3D11YCbCrRecycleAllocator;
465 }
466 #endif
467 
PlanarYCbCrImage()468 PlanarYCbCrImage::PlanarYCbCrImage()
469     : Image(nullptr, ImageFormat::PLANAR_YCBCR),
470       mOffscreenFormat(SurfaceFormat::UNKNOWN),
471       mBufferSize(0) {}
472 
BuildSurfaceDescriptorBuffer(SurfaceDescriptorBuffer & aSdBuffer)473 nsresult PlanarYCbCrImage::BuildSurfaceDescriptorBuffer(
474     SurfaceDescriptorBuffer& aSdBuffer) {
475   const PlanarYCbCrData* pdata = GetData();
476   MOZ_ASSERT(pdata, "must have PlanarYCbCrData");
477   MOZ_ASSERT(pdata->mYSkip == 0 && pdata->mCbSkip == 0 && pdata->mCrSkip == 0,
478              "YCbCrDescriptor doesn't hold skip values");
479   MOZ_ASSERT(pdata->mPicX == 0 && pdata->mPicY == 0,
480              "YCbCrDescriptor doesn't hold picx or picy");
481 
482   uint32_t yOffset;
483   uint32_t cbOffset;
484   uint32_t crOffset;
485   ImageDataSerializer::ComputeYCbCrOffsets(
486       pdata->mYStride, pdata->mYSize.height, pdata->mCbCrStride,
487       pdata->mCbCrSize.height, yOffset, cbOffset, crOffset);
488 
489   aSdBuffer.desc() = YCbCrDescriptor(
490       pdata->mYSize, pdata->mYStride, pdata->mCbCrSize, pdata->mCbCrStride,
491       yOffset, cbOffset, crOffset, pdata->mStereoMode, pdata->mColorDepth,
492       pdata->mYUVColorSpace, pdata->mColorRange,
493       /*hasIntermediateBuffer*/ false);
494 
495   uint8_t* buffer = nullptr;
496   const MemoryOrShmem& memOrShmem = aSdBuffer.data();
497   switch (memOrShmem.type()) {
498     case MemoryOrShmem::Tuintptr_t:
499       buffer = reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t());
500       break;
501     case MemoryOrShmem::TShmem:
502       buffer = memOrShmem.get_Shmem().get<uint8_t>();
503       break;
504     default:
505       MOZ_ASSERT(false, "Unknown MemoryOrShmem type");
506   }
507   MOZ_ASSERT(buffer, "no valid buffer available to copy image data");
508   if (!buffer) {
509     return NS_ERROR_INVALID_ARG;
510   }
511 
512   CopyPlane(buffer + yOffset, pdata->mYChannel, pdata->mYSize, pdata->mYStride,
513             pdata->mYSkip);
514   CopyPlane(buffer + cbOffset, pdata->mCbChannel, pdata->mCbCrSize,
515             pdata->mCbCrStride, pdata->mCbSkip);
516   CopyPlane(buffer + crOffset, pdata->mCrChannel, pdata->mCbCrSize,
517             pdata->mCbCrStride, pdata->mCrSkip);
518   return NS_OK;
519 }
520 
~RecyclingPlanarYCbCrImage()521 RecyclingPlanarYCbCrImage::~RecyclingPlanarYCbCrImage() {
522   if (mBuffer) {
523     mRecycleBin->RecycleBuffer(std::move(mBuffer), mBufferSize);
524   }
525 }
526 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const527 size_t RecyclingPlanarYCbCrImage::SizeOfExcludingThis(
528     MallocSizeOf aMallocSizeOf) const {
529   // Ignoring:
530   // - mData - just wraps mBuffer
531   // - Surfaces should be reported under gfx-surfaces-*:
532   //   - mSourceSurface
533   // - Base class:
534   //   - mImplData is not used
535   // Not owned:
536   // - mRecycleBin
537   size_t size = aMallocSizeOf(mBuffer.get());
538 
539   // Could add in the future:
540   // - mBackendData (from base class)
541 
542   return size;
543 }
544 
AllocateBuffer(uint32_t aSize)545 UniquePtr<uint8_t[]> RecyclingPlanarYCbCrImage::AllocateBuffer(uint32_t aSize) {
546   return mRecycleBin->GetBuffer(aSize);
547 }
548 
CopyPlane(uint8_t * aDst,const uint8_t * aSrc,const gfx::IntSize & aSize,int32_t aStride,int32_t aSkip)549 static void CopyPlane(uint8_t* aDst, const uint8_t* aSrc,
550                       const gfx::IntSize& aSize, int32_t aStride,
551                       int32_t aSkip) {
552   int32_t height = aSize.height;
553   int32_t width = aSize.width;
554 
555   MOZ_RELEASE_ASSERT(width <= aStride);
556 
557   if (!aSkip) {
558     // Fast path: planar input.
559     memcpy(aDst, aSrc, height * aStride);
560   } else {
561     for (int y = 0; y < height; ++y) {
562       const uint8_t* src = aSrc;
563       uint8_t* dst = aDst;
564       // Slow path
565       for (int x = 0; x < width; ++x) {
566         *dst++ = *src++;
567         src += aSkip;
568       }
569       aSrc += aStride;
570       aDst += aStride;
571     }
572   }
573 }
574 
CopyData(const Data & aData)575 bool RecyclingPlanarYCbCrImage::CopyData(const Data& aData) {
576   // update buffer size
577   // Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
578   const auto checkedSize =
579       CheckedInt<uint32_t>(aData.mCbCrStride) * aData.mCbCrSize.height * 2 +
580       CheckedInt<uint32_t>(aData.mYStride) * aData.mYSize.height;
581 
582   if (!checkedSize.isValid()) return false;
583 
584   const auto size = checkedSize.value();
585 
586   // get new buffer
587   mBuffer = AllocateBuffer(size);
588   if (!mBuffer) return false;
589 
590   // update buffer size
591   mBufferSize = size;
592 
593   mData = aData;
594   mData.mYChannel = mBuffer.get();
595   mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
596   mData.mCrChannel =
597       mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
598   mData.mYSkip = mData.mCbSkip = mData.mCrSkip = 0;
599 
600   CopyPlane(mData.mYChannel, aData.mYChannel, aData.mYSize, aData.mYStride,
601             aData.mYSkip);
602   CopyPlane(mData.mCbChannel, aData.mCbChannel, aData.mCbCrSize,
603             aData.mCbCrStride, aData.mCbSkip);
604   CopyPlane(mData.mCrChannel, aData.mCrChannel, aData.mCbCrSize,
605             aData.mCbCrStride, aData.mCrSkip);
606 
607   mSize = aData.mPicSize;
608   mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
609   return true;
610 }
611 
GetOffscreenFormat() const612 gfxImageFormat PlanarYCbCrImage::GetOffscreenFormat() const {
613   return mOffscreenFormat == SurfaceFormat::UNKNOWN ? gfxVars::OffscreenFormat()
614                                                     : mOffscreenFormat;
615 }
616 
AdoptData(const Data & aData)617 bool PlanarYCbCrImage::AdoptData(const Data& aData) {
618   mData = aData;
619   mSize = aData.mPicSize;
620   mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
621   return true;
622 }
623 
GetAsSourceSurface()624 already_AddRefed<gfx::SourceSurface> PlanarYCbCrImage::GetAsSourceSurface() {
625   if (mSourceSurface) {
626     RefPtr<gfx::SourceSurface> surface(mSourceSurface);
627     return surface.forget();
628   }
629 
630   gfx::IntSize size(mSize);
631   gfx::SurfaceFormat format =
632       gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());
633   gfx::GetYCbCrToRGBDestFormatAndSize(mData, format, size);
634   if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
635       mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
636     NS_ERROR("Illegal image dest width or height");
637     return nullptr;
638   }
639 
640   RefPtr<gfx::DataSourceSurface> surface =
641       gfx::Factory::CreateDataSourceSurface(size, format);
642   if (NS_WARN_IF(!surface)) {
643     return nullptr;
644   }
645 
646   DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
647   if (NS_WARN_IF(!mapping.IsMapped())) {
648     return nullptr;
649   }
650 
651   gfx::ConvertYCbCrToRGB(mData, format, size, mapping.GetData(),
652                          mapping.GetStride());
653 
654   mSourceSurface = surface;
655 
656   return surface.forget();
657 }
658 
NVImage()659 NVImage::NVImage() : Image(nullptr, ImageFormat::NV_IMAGE), mBufferSize(0) {}
660 
661 NVImage::~NVImage() = default;
662 
GetSize() const663 IntSize NVImage::GetSize() const { return mSize; }
664 
GetPictureRect() const665 IntRect NVImage::GetPictureRect() const { return mData.GetPictureRect(); }
666 
GetAsSourceSurface()667 already_AddRefed<SourceSurface> NVImage::GetAsSourceSurface() {
668   if (mSourceSurface) {
669     RefPtr<gfx::SourceSurface> surface(mSourceSurface);
670     return surface.forget();
671   }
672 
673   // Convert the current NV12 or NV21 data to YUV420P so that we can follow the
674   // logics in PlanarYCbCrImage::GetAsSourceSurface().
675   const int bufferLength = mData.mYSize.height * mData.mYStride +
676                            mData.mCbCrSize.height * mData.mCbCrSize.width * 2;
677   auto* buffer = new uint8_t[bufferLength];
678 
679   Data aData = mData;
680   aData.mCbCrStride = aData.mCbCrSize.width;
681   aData.mCbSkip = 0;
682   aData.mCrSkip = 0;
683   aData.mYChannel = buffer;
684   aData.mCbChannel = aData.mYChannel + aData.mYSize.height * aData.mYStride;
685   aData.mCrChannel =
686       aData.mCbChannel + aData.mCbCrSize.height * aData.mCbCrStride;
687 
688   if (mData.mCbChannel < mData.mCrChannel) {  // NV12
689     libyuv::NV12ToI420(mData.mYChannel, mData.mYStride, mData.mCbChannel,
690                        mData.mCbCrStride, aData.mYChannel, aData.mYStride,
691                        aData.mCbChannel, aData.mCbCrStride, aData.mCrChannel,
692                        aData.mCbCrStride, aData.mYSize.width,
693                        aData.mYSize.height);
694   } else {  // NV21
695     libyuv::NV21ToI420(mData.mYChannel, mData.mYStride, mData.mCrChannel,
696                        mData.mCbCrStride, aData.mYChannel, aData.mYStride,
697                        aData.mCbChannel, aData.mCbCrStride, aData.mCrChannel,
698                        aData.mCbCrStride, aData.mYSize.width,
699                        aData.mYSize.height);
700   }
701 
702   // The logics in PlanarYCbCrImage::GetAsSourceSurface().
703   gfx::IntSize size(mSize);
704   gfx::SurfaceFormat format = gfx::ImageFormatToSurfaceFormat(
705       gfxPlatform::GetPlatform()->GetOffscreenFormat());
706   gfx::GetYCbCrToRGBDestFormatAndSize(aData, format, size);
707   if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
708       mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
709     NS_ERROR("Illegal image dest width or height");
710     return nullptr;
711   }
712 
713   RefPtr<gfx::DataSourceSurface> surface =
714       gfx::Factory::CreateDataSourceSurface(size, format);
715   if (NS_WARN_IF(!surface)) {
716     return nullptr;
717   }
718 
719   DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
720   if (NS_WARN_IF(!mapping.IsMapped())) {
721     return nullptr;
722   }
723 
724   gfx::ConvertYCbCrToRGB(aData, format, size, mapping.GetData(),
725                          mapping.GetStride());
726 
727   mSourceSurface = surface;
728 
729   // Release the temporary buffer.
730   delete[] buffer;
731 
732   return surface.forget();
733 }
734 
IsValid() const735 bool NVImage::IsValid() const { return !!mBufferSize; }
736 
GetBufferSize() const737 uint32_t NVImage::GetBufferSize() const { return mBufferSize; }
738 
AsNVImage()739 NVImage* NVImage::AsNVImage() { return this; };
740 
SetData(const Data & aData)741 bool NVImage::SetData(const Data& aData) {
742   MOZ_ASSERT(aData.mCbSkip == 1 && aData.mCrSkip == 1);
743   MOZ_ASSERT((int)std::abs(aData.mCbChannel - aData.mCrChannel) == 1);
744 
745   // Calculate buffer size
746   // Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
747   const auto checkedSize =
748       CheckedInt<uint32_t>(aData.mYSize.height) * aData.mYStride +
749       CheckedInt<uint32_t>(aData.mCbCrSize.height) * aData.mCbCrStride;
750 
751   if (!checkedSize.isValid()) return false;
752 
753   const auto size = checkedSize.value();
754 
755   // Allocate a new buffer.
756   mBuffer = AllocateBuffer(size);
757   if (!mBuffer) {
758     return false;
759   }
760 
761   // Update mBufferSize.
762   mBufferSize = size;
763 
764   // Update mData.
765   mData = aData;
766   mData.mYChannel = mBuffer.get();
767   mData.mCbChannel = mData.mYChannel + (aData.mCbChannel - aData.mYChannel);
768   mData.mCrChannel = mData.mYChannel + (aData.mCrChannel - aData.mYChannel);
769 
770   // Update mSize.
771   mSize = aData.mPicSize;
772 
773   // Copy the input data into mBuffer.
774   // This copies the y-channel and the interleaving CbCr-channel.
775   memcpy(mData.mYChannel, aData.mYChannel, mBufferSize);
776 
777   return true;
778 }
779 
GetData() const780 const NVImage::Data* NVImage::GetData() const { return &mData; }
781 
AllocateBuffer(uint32_t aSize)782 UniquePtr<uint8_t> NVImage::AllocateBuffer(uint32_t aSize) {
783   UniquePtr<uint8_t> buffer(new uint8_t[aSize]);
784   return buffer;
785 }
786 
SourceSurfaceImage(const gfx::IntSize & aSize,gfx::SourceSurface * aSourceSurface)787 SourceSurfaceImage::SourceSurfaceImage(const gfx::IntSize& aSize,
788                                        gfx::SourceSurface* aSourceSurface)
789     : Image(nullptr, ImageFormat::CAIRO_SURFACE),
790       mSize(aSize),
791       mSourceSurface(aSourceSurface),
792       mTextureFlags(TextureFlags::DEFAULT) {}
793 
SourceSurfaceImage(gfx::SourceSurface * aSourceSurface)794 SourceSurfaceImage::SourceSurfaceImage(gfx::SourceSurface* aSourceSurface)
795     : Image(nullptr, ImageFormat::CAIRO_SURFACE),
796       mSize(aSourceSurface->GetSize()),
797       mSourceSurface(aSourceSurface),
798       mTextureFlags(TextureFlags::DEFAULT) {}
799 
800 SourceSurfaceImage::~SourceSurfaceImage() = default;
801 
GetTextureClient(KnowsCompositor * aKnowsCompositor)802 TextureClient* SourceSurfaceImage::GetTextureClient(
803     KnowsCompositor* aKnowsCompositor) {
804   if (!aKnowsCompositor) {
805     return nullptr;
806   }
807 
808   auto entry = mTextureClients.LookupForAdd(aKnowsCompositor->GetSerial());
809   if (entry) {
810     return entry.Data();
811   }
812 
813   RefPtr<TextureClient> textureClient;
814   RefPtr<SourceSurface> surface = GetAsSourceSurface();
815   MOZ_ASSERT(surface);
816   if (surface) {
817     // gfx::BackendType::NONE means default to content backend
818     textureClient = TextureClient::CreateFromSurface(
819         aKnowsCompositor, surface, BackendSelector::Content, mTextureFlags,
820         ALLOC_DEFAULT);
821   }
822   if (textureClient) {
823     textureClient->SyncWithObject(aKnowsCompositor->GetSyncObject());
824     entry.OrInsert([&textureClient]() { return textureClient; });
825     return textureClient;
826   }
827 
828   // Remove the speculatively added entry.
829   entry.OrRemove();
830   return nullptr;
831 }
832 
AllocateProducerID()833 ImageContainer::ProducerID ImageContainer::AllocateProducerID() {
834   // Callable on all threads.
835   static Atomic<ImageContainer::ProducerID> sProducerID(0u);
836   return ++sProducerID;
837 }
838 
839 }  // namespace mozilla::layers
840