tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ImageContainer.cpp (42348B)


      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 
      9 #include <string.h>  // for memcpy, memset
     10 
     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 "GPUVideoImage.h"
     18 #include "libyuv.h"
     19 #include "mozilla/CheckedInt.h"
     20 #include "mozilla/ProfilerLabels.h"
     21 #include "mozilla/RefPtr.h"  // for already_AddRefed
     22 #include "mozilla/StaticPrefs_layers.h"
     23 #include "mozilla/gfx/2D.h"
     24 #include "mozilla/gfx/gfxVars.h"
     25 #include "mozilla/gfx/Swizzle.h"
     26 #include "mozilla/ipc/CrossProcessMutex.h"  // for CrossProcessMutex, etc
     27 #include "mozilla/layers/CompositorTypes.h"
     28 #include "mozilla/layers/ImageBridgeChild.h"     // for ImageBridgeChild
     29 #include "mozilla/layers/ImageClient.h"          // for ImageClient
     30 #include "mozilla/layers/ImageDataSerializer.h"  // for SurfaceDescriptorBuffer
     31 #include "mozilla/layers/LayersMessages.h"
     32 #include "mozilla/layers/SharedPlanarYCbCrImage.h"
     33 #include "mozilla/layers/SharedRGBImage.h"
     34 #include "mozilla/layers/TextureClientRecycleAllocator.h"
     35 #include "nsProxyRelease.h"
     36 #include "nsISupportsUtils.h"  // for NS_IF_ADDREF
     37 
     38 #ifdef XP_DARWIN
     39 #  include "MacIOSurfaceImage.h"
     40 #endif
     41 
     42 #ifdef XP_WIN
     43 #  include <d3d10_1.h>
     44 
     45 #  include "gfxWindowsPlatform.h"
     46 #  include "mozilla/gfx/DeviceManagerDx.h"
     47 #  include "mozilla/layers/D3D11ShareHandleImage.h"
     48 #  include "mozilla/layers/D3D11YCbCrImage.h"
     49 #endif
     50 
     51 namespace mozilla::layers {
     52 
     53 using namespace mozilla::gfx;
     54 using namespace mozilla::ipc;
     55 
     56 Atomic<int32_t> Image::sSerialCounter(0);
     57 
     58 Atomic<uint32_t> ImageContainer::sGenerationCounter(0);
     59 
     60 static void CopyPlane(uint8_t* aDst, const uint8_t* aSrc,
     61                      const gfx::IntSize& aSize, int32_t aStride,
     62                      int32_t aSkip);
     63 
     64 RefPtr<PlanarYCbCrImage> ImageFactory::CreatePlanarYCbCrImage(
     65    const gfx::IntSize& aScaleHint, BufferRecycleBin* aRecycleBin) {
     66  return new RecyclingPlanarYCbCrImage(aRecycleBin);
     67 }
     68 
     69 BufferRecycleBin::BufferRecycleBin()
     70    : mLock("mozilla.layers.BufferRecycleBin.mLock")
     71      // This member is only valid when the bin is not empty and will be
     72      // properly initialized in RecycleBuffer, but initializing it here avoids
     73      // static analysis noise.
     74      ,
     75      mRecycledBufferSize(0) {}
     76 
     77 void BufferRecycleBin::RecycleBuffer(UniquePtr<uint8_t[]> aBuffer,
     78                                     uint32_t aSize) {
     79  MutexAutoLock lock(mLock);
     80 
     81  if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) {
     82    mRecycledBuffers.Clear();
     83  }
     84  mRecycledBufferSize = aSize;
     85  mRecycledBuffers.AppendElement(std::move(aBuffer));
     86 }
     87 
     88 UniquePtr<uint8_t[]> BufferRecycleBin::GetBuffer(uint32_t aSize) {
     89  MutexAutoLock lock(mLock);
     90 
     91  if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize) {
     92    return UniquePtr<uint8_t[]>(new (fallible) uint8_t[aSize]);
     93  }
     94 
     95  return mRecycledBuffers.PopLastElement();
     96 }
     97 
     98 void BufferRecycleBin::ClearRecycledBuffers() {
     99  MutexAutoLock lock(mLock);
    100  if (!mRecycledBuffers.IsEmpty()) {
    101    mRecycledBuffers.Clear();
    102  }
    103  mRecycledBufferSize = 0;
    104 }
    105 
    106 ImageContainerListener::ImageContainerListener(ImageContainer* aImageContainer)
    107    : mLock("mozilla.layers.ImageContainerListener.mLock"),
    108      mImageContainer(aImageContainer) {}
    109 
    110 ImageContainerListener::~ImageContainerListener() = default;
    111 
    112 void ImageContainerListener::NotifyComposite(
    113    const ImageCompositeNotification& aNotification) {
    114  MutexAutoLock lock(mLock);
    115  if (mImageContainer) {
    116    mImageContainer->NotifyComposite(aNotification);
    117  }
    118 }
    119 
    120 void ImageContainerListener::NotifyDropped(uint32_t aDropped) {
    121  MutexAutoLock lock(mLock);
    122  if (mImageContainer) {
    123    mImageContainer->NotifyDropped(aDropped);
    124  }
    125 }
    126 
    127 void ImageContainerListener::ClearImageContainer() {
    128  MutexAutoLock lock(mLock);
    129  mImageContainer = nullptr;
    130 }
    131 
    132 void ImageContainerListener::DropImageClient() {
    133  MutexAutoLock lock(mLock);
    134  if (mImageContainer) {
    135    mImageContainer->DropImageClient();
    136  }
    137 }
    138 
    139 already_AddRefed<ImageClient> ImageContainer::GetImageClient() {
    140  RecursiveMutexAutoLock mon(mRecursiveMutex);
    141  EnsureImageClient();
    142  RefPtr<ImageClient> imageClient = mImageClient;
    143  return imageClient.forget();
    144 }
    145 
    146 void ImageContainer::DropImageClient() {
    147  RecursiveMutexAutoLock mon(mRecursiveMutex);
    148  if (mImageClient) {
    149    mImageClient->ClearCachedResources();
    150    mImageClient = nullptr;
    151  }
    152 }
    153 
    154 void ImageContainer::EnsureImageClient() {
    155  // If we're not forcing a new ImageClient, then we can skip this if we don't
    156  // have an existing ImageClient, or if the existing one belongs to an IPC
    157  // actor that is still open.
    158  if (!mIsAsync) {
    159    return;
    160  }
    161  if (mImageClient &&
    162      mImageClient->GetForwarder()->GetLayersIPCActor()->IPCOpen()) {
    163    return;
    164  }
    165 
    166  RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
    167  if (!imageBridge) {
    168    return;
    169  }
    170 
    171  mImageClient = imageBridge->CreateImageClient(CompositableType::IMAGE, this);
    172  if (mImageClient) {
    173    mAsyncContainerHandle = mImageClient->GetAsyncHandle();
    174  } else {
    175    // It's okay to drop the async container handle since the ImageBridgeChild
    176    // is going to die anyway.
    177    mAsyncContainerHandle = CompositableHandle();
    178  }
    179 }
    180 
    181 ImageContainer::ImageContainer(ImageUsageType aUsageType, Mode aFlag)
    182    : mUsageType(aUsageType),
    183      mIsAsync(aFlag == ASYNCHRONOUS),
    184      mRecursiveMutex("ImageContainer.mRecursiveMutex"),
    185      mGenerationCounter(++sGenerationCounter),
    186      mPaintCount(0),
    187      mDroppedImageCount(0),
    188      mImageFactory(new ImageFactory()),
    189      mRotation(VideoRotation::kDegree_0),
    190      mRecycleBin(new BufferRecycleBin()),
    191      mCurrentProducerID(-1) {
    192  if (aFlag == ASYNCHRONOUS) {
    193    mNotifyCompositeListener = new ImageContainerListener(this);
    194    EnsureImageClient();
    195  }
    196 }
    197 
    198 ImageContainer::~ImageContainer() {
    199  if (mNotifyCompositeListener) {
    200    mNotifyCompositeListener->ClearImageContainer();
    201  }
    202  if (mAsyncContainerHandle) {
    203    if (RefPtr<ImageBridgeChild> imageBridge =
    204            ImageBridgeChild::GetSingleton()) {
    205      imageBridge->ForgetImageContainer(mAsyncContainerHandle);
    206    }
    207  }
    208 }
    209 
    210 /* static */ nsresult Image::AllocateSurfaceDescriptorBufferRgb(
    211    const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat, uint8_t*& aOutBuffer,
    212    SurfaceDescriptorBuffer& aSdBuffer, int32_t& aStride,
    213    const std::function<layers::MemoryOrShmem(uint32_t)>& aAllocate) {
    214  aStride = ImageDataSerializer::ComputeRGBStride(aFormat, aSize.width);
    215  size_t length = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
    216 
    217  if (aStride <= 0 || length == 0) {
    218    return NS_ERROR_INVALID_ARG;
    219  }
    220 
    221  aSdBuffer.desc() = RGBDescriptor(aSize, aFormat);
    222  aSdBuffer.data() = aAllocate(length);
    223 
    224  const layers::MemoryOrShmem& memOrShmem = aSdBuffer.data();
    225  switch (memOrShmem.type()) {
    226    case layers::MemoryOrShmem::Tuintptr_t:
    227      aOutBuffer = reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t());
    228      break;
    229    case layers::MemoryOrShmem::TShmem:
    230      aOutBuffer = memOrShmem.get_Shmem().get<uint8_t>();
    231      break;
    232    default:
    233      return NS_ERROR_OUT_OF_MEMORY;
    234  }
    235 
    236  MOZ_ASSERT(aOutBuffer);
    237  return NS_OK;
    238 }
    239 
    240 nsresult Image::BuildSurfaceDescriptorBuffer(
    241    SurfaceDescriptorBuffer& aSdBuffer, BuildSdbFlags aFlags,
    242    const std::function<MemoryOrShmem(uint32_t)>& aAllocate) {
    243  RefPtr<SourceSurface> surface = GetAsSourceSurface();
    244  if (NS_WARN_IF(!surface)) {
    245    return NS_ERROR_NOT_AVAILABLE;
    246  }
    247 
    248  RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
    249  if (NS_WARN_IF(!dataSurface)) {
    250    return NS_ERROR_FAILURE;
    251  }
    252 
    253  DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ);
    254  if (NS_WARN_IF(!map.IsMapped())) {
    255    return NS_ERROR_FAILURE;
    256  }
    257 
    258  SurfaceFormat format = dataSurface->GetFormat();
    259  IntSize size = dataSurface->GetSize();
    260  uint8_t* output = nullptr;
    261  int32_t stride = 0;
    262  nsresult rv = AllocateSurfaceDescriptorBufferRgb(
    263      size, format, output, aSdBuffer, stride, aAllocate);
    264  if (NS_WARN_IF(NS_FAILED(rv))) {
    265    return rv;
    266  }
    267 
    268  if (NS_WARN_IF(!SwizzleData(map.GetData(), map.GetStride(), format, output,
    269                              stride, format, size))) {
    270    return NS_ERROR_FAILURE;
    271  }
    272 
    273  return NS_OK;
    274 }
    275 
    276 nsresult Image::BuildSurfaceDescriptorGPUVideoOrBuffer(
    277    SurfaceDescriptor& aSd, BuildSdbFlags aFlags,
    278    const Maybe<VideoBridgeSource>& aDest,
    279    const std::function<MemoryOrShmem(uint32_t)>& aAllocate,
    280    const std::function<void(MemoryOrShmem&&)>& aFree) {
    281  if (auto* gpuImage = AsGPUVideoImage()) {
    282    if (auto maybeSd = gpuImage->GetDesc()) {
    283      if (!aDest ||
    284          (maybeSd->type() == SurfaceDescriptor::TSurfaceDescriptorGPUVideo &&
    285           maybeSd->get_SurfaceDescriptorGPUVideo()
    286                   .get_SurfaceDescriptorRemoteDecoder()
    287                   .source() == aDest)) {
    288        aSd = std::move(*maybeSd);
    289        return NS_OK;
    290      }
    291    }
    292  }
    293 
    294  SurfaceDescriptorBuffer sdb;
    295  nsresult rv = BuildSurfaceDescriptorBuffer(sdb, aFlags, aAllocate);
    296  if (NS_FAILED(rv)) {
    297    if (sdb.data().type() != MemoryOrShmem::Type::T__None) {
    298      aFree(std::move(sdb.data()));
    299    }
    300    return rv;
    301  }
    302 
    303  aSd = std::move(sdb);
    304  return NS_OK;
    305 }
    306 
    307 Maybe<SurfaceDescriptor> Image::GetDesc() { return GetDescFromTexClient(); }
    308 
    309 Maybe<SurfaceDescriptor> Image::GetDescFromTexClient(
    310    TextureClient* const tcOverride) {
    311  RefPtr<TextureClient> tc = tcOverride;
    312  if (!tcOverride) {
    313    tc = GetTextureClient(nullptr);
    314  }
    315  if (!tc) {
    316    return {};
    317  }
    318 
    319  const auto& tcd = tc->GetInternalData();
    320 
    321  SurfaceDescriptor ret;
    322  if (!tcd->Serialize(ret)) {
    323    return {};
    324  }
    325  return Some(ret);
    326 }
    327 
    328 already_AddRefed<ImageContainerListener>
    329 ImageContainer::GetImageContainerListener() const {
    330  MOZ_ASSERT(InImageBridgeChildThread());
    331  return do_AddRef(mNotifyCompositeListener);
    332 }
    333 
    334 RefPtr<PlanarYCbCrImage> ImageContainer::CreatePlanarYCbCrImage() {
    335  RecursiveMutexAutoLock lock(mRecursiveMutex);
    336  EnsureImageClient();
    337  if (mImageClient && mImageClient->AsImageClientSingle()) {
    338    return new SharedPlanarYCbCrImage(mImageClient);
    339  }
    340  if (mRecycleAllocator) {
    341    return new SharedPlanarYCbCrImage(mRecycleAllocator);
    342  }
    343  return mImageFactory->CreatePlanarYCbCrImage(mScaleHint, mRecycleBin);
    344 }
    345 
    346 RefPtr<SharedRGBImage> ImageContainer::CreateSharedRGBImage() {
    347  RecursiveMutexAutoLock lock(mRecursiveMutex);
    348  EnsureImageClient();
    349  if (mImageClient && mImageClient->AsImageClientSingle()) {
    350    return new SharedRGBImage(mImageClient);
    351  }
    352  if (mRecycleAllocator) {
    353    return new SharedRGBImage(mRecycleAllocator);
    354  }
    355  return nullptr;
    356 }
    357 
    358 void ImageContainer::SetCurrentImageInternal(
    359    const nsTArray<NonOwningImage>& aImages) {
    360  RecursiveMutexAutoLock lock(mRecursiveMutex);
    361 
    362  mGenerationCounter = ++sGenerationCounter;
    363 
    364  if (!aImages.IsEmpty()) {
    365    NS_ASSERTION(mCurrentImages.IsEmpty() ||
    366                     mCurrentImages[0].mProducerID != aImages[0].mProducerID ||
    367                     mCurrentImages[0].mFrameID <= aImages[0].mFrameID,
    368                 "frame IDs shouldn't go backwards");
    369    if (aImages[0].mProducerID != mCurrentProducerID) {
    370      mCurrentProducerID = aImages[0].mProducerID;
    371    }
    372    for (auto& img : mCurrentImages) {
    373      img.mImage->OnAbandonForwardToHost();
    374    }
    375  }
    376 
    377  nsTArray<OwningImage> newImages;
    378 
    379  for (uint32_t i = 0; i < aImages.Length(); ++i) {
    380    NS_ASSERTION(aImages[i].mImage, "image can't be null");
    381    NS_ASSERTION(!aImages[i].mTimeStamp.IsNull() || aImages.Length() == 1,
    382                 "Multiple images require timestamps");
    383    if (i > 0) {
    384      NS_ASSERTION(aImages[i].mTimeStamp >= aImages[i - 1].mTimeStamp,
    385                   "Timestamps must not decrease");
    386      NS_ASSERTION(aImages[i].mFrameID > aImages[i - 1].mFrameID,
    387                   "FrameIDs must increase");
    388      NS_ASSERTION(aImages[i].mProducerID == aImages[i - 1].mProducerID,
    389                   "ProducerIDs must be the same");
    390    }
    391    OwningImage* img = newImages.AppendElement();
    392    img->mImage = aImages[i].mImage;
    393    img->mTimeStamp = aImages[i].mTimeStamp;
    394    img->mProcessingDuration = aImages[i].mProcessingDuration;
    395    img->mMediaTime = aImages[i].mMediaTime;
    396    img->mWebrtcCaptureTime = aImages[i].mWebrtcCaptureTime;
    397    img->mWebrtcReceiveTime = aImages[i].mWebrtcReceiveTime;
    398    img->mRtpTimestamp = aImages[i].mRtpTimestamp;
    399    img->mFrameID = aImages[i].mFrameID;
    400    img->mProducerID = aImages[i].mProducerID;
    401    for (const auto& oldImg : mCurrentImages) {
    402      if (oldImg.mFrameID == img->mFrameID &&
    403          oldImg.mProducerID == img->mProducerID) {
    404        img->mComposited = oldImg.mComposited;
    405        break;
    406      }
    407    }
    408    img->mImage->OnPrepareForwardToHost();
    409  }
    410 
    411  mCurrentImages = std::move(newImages);
    412 }
    413 
    414 void ImageContainer::ClearImagesFromImageBridge() {
    415  RecursiveMutexAutoLock lock(mRecursiveMutex);
    416  SetCurrentImageInternal(nsTArray<NonOwningImage>());
    417 }
    418 
    419 void ImageContainer::SetCurrentImages(const nsTArray<NonOwningImage>& aImages) {
    420  AUTO_PROFILER_LABEL("ImageContainer::SetCurrentImages", GRAPHICS);
    421  MOZ_ASSERT(!aImages.IsEmpty());
    422  MOZ_ASSERT(mUsageType == ImageUsageType::Canvas ||
    423             mUsageType == ImageUsageType::OffscreenCanvas ||
    424             mUsageType == ImageUsageType::VideoFrameContainer);
    425 
    426  RecursiveMutexAutoLock lock(mRecursiveMutex);
    427  if (mIsAsync) {
    428    if (RefPtr<ImageBridgeChild> imageBridge =
    429            ImageBridgeChild::GetSingleton()) {
    430      imageBridge->UpdateImageClient(this);
    431    }
    432  }
    433  SetCurrentImageInternal(aImages);
    434 }
    435 
    436 void ImageContainer::ClearImagesInHost(ClearImagesType aType) {
    437  MOZ_ASSERT(mIsAsync);
    438  if (!mIsAsync) {
    439    return;
    440  }
    441 
    442  mRecursiveMutex.Lock();
    443  if (mImageClient) {
    444    RefPtr<ImageClient> imageClient = mImageClient;
    445    mRecursiveMutex.Unlock();
    446 
    447    // Let ImageClient clear Images(TextureClients). This doesn't return
    448    // until ImageBridge has called ImageClient::ClearImagesInHost.
    449    if (RefPtr<ImageBridgeChild> imageBridge =
    450            ImageBridgeChild::GetSingleton()) {
    451      imageBridge->ClearImagesInHost(imageClient, this, aType);
    452    }
    453    return;
    454  }
    455 
    456  SetCurrentImageInternal(nsTArray<NonOwningImage>());
    457  mRecursiveMutex.Unlock();
    458 }
    459 
    460 void ImageContainer::ClearCachedResources() {
    461  RecursiveMutexAutoLock lock(mRecursiveMutex);
    462  if (mImageClient && mImageClient->AsImageClientSingle()) {
    463    if (!mImageClient->HasTextureClientRecycler()) {
    464      return;
    465    }
    466    mImageClient->GetTextureClientRecycler()->ShrinkToMinimumSize();
    467    return;
    468  }
    469  return mRecycleBin->ClearRecycledBuffers();
    470 }
    471 
    472 void ImageContainer::SetCurrentImageInTransaction(Image* aImage) {
    473  AutoTArray<NonOwningImage, 1> images;
    474  images.AppendElement(NonOwningImage(aImage));
    475  SetCurrentImagesInTransaction(images);
    476 }
    477 
    478 void ImageContainer::SetCurrentImagesInTransaction(
    479    const nsTArray<NonOwningImage>& aImages) {
    480  MOZ_ASSERT(!mIsAsync);
    481  MOZ_ASSERT(mUsageType == ImageUsageType::WebRenderFallbackData);
    482 
    483  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
    484  NS_ASSERTION(!HasImageClient(),
    485               "Should use async image transfer with ImageBridge.");
    486 
    487  SetCurrentImageInternal(aImages);
    488 }
    489 
    490 bool ImageContainer::IsAsync() const { return mIsAsync; }
    491 
    492 CompositableHandle ImageContainer::GetAsyncContainerHandle() {
    493  NS_ASSERTION(IsAsync(),
    494               "Shared image ID is only relevant to async ImageContainers");
    495  RecursiveMutexAutoLock mon(mRecursiveMutex);
    496  NS_ASSERTION(mAsyncContainerHandle, "Should have a shared image ID");
    497  EnsureImageClient();
    498  return mAsyncContainerHandle;
    499 }
    500 
    501 bool ImageContainer::HasCurrentImage() {
    502  RecursiveMutexAutoLock lock(mRecursiveMutex);
    503 
    504  return !mCurrentImages.IsEmpty();
    505 }
    506 
    507 void ImageContainer::GetCurrentImages(nsTArray<OwningImage>* aImages,
    508                                      uint32_t* aGenerationCounter) {
    509  RecursiveMutexAutoLock lock(mRecursiveMutex);
    510 
    511  aImages->Assign(mCurrentImages);
    512  if (aGenerationCounter) {
    513    *aGenerationCounter = mGenerationCounter;
    514  }
    515 }
    516 
    517 gfx::IntSize ImageContainer::GetCurrentSize() {
    518  RecursiveMutexAutoLock lock(mRecursiveMutex);
    519 
    520  if (mCurrentImages.IsEmpty()) {
    521    return gfx::IntSize(0, 0);
    522  }
    523 
    524  return mCurrentImages[0].mImage->GetSize();
    525 }
    526 
    527 void ImageContainer::NotifyComposite(
    528    const ImageCompositeNotification& aNotification) {
    529  RecursiveMutexAutoLock lock(mRecursiveMutex);
    530 
    531  // An image composition notification is sent the first time a particular
    532  // image is composited by an ImageHost. Thus, every time we receive such
    533  // a notification, a new image has been painted.
    534  ++mPaintCount;
    535 
    536  if (aNotification.producerID() == mCurrentProducerID) {
    537    for (auto& img : mCurrentImages) {
    538      if (img.mFrameID == aNotification.frameID()) {
    539        img.mComposited = true;
    540      }
    541    }
    542  }
    543 
    544  if (!aNotification.imageTimeStamp().IsNull()) {
    545    mPaintDelay = aNotification.firstCompositeTimeStamp() -
    546                  aNotification.imageTimeStamp();
    547  }
    548 }
    549 
    550 void ImageContainer::NotifyDropped(uint32_t aDropped) {
    551  mDroppedImageCount += aDropped;
    552 }
    553 
    554 void ImageContainer::EnsureRecycleAllocatorForRDD(
    555    KnowsCompositor* aKnowsCompositor) {
    556  MOZ_ASSERT(!mIsAsync);
    557  MOZ_ASSERT(XRE_IsRDDProcess());
    558 
    559  RecursiveMutexAutoLock lock(mRecursiveMutex);
    560  MOZ_ASSERT(!mImageClient);
    561 
    562  if (mRecycleAllocator &&
    563      aKnowsCompositor == mRecycleAllocator->GetKnowsCompositor()) {
    564    return;
    565  }
    566 
    567  if (!StaticPrefs::layers_recycle_allocator_rdd_AtStartup()) {
    568    return;
    569  }
    570 
    571  static const uint32_t MAX_POOLED_VIDEO_COUNT = 5;
    572 
    573  mRecycleAllocator =
    574      new layers::TextureClientRecycleAllocator(aKnowsCompositor);
    575  mRecycleAllocator->SetMaxPoolSize(MAX_POOLED_VIDEO_COUNT);
    576 }
    577 
    578 #ifdef XP_WIN
    579 already_AddRefed<D3D11RecycleAllocator>
    580 ImageContainer::GetD3D11RecycleAllocator(KnowsCompositor* aKnowsCompositor,
    581                                         gfx::SurfaceFormat aPreferredFormat) {
    582  MOZ_ASSERT(aKnowsCompositor);
    583 
    584  if (!aKnowsCompositor->SupportsD3D11()) {
    585    return nullptr;
    586  }
    587 
    588  RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetImageDevice();
    589  if (!device) {
    590    return nullptr;
    591  }
    592 
    593  RecursiveMutexAutoLock lock(mRecursiveMutex);
    594  if (mD3D11RecycleAllocator && mD3D11RecycleAllocator->mDevice == device &&
    595      mD3D11RecycleAllocator->GetKnowsCompositor() == aKnowsCompositor) {
    596    return do_AddRef(mD3D11RecycleAllocator);
    597  }
    598 
    599  mD3D11RecycleAllocator =
    600      new D3D11RecycleAllocator(aKnowsCompositor, device, aPreferredFormat);
    601 
    602  if (device != DeviceManagerDx::Get()->GetCompositorDevice()) {
    603    RefPtr<SyncObjectClient> syncObject =
    604        SyncObjectClient::CreateSyncObjectClient(
    605            aKnowsCompositor->GetTextureFactoryIdentifier().mSyncHandle,
    606            device);
    607    mD3D11RecycleAllocator->SetSyncObject(syncObject);
    608  }
    609 
    610  return do_AddRef(mD3D11RecycleAllocator);
    611 }
    612 
    613 already_AddRefed<D3D11YCbCrRecycleAllocator>
    614 ImageContainer::GetD3D11YCbCrRecycleAllocator(
    615    KnowsCompositor* aKnowsCompositor) {
    616  if (!aKnowsCompositor->SupportsD3D11() ||
    617      !gfx::DeviceManagerDx::Get()->GetImageDevice()) {
    618    return nullptr;
    619  }
    620 
    621  RecursiveMutexAutoLock lock(mRecursiveMutex);
    622  if (mD3D11YCbCrRecycleAllocator &&
    623      aKnowsCompositor == mD3D11YCbCrRecycleAllocator->GetKnowsCompositor()) {
    624    return do_AddRef(mD3D11YCbCrRecycleAllocator);
    625  }
    626 
    627  mD3D11YCbCrRecycleAllocator =
    628      new D3D11YCbCrRecycleAllocator(aKnowsCompositor);
    629  return do_AddRef(mD3D11YCbCrRecycleAllocator);
    630 }
    631 #endif
    632 
    633 #ifdef XP_DARWIN
    634 already_AddRefed<MacIOSurfaceRecycleAllocator>
    635 ImageContainer::GetMacIOSurfaceRecycleAllocator() {
    636  RecursiveMutexAutoLock lock(mRecursiveMutex);
    637  if (!mMacIOSurfaceRecycleAllocator) {
    638    mMacIOSurfaceRecycleAllocator = new MacIOSurfaceRecycleAllocator();
    639  }
    640 
    641  return do_AddRef(mMacIOSurfaceRecycleAllocator);
    642 }
    643 #endif
    644 
    645 // -
    646 // https://searchfox.org/mozilla-central/source/dom/media/ipc/RemoteImageHolder.cpp#46
    647 
    648 Maybe<PlanarYCbCrData> PlanarYCbCrData::From(
    649    const SurfaceDescriptorBuffer& sdb) {
    650  if (sdb.desc().type() != BufferDescriptor::TYCbCrDescriptor) {
    651    return {};
    652  }
    653  const YCbCrDescriptor& yuvDesc = sdb.desc().get_YCbCrDescriptor();
    654 
    655  Maybe<Range<uint8_t>> buffer;
    656  const MemoryOrShmem& memOrShmem = sdb.data();
    657  switch (memOrShmem.type()) {
    658    case MemoryOrShmem::Tuintptr_t:
    659      gfxCriticalError() << "PlanarYCbCrData::From SurfaceDescriptorBuffer "
    660                            "w/uintptr_t unsupported.";
    661      break;
    662    case MemoryOrShmem::TShmem:
    663      buffer.emplace(memOrShmem.get_Shmem().Range<uint8_t>());
    664      break;
    665    default:
    666      MOZ_ASSERT(false, "Unknown MemoryOrShmem type");
    667      break;
    668  }
    669  if (!buffer) {
    670    return {};
    671  }
    672 
    673  PlanarYCbCrData yuvData;
    674  yuvData.mYStride = AssertedCast<int32_t>(yuvDesc.yStride());
    675  yuvData.mCbCrStride = AssertedCast<int32_t>(yuvDesc.cbCrStride());
    676  // default mYSkip, mCbSkip, mCrSkip because not held in YCbCrDescriptor
    677  yuvData.mYSkip = yuvData.mCbSkip = yuvData.mCrSkip = 0;
    678  yuvData.mPictureRect = yuvDesc.display();
    679  yuvData.mStereoMode = yuvDesc.stereoMode();
    680  yuvData.mColorDepth = yuvDesc.colorDepth();
    681  yuvData.mYUVColorSpace = yuvDesc.yUVColorSpace();
    682  yuvData.mColorRange = yuvDesc.colorRange();
    683  yuvData.mChromaSubsampling = yuvDesc.chromaSubsampling();
    684 
    685  const auto GetPlanePtr = [&](const uint32_t beginOffset,
    686                               const gfx::IntSize size,
    687                               const int32_t stride) -> uint8_t* {
    688    if (size.width > stride) return nullptr;
    689    auto bytesNeeded = CheckedInt<uintptr_t>(stride) *
    690                       size.height;  // Don't accept `stride*(h-1)+w`.
    691    bytesNeeded += beginOffset;
    692    if (!bytesNeeded.isValid() || bytesNeeded.value() > buffer->length()) {
    693      gfxCriticalError()
    694          << "PlanarYCbCrData::From asked for out-of-bounds plane data.";
    695      return nullptr;
    696    }
    697    return (buffer->begin() + beginOffset).get();
    698  };
    699  yuvData.mYChannel =
    700      GetPlanePtr(yuvDesc.yOffset(), yuvDesc.ySize(), yuvData.mYStride);
    701  yuvData.mCbChannel =
    702      GetPlanePtr(yuvDesc.cbOffset(), yuvDesc.cbCrSize(), yuvData.mCbCrStride);
    703  yuvData.mCrChannel =
    704      GetPlanePtr(yuvDesc.crOffset(), yuvDesc.cbCrSize(), yuvData.mCbCrStride);
    705 
    706  if (yuvData.mYSkip || yuvData.mCbSkip || yuvData.mCrSkip ||
    707      yuvDesc.ySize().width < 0 || yuvDesc.ySize().height < 0 ||
    708      yuvDesc.cbCrSize().width < 0 || yuvDesc.cbCrSize().height < 0 ||
    709      yuvData.mYStride < 0 || yuvData.mCbCrStride < 0 || !yuvData.mYChannel ||
    710      !yuvData.mCbChannel || !yuvData.mCrChannel) {
    711    gfxCriticalError() << "Unusual PlanarYCbCrData: " << yuvData.mYSkip << ","
    712                       << yuvData.mCbSkip << "," << yuvData.mCrSkip << ", "
    713                       << yuvDesc.ySize().width << "," << yuvDesc.ySize().height
    714                       << ", " << yuvDesc.cbCrSize().width << ","
    715                       << yuvDesc.cbCrSize().height << ", " << yuvData.mYStride
    716                       << "," << yuvData.mCbCrStride << ", "
    717                       << yuvData.mYChannel << "," << yuvData.mCbChannel << ","
    718                       << yuvData.mCrChannel;
    719    return {};
    720  }
    721 
    722  return Some(yuvData);
    723 }
    724 
    725 Maybe<PlanarYCbCrData> PlanarYCbCrData::From(
    726    const VideoData::YCbCrBuffer& yuvDesc) {
    727  constexpr int YPlane = 0;
    728  constexpr int CbPlane = 1;
    729  constexpr int CrPlane = 2;
    730 
    731  PlanarYCbCrData yuvData;
    732  yuvData.mYStride = yuvDesc.mPlanes[YPlane].mStride;
    733  yuvData.mCbCrStride = yuvDesc.mPlanes[CbPlane].mStride;
    734  yuvData.mYSkip = yuvDesc.mPlanes[YPlane].mSkip;
    735  yuvData.mCbSkip = yuvDesc.mPlanes[CbPlane].mSkip;
    736  yuvData.mCrSkip = yuvDesc.mPlanes[CrPlane].mSkip;
    737  yuvData.mPictureRect = gfx::IntRect(0, 0, yuvDesc.mPlanes[YPlane].mWidth,
    738                                      yuvDesc.mPlanes[YPlane].mHeight);
    739  yuvData.mColorDepth = yuvDesc.mColorDepth;
    740  yuvData.mYUVColorSpace = yuvDesc.mYUVColorSpace;
    741  yuvData.mColorRange = yuvDesc.mColorRange;
    742  yuvData.mChromaSubsampling = yuvDesc.mChromaSubsampling;
    743  yuvData.mYChannel = yuvDesc.mPlanes[YPlane].mData;
    744  yuvData.mCbChannel = yuvDesc.mPlanes[CbPlane].mData;
    745  yuvData.mCrChannel = yuvDesc.mPlanes[CrPlane].mData;
    746 
    747  if (yuvData.mYSkip || yuvData.mCbSkip || yuvData.mCrSkip ||
    748      yuvData.mYStride < 0 || yuvData.mCbCrStride < 0 || !yuvData.mYChannel ||
    749      !yuvData.mCbChannel || !yuvData.mCrChannel) {
    750    gfxCriticalError() << "Unusual PlanarYCbCrData: " << yuvData.mYSkip << ","
    751                       << yuvData.mCbSkip << "," << yuvData.mCrSkip << ","
    752                       << yuvData.mYStride << "," << yuvData.mCbCrStride << ", "
    753                       << yuvData.mYChannel << "," << yuvData.mCbChannel << ","
    754                       << yuvData.mCrChannel;
    755    return {};
    756  }
    757 
    758  return Some(yuvData);
    759 }
    760 
    761 // -
    762 
    763 PlanarYCbCrImage::PlanarYCbCrImage()
    764    : Image(nullptr, ImageFormat::PLANAR_YCBCR),
    765      mOffscreenFormat(SurfaceFormat::UNKNOWN),
    766      mBufferSize(0) {}
    767 
    768 nsresult PlanarYCbCrImage::BuildSurfaceDescriptorBuffer(
    769    SurfaceDescriptorBuffer& aSdBuffer, BuildSdbFlags aFlags,
    770    const std::function<MemoryOrShmem(uint32_t)>& aAllocate) {
    771  const PlanarYCbCrData* pdata = GetData();
    772  MOZ_ASSERT(pdata, "must have PlanarYCbCrData");
    773  MOZ_ASSERT(pdata->mYSkip == 0 && pdata->mCbSkip == 0 && pdata->mCrSkip == 0,
    774             "YCbCrDescriptor doesn't hold skip values");
    775 
    776  if (aFlags & BuildSdbFlags::RgbOnly) {
    777    gfx::IntSize size(mSize);
    778    auto format = gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());
    779    gfx::GetYCbCrToRGBDestFormatAndSize(mData, format, size);
    780 
    781    uint8_t* buffer = nullptr;
    782    int32_t stride = 0;
    783    nsresult rv = AllocateSurfaceDescriptorBufferRgb(
    784        size, format, buffer, aSdBuffer, stride, aAllocate);
    785    if (NS_WARN_IF(NS_FAILED(rv))) {
    786      return rv;
    787    }
    788 
    789    // If we can copy directly from the surface, let's do that to avoid the YUV
    790    // to RGB conversion.
    791    if (mSourceSurface && mSourceSurface->GetSize() == size) {
    792      DataSourceSurface::ScopedMap map(mSourceSurface, DataSourceSurface::READ);
    793      if (map.IsMapped() && SwizzleData(map.GetData(), map.GetStride(),
    794                                        mSourceSurface->GetFormat(), buffer,
    795                                        stride, format, size)) {
    796        return NS_OK;
    797      }
    798    }
    799 
    800    rv = gfx::ConvertYCbCrToRGB(mData, format, size, buffer, stride);
    801    MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to convert YUV into RGB data");
    802    return rv;
    803  }
    804 
    805  auto ySize = pdata->YDataSize();
    806  auto cbcrSize = pdata->CbCrDataSize();
    807  uint32_t yOffset;
    808  uint32_t cbOffset;
    809  uint32_t crOffset;
    810  ImageDataSerializer::ComputeYCbCrOffsets(pdata->mYStride, ySize.height,
    811                                           pdata->mCbCrStride, cbcrSize.height,
    812                                           yOffset, cbOffset, crOffset);
    813 
    814  uint32_t bufferSize = ImageDataSerializer::ComputeYCbCrBufferSize(
    815      ySize, pdata->mYStride, cbcrSize, pdata->mCbCrStride, yOffset, cbOffset,
    816      crOffset);
    817 
    818  aSdBuffer.data() = aAllocate(bufferSize);
    819 
    820  uint8_t* buffer = nullptr;
    821  const MemoryOrShmem& memOrShmem = aSdBuffer.data();
    822  switch (memOrShmem.type()) {
    823    case MemoryOrShmem::Tuintptr_t:
    824      buffer = reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t());
    825      break;
    826    case MemoryOrShmem::TShmem:
    827      buffer = memOrShmem.get_Shmem().get<uint8_t>();
    828      break;
    829    default:
    830      buffer = nullptr;
    831      break;
    832  }
    833  if (!buffer) {
    834    return NS_ERROR_OUT_OF_MEMORY;
    835  }
    836 
    837  aSdBuffer.desc() = YCbCrDescriptor(
    838      pdata->mPictureRect, ySize, pdata->mYStride, cbcrSize, pdata->mCbCrStride,
    839      yOffset, cbOffset, crOffset, pdata->mStereoMode, pdata->mColorDepth,
    840      pdata->mYUVColorSpace, pdata->mColorRange, pdata->mChromaSubsampling);
    841 
    842  CopyPlane(buffer + yOffset, pdata->mYChannel, ySize, pdata->mYStride,
    843            pdata->mYSkip);
    844  CopyPlane(buffer + cbOffset, pdata->mCbChannel, cbcrSize, pdata->mCbCrStride,
    845            pdata->mCbSkip);
    846  CopyPlane(buffer + crOffset, pdata->mCrChannel, cbcrSize, pdata->mCbCrStride,
    847            pdata->mCrSkip);
    848  return NS_OK;
    849 }
    850 
    851 RecyclingPlanarYCbCrImage::~RecyclingPlanarYCbCrImage() {
    852  if (mBuffer) {
    853    mRecycleBin->RecycleBuffer(std::move(mBuffer), mBufferSize);
    854  }
    855 }
    856 
    857 size_t RecyclingPlanarYCbCrImage::SizeOfExcludingThis(
    858    MallocSizeOf aMallocSizeOf) const {
    859  // Ignoring:
    860  // - mData - just wraps mBuffer
    861  // - Surfaces should be reported under gfx-surfaces-*:
    862  //   - mSourceSurface
    863  // - Base class:
    864  //   - mImplData is not used
    865  // Not owned:
    866  // - mRecycleBin
    867  size_t size = aMallocSizeOf(mBuffer.get());
    868 
    869  // Could add in the future:
    870  // - mBackendData (from base class)
    871 
    872  return size;
    873 }
    874 
    875 UniquePtr<uint8_t[]> RecyclingPlanarYCbCrImage::AllocateBuffer(uint32_t aSize) {
    876  return mRecycleBin->GetBuffer(aSize);
    877 }
    878 
    879 static void CopyPlane(uint8_t* aDst, const uint8_t* aSrc,
    880                      const gfx::IntSize& aSize, int32_t aStride,
    881                      int32_t aSkip) {
    882  int32_t height = aSize.height;
    883  int32_t width = aSize.width;
    884 
    885  MOZ_RELEASE_ASSERT(width <= aStride);
    886 
    887  if (!aSkip) {
    888    // Fast path: planar input.
    889    memcpy(aDst, aSrc, height * aStride);
    890  } else {
    891    for (int y = 0; y < height; ++y) {
    892      const uint8_t* src = aSrc;
    893      uint8_t* dst = aDst;
    894      // Slow path
    895      for (int x = 0; x < width; ++x) {
    896        *dst++ = *src++;
    897        src += aSkip;
    898      }
    899      aSrc += aStride;
    900      aDst += aStride;
    901    }
    902  }
    903 }
    904 
    905 nsresult RecyclingPlanarYCbCrImage::CopyData(const Data& aData) {
    906  // update buffer size
    907  // Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
    908  auto ySize = aData.YDataSize();
    909  auto cbcrSize = aData.CbCrDataSize();
    910  const auto checkedSize =
    911      CheckedInt<uint32_t>(aData.mCbCrStride) * cbcrSize.height * 2 +
    912      CheckedInt<uint32_t>(aData.mYStride) * ySize.height *
    913          (aData.mAlpha ? 2 : 1);
    914 
    915  if (!checkedSize.isValid()) return NS_ERROR_INVALID_ARG;
    916 
    917  const auto size = checkedSize.value();
    918 
    919  // get new buffer
    920  mBuffer = AllocateBuffer(size);
    921  if (!mBuffer) return NS_ERROR_OUT_OF_MEMORY;
    922 
    923  // update buffer size
    924  mBufferSize = size;
    925 
    926  mData = aData;  // mAlpha will be set if aData has it
    927  mData.mYChannel = mBuffer.get();
    928  mData.mCbChannel = mData.mYChannel + mData.mYStride * ySize.height;
    929  mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * cbcrSize.height;
    930  mData.mYSkip = mData.mCbSkip = mData.mCrSkip = 0;
    931 
    932  CopyPlane(mData.mYChannel, aData.mYChannel, ySize, aData.mYStride,
    933            aData.mYSkip);
    934  CopyPlane(mData.mCbChannel, aData.mCbChannel, cbcrSize, aData.mCbCrStride,
    935            aData.mCbSkip);
    936  CopyPlane(mData.mCrChannel, aData.mCrChannel, cbcrSize, aData.mCbCrStride,
    937            aData.mCrSkip);
    938  if (aData.mAlpha) {
    939    MOZ_ASSERT(mData.mAlpha);
    940    mData.mAlpha->mChannel =
    941        mData.mCrChannel + mData.mCbCrStride * cbcrSize.height;
    942    CopyPlane(mData.mAlpha->mChannel, aData.mAlpha->mChannel, ySize,
    943              aData.mYStride, aData.mYSkip);
    944  }
    945 
    946  mSize = aData.mPictureRect.Size();
    947  mOrigin = aData.mPictureRect.TopLeft();
    948  return NS_OK;
    949 }
    950 
    951 gfxImageFormat PlanarYCbCrImage::GetOffscreenFormat() const {
    952  return mOffscreenFormat == SurfaceFormat::UNKNOWN ? gfxVars::OffscreenFormat()
    953                                                    : mOffscreenFormat;
    954 }
    955 
    956 nsresult PlanarYCbCrImage::AdoptData(const Data& aData) {
    957  mData = aData;
    958  mSize = aData.mPictureRect.Size();
    959  mOrigin = aData.mPictureRect.TopLeft();
    960  return NS_OK;
    961 }
    962 
    963 already_AddRefed<gfx::SourceSurface> PlanarYCbCrImage::GetAsSourceSurface() {
    964  if (mSourceSurface) {
    965    RefPtr<gfx::SourceSurface> surface(mSourceSurface);
    966    return surface.forget();
    967  }
    968 
    969  gfx::IntSize size(mSize);
    970  gfx::SurfaceFormat format =
    971      gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());
    972  gfx::GetYCbCrToRGBDestFormatAndSize(mData, format, size);
    973  if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
    974      mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
    975    NS_ERROR("Illegal image dest width or height");
    976    return nullptr;
    977  }
    978 
    979  RefPtr<gfx::DataSourceSurface> surface =
    980      gfx::Factory::CreateDataSourceSurface(size, format);
    981  if (NS_WARN_IF(!surface)) {
    982    return nullptr;
    983  }
    984 
    985  DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
    986  if (NS_WARN_IF(!mapping.IsMapped())) {
    987    return nullptr;
    988  }
    989 
    990  if (NS_WARN_IF(NS_FAILED(gfx::ConvertYCbCrToRGB(
    991          mData, format, size, mapping.GetData(), mapping.GetStride())))) {
    992    MOZ_ASSERT_UNREACHABLE("Failed to convert YUV into RGB data");
    993    return nullptr;
    994  }
    995 
    996  mSourceSurface = surface;
    997 
    998  return surface.forget();
    999 }
   1000 
   1001 PlanarYCbCrImage::~PlanarYCbCrImage() {
   1002  NS_ReleaseOnMainThread("PlanarYCbCrImage::mSourceSurface",
   1003                         mSourceSurface.forget());
   1004 }
   1005 
   1006 NVImage::NVImage() : Image(nullptr, ImageFormat::NV_IMAGE), mBufferSize(0) {}
   1007 
   1008 NVImage::~NVImage() {
   1009  NS_ReleaseOnMainThread("NVImage::mSourceSurface", mSourceSurface.forget());
   1010 }
   1011 
   1012 IntSize NVImage::GetSize() const { return mSize; }
   1013 
   1014 IntRect NVImage::GetPictureRect() const { return mData.mPictureRect; }
   1015 
   1016 already_AddRefed<SourceSurface> NVImage::GetAsSourceSurface() {
   1017  if (mSourceSurface) {
   1018    RefPtr<gfx::SourceSurface> surface(mSourceSurface);
   1019    return surface.forget();
   1020  }
   1021 
   1022  // Convert the current NV12 or NV21 data to YUV420P so that we can follow the
   1023  // logics in PlanarYCbCrImage::GetAsSourceSurface().
   1024  auto ySize = mData.YDataSize();
   1025  auto cbcrSize = mData.CbCrDataSize();
   1026  const int bufferLength =
   1027      ySize.height * mData.mYStride + cbcrSize.height * cbcrSize.width * 2;
   1028  UniquePtr<uint8_t[]> buffer(new uint8_t[bufferLength]);
   1029 
   1030  Data aData = mData;
   1031  aData.mCbCrStride = cbcrSize.width;
   1032  aData.mCbSkip = 0;
   1033  aData.mCrSkip = 0;
   1034  aData.mYChannel = buffer.get();
   1035  aData.mCbChannel = aData.mYChannel + ySize.height * aData.mYStride;
   1036  aData.mCrChannel = aData.mCbChannel + cbcrSize.height * aData.mCbCrStride;
   1037 
   1038  if (mData.mCbChannel < mData.mCrChannel) {  // NV12
   1039    libyuv::NV12ToI420(mData.mYChannel, mData.mYStride, mData.mCbChannel,
   1040                       mData.mCbCrStride, aData.mYChannel, aData.mYStride,
   1041                       aData.mCbChannel, aData.mCbCrStride, aData.mCrChannel,
   1042                       aData.mCbCrStride, ySize.width, ySize.height);
   1043  } else {  // NV21
   1044    libyuv::NV21ToI420(mData.mYChannel, mData.mYStride, mData.mCrChannel,
   1045                       mData.mCbCrStride, aData.mYChannel, aData.mYStride,
   1046                       aData.mCbChannel, aData.mCbCrStride, aData.mCrChannel,
   1047                       aData.mCbCrStride, ySize.width, ySize.height);
   1048  }
   1049 
   1050  // The logics in PlanarYCbCrImage::GetAsSourceSurface().
   1051  gfx::IntSize size(mSize);
   1052  gfx::SurfaceFormat format = gfx::ImageFormatToSurfaceFormat(
   1053      gfxPlatform::GetPlatform()->GetOffscreenFormat());
   1054  gfx::GetYCbCrToRGBDestFormatAndSize(aData, format, size);
   1055  if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
   1056      mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
   1057    NS_ERROR("Illegal image dest width or height");
   1058    return nullptr;
   1059  }
   1060 
   1061  RefPtr<gfx::DataSourceSurface> surface =
   1062      gfx::Factory::CreateDataSourceSurface(size, format);
   1063  if (NS_WARN_IF(!surface)) {
   1064    return nullptr;
   1065  }
   1066 
   1067  DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
   1068  if (NS_WARN_IF(!mapping.IsMapped())) {
   1069    return nullptr;
   1070  }
   1071 
   1072  if (NS_WARN_IF(NS_FAILED(gfx::ConvertYCbCrToRGB(
   1073          aData, format, size, mapping.GetData(), mapping.GetStride())))) {
   1074    MOZ_ASSERT_UNREACHABLE("Failed to convert YUV into RGB data");
   1075    return nullptr;
   1076  }
   1077 
   1078  mSourceSurface = surface;
   1079 
   1080  return surface.forget();
   1081 }
   1082 
   1083 nsresult NVImage::BuildSurfaceDescriptorBuffer(
   1084    SurfaceDescriptorBuffer& aSdBuffer, BuildSdbFlags aFlags,
   1085    const std::function<MemoryOrShmem(uint32_t)>& aAllocate) {
   1086  // Convert the current NV12 or NV21 data to YUV420P so that we can follow the
   1087  // logics in PlanarYCbCrImage::GetAsSourceSurface().
   1088  auto ySize = mData.YDataSize();
   1089  auto cbcrSize = mData.CbCrDataSize();
   1090 
   1091  Data aData = mData;
   1092  aData.mCbCrStride = cbcrSize.width;
   1093  aData.mCbSkip = 0;
   1094  aData.mCrSkip = 0;
   1095  aData.mCbChannel = aData.mYChannel + ySize.height * aData.mYStride;
   1096  aData.mCrChannel = aData.mCbChannel + cbcrSize.height * aData.mCbCrStride;
   1097 
   1098  UniquePtr<uint8_t[]> buffer;
   1099 
   1100  if (!mSourceSurface) {
   1101    const int bufferLength =
   1102        ySize.height * mData.mYStride + cbcrSize.height * cbcrSize.width * 2;
   1103    buffer = MakeUnique<uint8_t[]>(bufferLength);
   1104    aData.mYChannel = buffer.get();
   1105 
   1106    if (mData.mCbChannel < mData.mCrChannel) {  // NV12
   1107      libyuv::NV12ToI420(mData.mYChannel, mData.mYStride, mData.mCbChannel,
   1108                         mData.mCbCrStride, aData.mYChannel, aData.mYStride,
   1109                         aData.mCbChannel, aData.mCbCrStride, aData.mCrChannel,
   1110                         aData.mCbCrStride, ySize.width, ySize.height);
   1111    } else {  // NV21
   1112      libyuv::NV21ToI420(mData.mYChannel, mData.mYStride, mData.mCrChannel,
   1113                         mData.mCbCrStride, aData.mYChannel, aData.mYStride,
   1114                         aData.mCbChannel, aData.mCbCrStride, aData.mCrChannel,
   1115                         aData.mCbCrStride, ySize.width, ySize.height);
   1116    }
   1117  }
   1118 
   1119  // The logics in PlanarYCbCrImage::GetAsSourceSurface().
   1120  gfx::IntSize size(mSize);
   1121  gfx::SurfaceFormat format = gfx::ImageFormatToSurfaceFormat(
   1122      gfxPlatform::GetPlatform()->GetOffscreenFormat());
   1123  gfx::GetYCbCrToRGBDestFormatAndSize(aData, format, size);
   1124  if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
   1125      mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
   1126    NS_ERROR("Illegal image dest width or height");
   1127    return NS_ERROR_FAILURE;
   1128  }
   1129 
   1130  if (mSourceSurface && mSourceSurface->GetSize() != size) {
   1131    return NS_ERROR_NOT_IMPLEMENTED;
   1132  }
   1133 
   1134  uint8_t* output = nullptr;
   1135  int32_t stride = 0;
   1136  nsresult rv = AllocateSurfaceDescriptorBufferRgb(
   1137      size, format, output, aSdBuffer, stride, aAllocate);
   1138  if (NS_WARN_IF(NS_FAILED(rv))) {
   1139    return rv;
   1140  }
   1141 
   1142  if (!mSourceSurface) {
   1143    rv = gfx::ConvertYCbCrToRGB(aData, format, size, output, stride);
   1144    if (NS_WARN_IF(NS_FAILED(rv))) {
   1145      MOZ_ASSERT_UNREACHABLE("Failed to convert YUV into RGB data");
   1146      return rv;
   1147    }
   1148    return NS_OK;
   1149  }
   1150 
   1151  DataSourceSurface::ScopedMap map(mSourceSurface, DataSourceSurface::WRITE);
   1152  if (NS_WARN_IF(!map.IsMapped())) {
   1153    return NS_ERROR_FAILURE;
   1154  }
   1155 
   1156  if (!SwizzleData(map.GetData(), map.GetStride(), mSourceSurface->GetFormat(),
   1157                   output, stride, format, size)) {
   1158    return NS_ERROR_FAILURE;
   1159  }
   1160 
   1161  return NS_OK;
   1162 }
   1163 
   1164 bool NVImage::IsValid() const { return !!mBufferSize; }
   1165 
   1166 uint32_t NVImage::GetBufferSize() const { return mBufferSize; }
   1167 
   1168 NVImage* NVImage::AsNVImage() { return this; };
   1169 
   1170 nsresult NVImage::SetData(const Data& aData) {
   1171  MOZ_ASSERT(aData.mCbSkip == 1 && aData.mCrSkip == 1);
   1172  MOZ_ASSERT((int)std::abs(aData.mCbChannel - aData.mCrChannel) == 1);
   1173 
   1174  // Calculate buffer size
   1175  // Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
   1176  const auto checkedSize =
   1177      CheckedInt<uint32_t>(aData.YDataSize().height) * aData.mYStride +
   1178      CheckedInt<uint32_t>(aData.CbCrDataSize().height) * aData.mCbCrStride;
   1179 
   1180  if (!checkedSize.isValid()) {
   1181    return NS_ERROR_INVALID_ARG;
   1182  }
   1183 
   1184  const auto size = checkedSize.value();
   1185 
   1186  // Allocate a new buffer.
   1187  mBuffer = AllocateBuffer(size);
   1188  if (!mBuffer) {
   1189    return NS_ERROR_OUT_OF_MEMORY;
   1190  }
   1191 
   1192  // Update mBufferSize.
   1193  mBufferSize = size;
   1194 
   1195  // Update mData.
   1196  mData = aData;
   1197  mData.mYChannel = mBuffer.get();
   1198  mData.mCbChannel = mData.mYChannel + (aData.mCbChannel - aData.mYChannel);
   1199  mData.mCrChannel = mData.mYChannel + (aData.mCrChannel - aData.mYChannel);
   1200 
   1201  // Update mSize.
   1202  mSize = aData.mPictureRect.Size();
   1203 
   1204  // Copy the input data into mBuffer.
   1205  // This copies the y-channel and the interleaving CbCr-channel.
   1206  memcpy(mData.mYChannel, aData.mYChannel, mBufferSize);
   1207 
   1208  return NS_OK;
   1209 }
   1210 
   1211 const NVImage::Data* NVImage::GetData() const { return &mData; }
   1212 
   1213 UniquePtr<uint8_t[]> NVImage::AllocateBuffer(uint32_t aSize) {
   1214  UniquePtr<uint8_t[]> buffer(new uint8_t[aSize]);
   1215  return buffer;
   1216 }
   1217 
   1218 SourceSurfaceImage::SourceSurfaceImage(const gfx::IntSize& aSize,
   1219                                       gfx::SourceSurface* aSourceSurface)
   1220    : Image(nullptr, ImageFormat::MOZ2D_SURFACE),
   1221      mSize(aSize),
   1222      mSourceSurface(aSourceSurface),
   1223      mTextureFlags(TextureFlags::DEFAULT) {}
   1224 
   1225 SourceSurfaceImage::SourceSurfaceImage(gfx::SourceSurface* aSourceSurface)
   1226    : Image(nullptr, ImageFormat::MOZ2D_SURFACE),
   1227      mSize(aSourceSurface->GetSize()),
   1228      mSourceSurface(aSourceSurface),
   1229      mTextureFlags(TextureFlags::DEFAULT) {}
   1230 
   1231 SourceSurfaceImage::~SourceSurfaceImage() {
   1232  NS_ReleaseOnMainThread("SourceSurfaceImage::mSourceSurface",
   1233                         mSourceSurface.forget());
   1234 }
   1235 
   1236 TextureClient* SourceSurfaceImage::GetTextureClient(
   1237    KnowsCompositor* aKnowsCompositor) {
   1238  if (!aKnowsCompositor) {
   1239    return nullptr;
   1240  }
   1241 
   1242  return mTextureClients.WithEntryHandle(
   1243      aKnowsCompositor->GetSerial(), [&](auto&& entry) -> TextureClient* {
   1244        if (entry) {
   1245          return entry->get();
   1246        }
   1247 
   1248        RefPtr<TextureClient> textureClient;
   1249        RefPtr<SourceSurface> surface = GetAsSourceSurface();
   1250        MOZ_ASSERT(surface);
   1251        if (surface) {
   1252          // gfx::BackendType::NONE means default to content backend
   1253          textureClient = TextureClient::CreateFromSurface(
   1254              aKnowsCompositor, surface, BackendSelector::Content,
   1255              mTextureFlags, ALLOC_DEFAULT);
   1256        }
   1257        if (textureClient) {
   1258          textureClient->SyncWithObject(aKnowsCompositor->GetSyncObject());
   1259          return entry.Insert(std::move(textureClient)).get();
   1260        }
   1261 
   1262        return nullptr;
   1263      });
   1264 }
   1265 
   1266 ImageContainer::ProducerID ImageContainer::AllocateProducerID() {
   1267  // Callable on all threads.
   1268  static Atomic<ImageContainer::ProducerID> sProducerID(0u);
   1269  return ++sProducerID;
   1270 }
   1271 
   1272 }  // namespace mozilla::layers