tor-browser

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

ExternalTexture.cpp (50731B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "ExternalTexture.h"
      7 
      8 #include "Colorspaces.h"
      9 #include "ImageContainer.h"
     10 #include "mozilla/Assertions.h"
     11 #include "mozilla/ErrorResult.h"
     12 #include "mozilla/dom/HTMLVideoElement.h"
     13 #include "mozilla/dom/VideoFrame.h"
     14 #include "mozilla/dom/WebGPUBinding.h"
     15 #include "mozilla/gfx/Logging.h"
     16 #include "mozilla/gfx/Types.h"
     17 #include "mozilla/layers/ImageDataSerializer.h"
     18 #include "mozilla/layers/LayersSurfaces.h"
     19 #include "mozilla/layers/TextureHost.h"
     20 #include "mozilla/layers/VideoBridgeParent.h"
     21 #include "mozilla/webgpu/Queue.h"
     22 #include "mozilla/webgpu/Utility.h"
     23 #include "mozilla/webgpu/WebGPUChild.h"
     24 #include "mozilla/webgpu/WebGPUParent.h"
     25 #include "nsLayoutUtils.h"
     26 #include "nsPrintfCString.h"
     27 
     28 #ifdef XP_WIN
     29 #  include "mozilla/layers/CompositeProcessD3D11FencesHolderMap.h"
     30 #  include "mozilla/layers/GpuProcessD3D11TextureMap.h"
     31 #  include "mozilla/layers/TextureD3D11.h"
     32 #endif
     33 #ifdef XP_MACOSX
     34 #  include "mozilla/gfx/MacIOSurface.h"
     35 #  include "mozilla/layers/MacIOSurfaceTextureHostOGL.h"
     36 #endif
     37 
     38 namespace mozilla::webgpu {
     39 
     40 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(ExternalTexture, mParent)
     41 GPU_IMPL_JS_WRAP(ExternalTexture)
     42 
     43 ExternalTexture::ExternalTexture(Device* const aParent, RawId aId,
     44                                 RefPtr<ExternalTextureSourceClient> aSource)
     45    : ObjectBase(aParent->GetChild(), aId,
     46                 ffi::wgpu_client_drop_external_texture),
     47      ChildOf(aParent),
     48      mSource(aSource) {}
     49 
     50 ExternalTexture::~ExternalTexture() = default;
     51 
     52 /* static */ already_AddRefed<ExternalTexture> ExternalTexture::Create(
     53    Device* const aParent, const nsString& aLabel,
     54    const RefPtr<ExternalTextureSourceClient>& aSource,
     55    dom::PredefinedColorSpace aColorSpace) {
     56  const webgpu::StringHelper label(aLabel);
     57  const ffi::WGPUPredefinedColorSpace colorSpace =
     58      ConvertPredefinedColorSpace(aColorSpace);
     59  const ffi::WGPUExternalTextureDescriptor desc = {
     60      .label = label.Get(),
     61      .source = aSource ? aSource->GetId() : 0,
     62      .color_space = colorSpace,
     63  };
     64 
     65  const RawId id = ffi::wgpu_client_create_external_texture(
     66      aParent->GetClient(), aParent->GetId(), &desc);
     67 
     68  RefPtr<ExternalTexture> externalTexture =
     69      new ExternalTexture(aParent, id, aSource);
     70  externalTexture->SetLabel(aLabel);
     71 
     72  return externalTexture.forget();
     73 }
     74 
     75 void ExternalTexture::Expire() {
     76  mIsExpired = true;
     77  MaybeDestroy();
     78 }
     79 
     80 void ExternalTexture::Unexpire() {
     81  MOZ_ASSERT(!mIsDestroyed);
     82  MOZ_ASSERT(mSource);
     83  mIsExpired = false;
     84 }
     85 
     86 void ExternalTexture::OnSubmit(uint64_t aSubmissionIndex) {
     87  mLastSubmittedIndex = aSubmissionIndex;
     88 }
     89 
     90 void ExternalTexture::OnSubmittedWorkDone(uint64_t aSubmissionIndex) {
     91  mLastSubmittedWorkDoneIndex = aSubmissionIndex;
     92  MaybeDestroy();
     93 }
     94 
     95 void ExternalTexture::MaybeDestroy() {
     96  if (!mIsDestroyed && mIsExpired &&
     97      mLastSubmittedWorkDoneIndex >= mLastSubmittedIndex) {
     98    mIsDestroyed = true;
     99    mSource = nullptr;
    100    // We could be cleverer and keep the external texture alive until its
    101    // source is destroyed and there's no chance we could want to reuse the
    102    // external texture. But that would complicate the logic and perhaps not
    103    // even gain all that much, as typically attempts to reuse the external
    104    // texture will occur before the previously submitted work is done, so will
    105    // be successful anyway.
    106    ffi::wgpu_client_destroy_external_texture(GetClient(), GetId());
    107  }
    108 }
    109 
    110 RefPtr<ExternalTexture> ExternalTextureCache::GetOrCreate(
    111    Device* aDevice, const dom::GPUExternalTextureDescriptor& aDesc,
    112    ErrorResult& aRv) {
    113  const RefPtr<ExternalTextureSourceClient> source =
    114      GetOrCreateSource(aDevice, aDesc.mSource, aRv);
    115 
    116  if (source) {
    117    return source->GetOrCreateExternalTexture(aDevice, aDesc);
    118  }
    119 
    120  // Create external texture with a null source to indicate error state.
    121  return ExternalTexture::Create(aDevice, aDesc.mLabel, nullptr,
    122                                 aDesc.mColorSpace);
    123 }
    124 
    125 RefPtr<ExternalTextureSourceClient> ExternalTextureCache::GetOrCreateSource(
    126    Device* aDevice, const dom::OwningHTMLVideoElementOrVideoFrame& aSource,
    127    ErrorResult& aRv) {
    128  RefPtr<layers::Image> image;
    129  switch (aSource.GetType()) {
    130    case dom::OwningHTMLVideoElementOrVideoFrame::Type::eHTMLVideoElement:
    131      image = aSource.GetAsHTMLVideoElement()->GetCurrentImage();
    132      break;
    133    case dom::OwningHTMLVideoElementOrVideoFrame::Type::eVideoFrame:
    134      image = aSource.GetAsVideoFrame()->GetImage();
    135      break;
    136  }
    137 
    138  typename decltype(mSources)::AddPtr p;
    139  if (image) {
    140    p = mSources.lookupForAdd(image->GetSerial());
    141    if (p) {
    142      const RefPtr<ExternalTextureSourceClient> source = p->value();
    143      MOZ_ASSERT(source->mImage == image);
    144      return source;
    145    }
    146  }
    147 
    148  // If we didn't find an image above we know this is going to fail, but call
    149  // it anyway so that we can keep all our error handling in one place.
    150  const RefPtr<ExternalTextureSourceClient> source =
    151      ExternalTextureSourceClient::Create(aDevice, this, aSource, aRv);
    152  if (source) {
    153    // If creating the source succeeded, we must have found an image, which
    154    // means we must have a valid AddPtr from above.
    155    // An OOM error in add() just means we don't get to cache the source, but we
    156    // can still proceed.
    157    (void)mSources.add(p, source->mImage->GetSerial(), source);
    158  }
    159  return source;
    160 }
    161 
    162 void ExternalTextureCache::RemoveSource(
    163    const ExternalTextureSourceClient* aSource) {
    164  mSources.remove(aSource->mImage->GetSerial());
    165 }
    166 
    167 ExternalTextureSourceClient::ExternalTextureSourceClient(
    168    WebGPUChild* aChild, RawId aId, ExternalTextureCache* aCache,
    169    const RefPtr<layers::Image>& aImage,
    170    const std::array<RawId, 3>& aTextureIds,
    171    const std::array<RawId, 3>& aViewIds)
    172    : ObjectBase(aChild, aId, ffi::wgpu_client_drop_external_texture_source),
    173      mImage(aImage),
    174      mTextureIds(std::move(aTextureIds)),
    175      mViewIds(std::move(aViewIds)),
    176      mCache(aCache) {
    177  MOZ_RELEASE_ASSERT(aId);
    178 }
    179 
    180 ExternalTextureSourceClient::~ExternalTextureSourceClient() {
    181  if (mCache) {
    182    mCache->RemoveSource(this);
    183  }
    184 
    185  // Call destroy() in addition to drop() to ensure the plane textures are
    186  // destroyed immediately. Otherwise they will remain alive until any external
    187  // textures/bind groups referencing them are garbage collected, which can
    188  // quickly result in excessive memory usage.
    189  ffi::wgpu_client_destroy_external_texture_source(GetClient(), GetId());
    190  // Usually we'd just drop() the textures and views, which would in turn free
    191  // their IDs. However, we don't know which IDs were used by the host to
    192  // actually create textures and views with. Therefore the host side is
    193  // responsible for dropping the textures and views that it actually created,
    194  // but the client side must free all of the IDs that were made.
    195  for (const auto id : mViewIds) {
    196    wgpu_client_free_texture_view_id(GetClient(), id);
    197  }
    198  for (const auto id : mTextureIds) {
    199    wgpu_client_free_texture_id(GetClient(), id);
    200  }
    201 }
    202 
    203 /* static */ already_AddRefed<ExternalTextureSourceClient>
    204 ExternalTextureSourceClient::Create(
    205    Device* aDevice, ExternalTextureCache* aCache,
    206    const dom::OwningHTMLVideoElementOrVideoFrame& aSource, ErrorResult& aRv) {
    207  // Obtain the layers::Image from the HTMLVideoElement or VideoFrame. We use
    208  // nsLayoutUtils::SurfaceFrom*() instead of directly fetching the image, as it
    209  // helps with the security checks below. It also helpfully determines the
    210  // (coded) size, intrinsic size, and crop rect fields for us. Passing
    211  // SFE_ALLOW_UNCROPPED_UNSCALED ensures it does not create a source surface,
    212  // as we are able to handle the cropping and scaling ourself.
    213  const uint32_t flags = nsLayoutUtils::SFE_ALLOW_UNCROPPED_UNSCALED;
    214  SurfaceFromElementResult sfeResult;
    215  VideoRotation rotation;
    216  switch (aSource.GetType()) {
    217    case dom::OwningHTMLVideoElementOrVideoFrame::Type::eHTMLVideoElement: {
    218      const auto& videoElement = aSource.GetAsHTMLVideoElement();
    219      sfeResult = nsLayoutUtils::SurfaceFromElement(videoElement.get(), flags);
    220      rotation = videoElement->RotationDegrees();
    221    } break;
    222    case dom::OwningHTMLVideoElementOrVideoFrame::Type::eVideoFrame: {
    223      const auto& videoFrame = aSource.GetAsVideoFrame();
    224      sfeResult = nsLayoutUtils::SurfaceFromVideoFrame(videoFrame.get(), flags);
    225      rotation = VideoRotation::kDegree_0;
    226    } break;
    227  }
    228 
    229  // If source is not origin-clean, throw a SecurityError and return.
    230  // https://www.w3.org/TR/webgpu/#dom-gpudevice-importexternaltexture
    231  if (!sfeResult.mCORSUsed) {
    232    const nsIGlobalObject* const global = aDevice->GetOwnerGlobal();
    233    nsIPrincipal* const dstPrincipal =
    234        global ? global->PrincipalOrNull() : nullptr;
    235    if (!sfeResult.mPrincipal || !dstPrincipal ||
    236        !dstPrincipal->Subsumes(sfeResult.mPrincipal)) {
    237      aRv.ThrowSecurityError("Cross-origin elements require CORS!");
    238      return nullptr;
    239    }
    240  }
    241  if (sfeResult.mIsWriteOnly) {
    242    aRv.ThrowSecurityError("Write only source data not supported!");
    243    return nullptr;
    244  }
    245 
    246  const auto child = aDevice->GetChild();
    247 
    248  // Let usability be ? check the usability of the image argument(source).
    249  // If usability is not good:
    250  //   1. Generate a validation error.
    251  //   2. Return an invalidated GPUExternalTexture.
    252  // https://www.w3.org/TR/webgpu/#dom-gpudevice-importexternaltexture
    253  const RefPtr<layers::Image> image = sfeResult.mLayersImage;
    254  if (!image) {
    255    ffi::wgpu_report_validation_error(child->GetClient(), aDevice->GetId(),
    256                                      "Video source's usability is bad");
    257    return nullptr;
    258  }
    259 
    260  layers::SurfaceDescriptor sd;
    261  const nsresult rv = image->BuildSurfaceDescriptorGPUVideoOrBuffer(
    262      sd, layers::Image::BuildSdbFlags::Default, Nothing(),
    263      [&](uint32_t aBufferSize) {
    264        ipc::Shmem buffer;
    265        if (!child->AllocShmem(aBufferSize, &buffer)) {
    266          return layers::MemoryOrShmem();
    267        }
    268        return layers::MemoryOrShmem(std::move(buffer));
    269      },
    270      [&](layers::MemoryOrShmem&& aBuffer) {
    271        child->DeallocShmem(aBuffer.get_Shmem());
    272      });
    273  if (NS_FAILED(rv)) {
    274    gfxCriticalErrorOnce() << "BuildSurfaceDescriptorGPUVideoOrBuffer failed";
    275    ffi::wgpu_report_internal_error(
    276        child->GetClient(), aDevice->GetId(),
    277        "BuildSurfaceDescriptorGPUVideoOrBuffer failed");
    278    return nullptr;
    279  }
    280 
    281  const auto sourceId =
    282      ffi::wgpu_client_make_external_texture_source_id(child->GetClient());
    283  // We don't know how many textures or views the host side will need, so make
    284  // enough IDs for up to 3 of each.
    285  const std::array<RawId, 3> textureIds{
    286      ffi::wgpu_client_make_texture_id(child->GetClient()),
    287      ffi::wgpu_client_make_texture_id(child->GetClient()),
    288      ffi::wgpu_client_make_texture_id(child->GetClient()),
    289  };
    290  const std::array<RawId, 3> viewIds{
    291      ffi::wgpu_client_make_texture_view_id(child->GetClient()),
    292      ffi::wgpu_client_make_texture_view_id(child->GetClient()),
    293      ffi::wgpu_client_make_texture_view_id(child->GetClient()),
    294  };
    295 
    296  // The actual size of the surface (possibly including non-visible padding).
    297  // This has not been adjusted for any rotation.
    298  const gfx::IntSize codedSize = sfeResult.mSize;
    299  // The crop rectangle to be displayed, defaulting to the full surface if not
    300  // provided. This is relative to the coded size, and again has not been
    301  // adjusted for any rotation.
    302  const gfx::IntRect cropRect =
    303      sfeResult.mCropRect.valueOr(gfx::IntRect({}, codedSize));
    304  // The size the surface is intended to be rendered at. We use this for the
    305  // external texture descriptor's size field which will be the size reported
    306  // to web content, eg via WGSL's `textureDimensions()` builtin. This has had
    307  // rotation taken into account.
    308  const gfx::IntSize intrinsicSize = sfeResult.mIntrinsicSize;
    309 
    310  // Calculate the sample transform, starting with the rotation. As only 90
    311  // degree increments are supported, we hard-code the values to avoid
    312  // expensive trig and to keep the numbers precise. If/when we support flips
    313  // we'd handle that here too.
    314  gfx::Matrix sampleTransform;
    315  switch (rotation) {
    316    case VideoRotation::kDegree_0:
    317      break;
    318    case VideoRotation::kDegree_90:
    319      sampleTransform = gfx::Matrix(0.0, -1.0, 1.0, 0.0, 0.0, 1.0);
    320      break;
    321    case VideoRotation::kDegree_180:
    322      sampleTransform = gfx::Matrix(-1.0, 0.0, 0.0, -1.0, 1.0, 1.0);
    323      break;
    324    case VideoRotation::kDegree_270:
    325      sampleTransform = gfx::Matrix(0.0, 1.0, -1.0, 0.0, 1.0, 0.0);
    326      break;
    327  }
    328 
    329  // Scale and translate to account for the crop rect. We need to ensure that
    330  // the normalized coordinates (0,0)..(1,1) map to the crop rect rather than
    331  // the coded size. We must therefore normalize the crop rect by dividing by
    332  // the coded size, then scale and translate the transform based on the
    333  // normalized crop rect. We apply these transformations pre-rotation as the
    334  // crop rect itself is expressed pre-rotation. Note the intrinsic size is
    335  // irrelevant here as we are dealing with normalized coordinates.
    336  gfx::Rect normalizedCropRect = gfx::Rect(cropRect);
    337  normalizedCropRect.Scale(1.0 / static_cast<float>(codedSize.width),
    338                           1.0 / static_cast<float>(codedSize.height));
    339  sampleTransform.PreTranslate(normalizedCropRect.x, normalizedCropRect.y);
    340  sampleTransform.PreScale(normalizedCropRect.Width(),
    341                           normalizedCropRect.Height());
    342 
    343  // Derive the load transform from the sample transform. Texture loads accept
    344  // unnormalized texel coordinates ranging from (0,0) to the intrinsic size
    345  // minus one, i.e. based on the size the external texture reports itself as
    346  // to web content. We need to map these to our rotated crop rect, and the end
    347  // result must be texel coordinates based on the actual texture size. This
    348  // can be achieved by first normalizing the coordinates by dividing by the
    349  // intrinsic size minus one, then applying the sample transformation, then
    350  // unnormalizing the transformed coordinates by multiplying by the actual
    351  // texture size minus one.
    352  gfx::Matrix loadTransform = sampleTransform;
    353  loadTransform.PreScale(
    354      1.0 / static_cast<float>(std::max(intrinsicSize.width - 1, 1)),
    355      1.0 / static_cast<float>(std::max(intrinsicSize.height - 1, 1)));
    356  loadTransform.PostScale(static_cast<float>(codedSize.width - 1),
    357                          static_cast<float>(codedSize.height - 1));
    358 
    359  const ExternalTextureSourceDescriptor sourceDesc = {
    360      .mTextureIds = textureIds,
    361      .mViewIds = viewIds,
    362      .mSurfaceDescriptor = std::move(sd),
    363      .mSize = intrinsicSize,
    364      .mSampleTransform = {sampleTransform._11, sampleTransform._12,
    365                           sampleTransform._21, sampleTransform._22,
    366                           sampleTransform._31, sampleTransform._32},
    367      .mLoadTransform = {loadTransform._11, loadTransform._12,
    368                         loadTransform._21, loadTransform._22,
    369                         loadTransform._31, loadTransform._32},
    370  };
    371 
    372  // We use a separate IPDL message than Messages() so that IPDL can handle the
    373  // SurfaceDescriptor (de)serialization for us. We must therefore flush any
    374  // queued messages first so that they are processed in the correct order.
    375  child->FlushQueuedMessages();
    376  child->SendCreateExternalTextureSource(
    377      aDevice->GetId(), aDevice->GetQueue()->GetId(), sourceId, sourceDesc);
    378 
    379  RefPtr<ExternalTextureSourceClient> source = new ExternalTextureSourceClient(
    380      child, sourceId, aCache, image, textureIds, viewIds);
    381  return source.forget();
    382 }
    383 
    384 RefPtr<ExternalTexture> ExternalTextureSourceClient::GetOrCreateExternalTexture(
    385    Device* aDevice, const dom::GPUExternalTextureDescriptor& aDesc) {
    386  auto p = mExternalTextures.lookupForAdd(aDesc.mColorSpace);
    387  if (p) {
    388    if (auto* const externalTexture = p->value().get()) {
    389      if (!externalTexture->IsDestroyed()) {
    390        externalTexture->Unexpire();
    391        return externalTexture;
    392      }
    393    }
    394  }
    395 
    396  const RefPtr<ExternalTexture> externalTexture =
    397      ExternalTexture::Create(aDevice, aDesc.mLabel, this, aDesc.mColorSpace);
    398 
    399  if (externalTexture) {
    400    if (p) {
    401      p->value() = externalTexture;
    402    } else {
    403      // OOM error in add() just means we don't get to cache the external
    404      // texture, but we can still proceed.
    405      (void)mExternalTextures.add(p, aDesc.mColorSpace, externalTexture);
    406    }
    407  }
    408 
    409  return externalTexture;
    410 }
    411 
    412 ExternalTextureSourceHost::ExternalTextureSourceHost(
    413    Span<const RawId> aTextureIds, Span<const RawId> aViewIds,
    414    gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
    415    gfx::YUVRangedColorSpace aColorSpace,
    416    const std::array<float, 6>& aSampleTransform,
    417    const std::array<float, 6>& aLoadTransform)
    418    : mSize(aSize),
    419      mFormat(aFormat),
    420      mColorSpace(aColorSpace),
    421      mSampleTransform(aSampleTransform),
    422      mLoadTransform(aLoadTransform) {
    423  mTextureIds.AppendElements(aTextureIds);
    424  mViewIds.AppendElements(aViewIds);
    425 }
    426 
    427 /* static */ ExternalTextureSourceHost ExternalTextureSourceHost::Create(
    428    WebGPUParent* aParent, RawId aDeviceId, RawId aQueueId,
    429    const ExternalTextureSourceDescriptor& aDesc) {
    430  const auto& sd = aDesc.mSurfaceDescriptor;
    431  switch (sd.type()) {
    432    case layers::SurfaceDescriptor::TSurfaceDescriptorBuffer: {
    433      const layers::SurfaceDescriptorBuffer& bufferDesc =
    434          sd.get_SurfaceDescriptorBuffer();
    435      ipc::Shmem& bufferShmem = bufferDesc.data().get_Shmem();
    436      auto source =
    437          CreateFromBufferDesc(aParent, aDeviceId, aQueueId, aDesc,
    438                               bufferDesc.desc(), bufferShmem.Range<uint8_t>());
    439      aParent->DeallocShmem(bufferShmem);
    440      return source;
    441    } break;
    442 
    443    case layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo: {
    444      const layers::SurfaceDescriptorGPUVideo& gpuVideoDesc =
    445          sd.get_SurfaceDescriptorGPUVideo();
    446      const layers::SurfaceDescriptorRemoteDecoder& remoteDecoderDesc =
    447          gpuVideoDesc.get_SurfaceDescriptorRemoteDecoder();
    448 
    449      const auto videoBridge =
    450          layers::VideoBridgeParent::GetSingleton(remoteDecoderDesc.source());
    451      if (!videoBridge) {
    452        gfxCriticalErrorOnce() << "Failed to get VideoBridge";
    453        aParent->ReportError(aDeviceId, dom::GPUErrorFilter::Internal,
    454                             "Failed to get VideoBridge"_ns);
    455        return CreateError();
    456      }
    457      const RefPtr<layers::TextureHost> textureHost =
    458          videoBridge->LookupTexture(aParent->mContentId,
    459                                     remoteDecoderDesc.handle());
    460      if (!textureHost) {
    461        gfxCriticalErrorOnce() << "Failed to lookup remote decoder texture";
    462        aParent->ReportError(aDeviceId, dom::GPUErrorFilter::Internal,
    463                             "Failed to lookup remote decoder texture"_ns);
    464        return CreateError();
    465      }
    466 
    467      if (const auto* bufferHost = textureHost->AsBufferTextureHost()) {
    468        return CreateFromBufferDesc(
    469            aParent, aDeviceId, aQueueId, aDesc,
    470            bufferHost->GetBufferDescriptor(),
    471            Span(bufferHost->GetBuffer(), bufferHost->GetBufferSize()));
    472      } else if (const auto* dxgiHost = textureHost->AsDXGITextureHostD3D11()) {
    473        return CreateFromDXGITextureHost(aParent, aDeviceId, aQueueId, aDesc,
    474                                         dxgiHost);
    475      } else if (const auto* dxgiYCbCrHost =
    476                     textureHost->AsDXGIYCbCrTextureHostD3D11()) {
    477        return CreateFromDXGIYCbCrTextureHost(aParent, aDeviceId, aQueueId,
    478                                              aDesc, dxgiYCbCrHost);
    479      } else if (const auto* ioSurfHost =
    480                     textureHost->AsMacIOSurfaceTextureHost()) {
    481        return CreateFromMacIOSurfaceTextureHost(aParent, aDeviceId, aDesc,
    482                                                 ioSurfHost);
    483      } else {
    484        gfxCriticalErrorOnce()
    485            << "Unexpected SurfaceDescriptorGPUVideo TextureHost type";
    486        aParent->ReportError(
    487            aDeviceId, dom::GPUErrorFilter::Internal,
    488            "Unexpected SurfaceDescriptorGPUVideo TextureHost type"_ns);
    489        return CreateError();
    490      }
    491    } break;
    492    default:
    493      gfxCriticalErrorOnce()
    494          << "Unexpected SurfaceDescriptor type: " << sd.type();
    495      aParent->ReportError(
    496          aDeviceId, dom::GPUErrorFilter::Internal,
    497          nsPrintfCString("Unexpected SurfaceDescriptor type: %d", sd.type()));
    498      return CreateError();
    499  }
    500  return CreateError();
    501 }
    502 
    503 /* static */ ExternalTextureSourceHost
    504 ExternalTextureSourceHost::CreateFromBufferDesc(
    505    WebGPUParent* aParent, RawId aDeviceId, RawId aQueueId,
    506    const ExternalTextureSourceDescriptor& aDesc,
    507    const layers::BufferDescriptor& aBufferDesc, Span<uint8_t> aBuffer) {
    508  const gfx::SurfaceFormat format =
    509      layers::ImageDataSerializer::FormatFromBufferDescriptor(aBufferDesc);
    510  // Creates a texture and view for a single plane, and writes the provided data
    511  // to the texture.
    512  auto createPlane = [aParent, aDeviceId, aQueueId](
    513                         RawId texId, RawId viewId,
    514                         ffi::WGPUTextureFormat format, gfx::IntSize size,
    515                         Span<uint8_t> buffer, uint32_t stride) {
    516    const ffi::WGPUTextureDescriptor textureDesc{
    517        .size =
    518            ffi::WGPUExtent3d{
    519                .width = static_cast<uint32_t>(size.width),
    520                .height = static_cast<uint32_t>(size.height),
    521                .depth_or_array_layers = 1,
    522            },
    523        .mip_level_count = 1,
    524        .sample_count = 1,
    525        .dimension = ffi::WGPUTextureDimension_D2,
    526        .format = format,
    527        .usage = WGPUTextureUsages_TEXTURE_BINDING | WGPUTextureUsages_COPY_DST,
    528        .view_formats = {},
    529    };
    530 
    531    {
    532      ErrorBuffer error;
    533      ffi::wgpu_server_device_create_texture(
    534          aParent->GetContext(), aDeviceId, texId, &textureDesc, error.ToFFI());
    535      // Since we have full control over the creation of this texture, any
    536      // validation error we encounter should be treated as an internal error.
    537      error.CoerceValidationToInternal();
    538      aParent->ForwardError(error);
    539    }
    540 
    541    const ffi::WGPUTexelCopyTextureInfo dest{
    542        .texture = texId,
    543        .mip_level = 0,
    544        .origin = {},
    545        .aspect = ffi::WGPUTextureAspect_All,
    546    };
    547 
    548    const ffi::WGPUTexelCopyBufferLayout layout{
    549        .offset = 0,
    550        .bytes_per_row = &stride,
    551        .rows_per_image = nullptr,
    552    };
    553    const Span<uint8_t> slice = buffer.to(size.height * stride);
    554    const ffi::WGPUFfiSlice_u8 data{
    555        .data = slice.data(),
    556        .length = slice.size(),
    557    };
    558    {
    559      ErrorBuffer error;
    560      ffi::wgpu_server_queue_write_texture(aParent->GetContext(), aDeviceId,
    561                                           aQueueId, &dest, data, &layout,
    562                                           &textureDesc.size, error.ToFFI());
    563      error.CoerceValidationToInternal();
    564      aParent->ForwardError(error);
    565    }
    566 
    567    const ffi::WGPUTextureViewDescriptor viewDesc{};
    568    {
    569      ErrorBuffer error;
    570      ffi::wgpu_server_texture_create_view(aParent->GetContext(), aDeviceId,
    571                                           texId, viewId, &viewDesc,
    572                                           error.ToFFI());
    573      error.CoerceValidationToInternal();
    574      aParent->ForwardError(error);
    575    }
    576  };
    577 
    578  AutoTArray<RawId, 3> usedTextureIds;
    579  AutoTArray<RawId, 3> usedViewIds;
    580  gfx::YUVRangedColorSpace colorSpace;
    581  switch (aBufferDesc.type()) {
    582    case layers::BufferDescriptor::TRGBDescriptor: {
    583      const layers::RGBDescriptor& rgbDesc = aBufferDesc.get_RGBDescriptor();
    584      ffi::WGPUTextureFormat planeFormat;
    585      switch (rgbDesc.format()) {
    586        case gfx::SurfaceFormat::B8G8R8A8:
    587        case gfx::SurfaceFormat::B8G8R8X8:
    588          planeFormat = {ffi::WGPUTextureFormat_Bgra8Unorm};
    589          break;
    590        case gfx::SurfaceFormat::R8G8B8A8:
    591        case gfx::SurfaceFormat::R8G8B8X8:
    592          planeFormat = {ffi::WGPUTextureFormat_Rgba8Unorm};
    593          break;
    594        default:
    595          gfxCriticalErrorOnce()
    596              << "Unexpected RGBDescriptor format: " << rgbDesc.format();
    597          aParent->ReportError(
    598              aDeviceId, dom::GPUErrorFilter::Internal,
    599              nsPrintfCString("Unexpected RGBDescriptor format: %s",
    600                              mozilla::ToString(rgbDesc.format()).c_str()));
    601          return CreateError();
    602      }
    603      createPlane(aDesc.mTextureIds[0], aDesc.mViewIds[0], planeFormat,
    604                  rgbDesc.size(), aBuffer,
    605                  layers::ImageDataSerializer::GetRGBStride(rgbDesc));
    606      usedTextureIds.AppendElement(aDesc.mTextureIds[0]);
    607      usedViewIds.AppendElement(aDesc.mViewIds[0]);
    608      colorSpace = gfx::YUVRangedColorSpace::GbrIdentity;
    609    } break;
    610    case layers::BufferDescriptor::TYCbCrDescriptor: {
    611      const layers::YCbCrDescriptor& yCbCrDesc =
    612          aBufferDesc.get_YCbCrDescriptor();
    613      const gfx::IntSize ySize =
    614          layers::ImageDataSerializer::SizeFromBufferDescriptor(aBufferDesc);
    615      const gfx::IntSize cbCrSize =
    616          layers::ImageDataSerializer::GetCroppedCbCrSize(aBufferDesc);
    617 
    618      ffi::WGPUTextureFormat planeFormat;
    619      switch (yCbCrDesc.colorDepth()) {
    620        case gfx::ColorDepth::COLOR_8:
    621          planeFormat = {ffi::WGPUTextureFormat_R8Unorm};
    622          break;
    623        case gfx::ColorDepth::COLOR_10:
    624        case gfx::ColorDepth::COLOR_12:
    625        case gfx::ColorDepth::COLOR_16:
    626          gfxCriticalNoteOnce << "Unsupported color depth: "
    627                              << yCbCrDesc.colorDepth();
    628          aParent->ReportError(
    629              aDeviceId, dom::GPUErrorFilter::Internal,
    630              nsPrintfCString(
    631                  "Unsupported color depth: %s",
    632                  mozilla::ToString(yCbCrDesc.colorDepth()).c_str()));
    633          return CreateError();
    634      }
    635 
    636      createPlane(aDesc.mTextureIds[0], aDesc.mViewIds[0], planeFormat, ySize,
    637                  aBuffer.from(yCbCrDesc.yOffset()), yCbCrDesc.yStride());
    638      createPlane(aDesc.mTextureIds[1], aDesc.mViewIds[1], planeFormat,
    639                  cbCrSize, aBuffer.from(yCbCrDesc.cbOffset()),
    640                  yCbCrDesc.cbCrStride());
    641      createPlane(aDesc.mTextureIds[2], aDesc.mViewIds[2], planeFormat,
    642                  cbCrSize, aBuffer.from(yCbCrDesc.crOffset()),
    643                  yCbCrDesc.cbCrStride());
    644      usedTextureIds.AppendElements(aDesc.mTextureIds.data(),
    645                                    aDesc.mTextureIds.size());
    646      usedViewIds.AppendElements(aDesc.mViewIds.data(), aDesc.mViewIds.size());
    647      colorSpace = gfx::ToYUVRangedColorSpace(yCbCrDesc.yUVColorSpace(),
    648                                              yCbCrDesc.colorRange());
    649    } break;
    650    case layers::BufferDescriptor::T__None: {
    651      gfxCriticalErrorOnce() << "Invalid BufferDescriptor";
    652      aParent->ReportError(aDeviceId, dom::GPUErrorFilter::Internal,
    653                           "Invalid BufferDescriptor"_ns);
    654      return CreateError();
    655    } break;
    656  }
    657 
    658  return ExternalTextureSourceHost(usedTextureIds, usedViewIds, aDesc.mSize,
    659                                   format, colorSpace, aDesc.mSampleTransform,
    660                                   aDesc.mLoadTransform);
    661 }
    662 
    663 /* static */ ExternalTextureSourceHost
    664 ExternalTextureSourceHost::CreateError() {
    665  return ExternalTextureSourceHost(
    666      {}, {}, gfx::IntSize(0, 0), gfx::SurfaceFormat::R8G8B8A8,
    667      gfx::YUVRangedColorSpace::GbrIdentity, {}, {});
    668 }
    669 
    670 /* static */ ExternalTextureSourceHost
    671 ExternalTextureSourceHost::CreateFromDXGITextureHost(
    672    WebGPUParent* aParent, RawId aDeviceId, RawId aQueueId,
    673    const ExternalTextureSourceDescriptor& aDesc,
    674    const layers::DXGITextureHostD3D11* aTextureHost) {
    675 #ifdef XP_WIN
    676  Maybe<HANDLE> handle;
    677  if (aTextureHost->mGpuProcessTextureId) {
    678    auto* textureMap = layers::GpuProcessD3D11TextureMap::Get();
    679    if (textureMap) {
    680      handle =
    681          textureMap->GetSharedHandle(aTextureHost->mGpuProcessTextureId.ref());
    682    }
    683  } else if (aTextureHost->mHandle) {
    684    handle.emplace(aTextureHost->mHandle->GetHandle());
    685  }
    686 
    687  if (!handle) {
    688    gfxCriticalErrorOnce() << "Failed to obtain D3D texture handle";
    689    aParent->ReportError(aDeviceId, dom::GPUErrorFilter::Internal,
    690                         "Failed to obtain D3D texture handle"_ns);
    691    return CreateError();
    692  }
    693 
    694  const gfx::YUVRangedColorSpace colorSpace = gfx::ToYUVRangedColorSpace(
    695      gfx::ToYUVColorSpace(aTextureHost->mColorSpace),
    696      aTextureHost->mColorRange);
    697 
    698  ffi::WGPUTextureFormat textureFormat;
    699  AutoTArray<std::pair<ffi::WGPUTextureFormat, ffi::WGPUTextureAspect>, 2>
    700      viewFormatAndAspects;
    701  switch (aTextureHost->mFormat) {
    702    case gfx::SurfaceFormat::R8G8B8A8:
    703    case gfx::SurfaceFormat::R8G8B8X8:
    704      textureFormat = {ffi::WGPUTextureFormat_Rgba8Unorm};
    705      viewFormatAndAspects.AppendElement(
    706          std::make_pair(textureFormat, ffi::WGPUTextureAspect_All));
    707      break;
    708    case gfx::SurfaceFormat::B8G8R8A8:
    709    case gfx::SurfaceFormat::B8G8R8X8:
    710      textureFormat = {ffi::WGPUTextureFormat_Bgra8Unorm};
    711      viewFormatAndAspects.AppendElement(
    712          std::make_pair(textureFormat, ffi::WGPUTextureAspect_All));
    713      break;
    714    case gfx::SurfaceFormat::NV12:
    715      textureFormat = {ffi::WGPUTextureFormat_NV12};
    716      viewFormatAndAspects.AppendElement(
    717          std::make_pair(ffi::WGPUTextureFormat{ffi::WGPUTextureFormat_R8Unorm},
    718                         ffi::WGPUTextureAspect_Plane0));
    719      viewFormatAndAspects.AppendElement(std::make_pair(
    720          ffi::WGPUTextureFormat{ffi::WGPUTextureFormat_Rg8Unorm},
    721          ffi::WGPUTextureAspect_Plane1));
    722      break;
    723    case gfx::SurfaceFormat::P010:
    724      textureFormat = {ffi::WGPUTextureFormat_P010};
    725      viewFormatAndAspects.AppendElement(std::make_pair(
    726          ffi::WGPUTextureFormat{ffi::WGPUTextureFormat_R16Unorm},
    727          ffi::WGPUTextureAspect_Plane0));
    728      viewFormatAndAspects.AppendElement(std::make_pair(
    729          ffi::WGPUTextureFormat{ffi::WGPUTextureFormat_Rg16Unorm},
    730          ffi::WGPUTextureAspect_Plane1));
    731      break;
    732    default:
    733      gfxCriticalNoteOnce << "Unsupported surface format: "
    734                          << aTextureHost->mFormat;
    735      aParent->ReportError(
    736          aDeviceId, dom::GPUErrorFilter::Internal,
    737          nsPrintfCString("Unsupported surface format: %s",
    738                          mozilla::ToString(aTextureHost->mFormat).c_str()));
    739      return CreateError();
    740  }
    741 
    742  AutoTArray<RawId, 1> usedTextureIds = {aDesc.mTextureIds[0]};
    743  AutoTArray<RawId, 2> usedViewIds;
    744 
    745  const ffi::WGPUTextureDescriptor textureDesc{
    746      .size =
    747          ffi::WGPUExtent3d{
    748              .width = static_cast<uint32_t>(aTextureHost->mSize.width),
    749              .height = static_cast<uint32_t>(aTextureHost->mSize.height),
    750              .depth_or_array_layers = 1,
    751          },
    752      .mip_level_count = 1,
    753      .sample_count = 1,
    754      .dimension = ffi::WGPUTextureDimension_D2,
    755      .format = textureFormat,
    756      .usage = WGPUTextureUsages_TEXTURE_BINDING,
    757      .view_formats = {},
    758  };
    759  {
    760    ErrorBuffer error;
    761    ffi::wgpu_server_device_import_texture_from_shared_handle(
    762        aParent->GetContext(), aDeviceId, usedTextureIds[0], &textureDesc,
    763        *handle, error.ToFFI());
    764    // From here on there's no need to return early with `CreateError()` in
    765    // case of an error, as an error creating a texture or view will be
    766    // propagated to any views or external textures created from them.
    767    // Since we have full control over the creation of this texture, any
    768    // validation error we encounter should be treated as an internal error.
    769    error.CoerceValidationToInternal();
    770    aParent->ForwardError(error);
    771  }
    772 
    773  for (size_t i = 0; i < viewFormatAndAspects.Length(); i++) {
    774    auto [format, aspect] = viewFormatAndAspects[i];
    775    ffi::WGPUTextureViewDescriptor viewDesc{
    776        .format = &format,
    777        .aspect = aspect,
    778    };
    779    {
    780      ErrorBuffer error;
    781      ffi::wgpu_server_texture_create_view(aParent->GetContext(), aDeviceId,
    782                                           usedTextureIds[0], aDesc.mViewIds[i],
    783                                           &viewDesc, error.ToFFI());
    784      error.CoerceValidationToInternal();
    785      aParent->ForwardError(error);
    786    }
    787    usedViewIds.AppendElement(aDesc.mViewIds[i]);
    788  }
    789  ExternalTextureSourceHost source(
    790      usedTextureIds, usedViewIds, aDesc.mSize, aTextureHost->mFormat,
    791      colorSpace, aDesc.mSampleTransform, aDesc.mLoadTransform);
    792  source.mFenceId = aTextureHost->mFencesHolderId;
    793  return source;
    794 #else
    795  MOZ_CRASH();
    796 #endif
    797 }
    798 
    799 /* static */ ExternalTextureSourceHost
    800 ExternalTextureSourceHost::CreateFromDXGIYCbCrTextureHost(
    801    WebGPUParent* aParent, RawId aDeviceId, RawId aQueueId,
    802    const ExternalTextureSourceDescriptor& aDesc,
    803    const layers::DXGIYCbCrTextureHostD3D11* aTextureHost) {
    804 #ifdef XP_WIN
    805  const gfx::YUVRangedColorSpace colorSpace = gfx::ToYUVRangedColorSpace(
    806      aTextureHost->mYUVColorSpace, aTextureHost->mColorRange);
    807 
    808  ffi::WGPUTextureFormat planeFormat;
    809  switch (aTextureHost->mColorDepth) {
    810    case gfx::ColorDepth::COLOR_8:
    811      planeFormat = {ffi::WGPUTextureFormat_R8Unorm};
    812      break;
    813    case gfx::ColorDepth::COLOR_10:
    814    case gfx::ColorDepth::COLOR_12:
    815    case gfx::ColorDepth::COLOR_16:
    816      gfxCriticalNoteOnce << "Unsupported color depth: "
    817                          << aTextureHost->mColorDepth;
    818      aParent->ReportError(
    819          aDeviceId, dom::GPUErrorFilter::Internal,
    820          nsPrintfCString(
    821              "Unsupported color depth: %s",
    822              mozilla::ToString(aTextureHost->mColorDepth).c_str()));
    823      return CreateError();
    824  }
    825 
    826  for (int i = 0; i < 3; i++) {
    827    {
    828      const auto size = i == 0 ? aTextureHost->mSizeY : aTextureHost->mSizeCbCr;
    829      const ffi::WGPUTextureDescriptor textureDesc{
    830          .size =
    831              ffi::WGPUExtent3d{
    832                  .width = static_cast<uint32_t>(size.width),
    833                  .height = static_cast<uint32_t>(size.height),
    834                  .depth_or_array_layers = 1,
    835              },
    836          .mip_level_count = 1,
    837          .sample_count = 1,
    838          .dimension = ffi::WGPUTextureDimension_D2,
    839          .format = planeFormat,
    840          .usage = WGPUTextureUsages_TEXTURE_BINDING,
    841          .view_formats = {},
    842      };
    843      ErrorBuffer error;
    844      ffi::wgpu_server_device_import_texture_from_shared_handle(
    845          aParent->GetContext(), aDeviceId, aDesc.mTextureIds[i], &textureDesc,
    846          aTextureHost->mHandles[i]->GetHandle(), error.ToFFI());
    847      // From here on there's no need to return early with `CreateError()` in
    848      // case of an error, as an error creating a texture or view will be
    849      // propagated to any views or external textures created from them.
    850      // Since we have full control over the creation of this texture, any
    851      // validation error we encounter should be treated as an internal error.
    852      error.CoerceValidationToInternal();
    853      aParent->ForwardError(error);
    854    }
    855    {
    856      ffi::WGPUTextureViewDescriptor viewDesc{};
    857      ErrorBuffer error;
    858      ffi::wgpu_server_texture_create_view(
    859          aParent->GetContext(), aDeviceId, aDesc.mTextureIds[i],
    860          aDesc.mViewIds[i], &viewDesc, error.ToFFI());
    861      error.CoerceValidationToInternal();
    862      aParent->ForwardError(error);
    863    }
    864  }
    865 
    866  ExternalTextureSourceHost source(
    867      aDesc.mTextureIds, aDesc.mViewIds, aDesc.mSize, aTextureHost->GetFormat(),
    868      colorSpace, aDesc.mSampleTransform, aDesc.mLoadTransform);
    869  source.mFenceId = Some(aTextureHost->mFencesHolderId);
    870  return source;
    871 #else
    872  MOZ_CRASH();
    873 #endif
    874 }
    875 
    876 /* static */ ExternalTextureSourceHost
    877 ExternalTextureSourceHost::CreateFromMacIOSurfaceTextureHost(
    878    WebGPUParent* aParent, RawId aDeviceId,
    879    const ExternalTextureSourceDescriptor& aDesc,
    880    const layers::MacIOSurfaceTextureHostOGL* aTextureHost) {
    881 #ifdef XP_MACOSX
    882  const RefPtr<MacIOSurface> ioSurface = aTextureHost->mSurface;
    883  if (!ioSurface) {
    884    gfxCriticalErrorOnce() << "Failed to lookup MacIOSurface";
    885    aParent->ReportError(aDeviceId, dom::GPUErrorFilter::Internal,
    886                         "Failed to lookup MacIOSurface"_ns);
    887 
    888    return CreateError();
    889  }
    890 
    891  // mGpuFence should be null. It is only required to synchronize GPU reads
    892  // from an IOSurface following GPU writes, e.g. when an IOSurface is used for
    893  // WebGPU presentation. In our case the IOSurface has been written to from
    894  // the CPU or obtained from a CVPixelBuffer, and no additional synchronization
    895  // is required.
    896  MOZ_ASSERT(!aTextureHost->mGpuFence);
    897 
    898  const gfx::SurfaceFormat format = ioSurface->GetFormat();
    899  const gfx::YUVRangedColorSpace colorSpace = gfx::ToYUVRangedColorSpace(
    900      ioSurface->GetYUVColorSpace(), ioSurface->GetColorRange());
    901 
    902  auto planeSize = [ioSurface](auto plane) {
    903    return ffi::WGPUExtent3d{
    904        .width = static_cast<uint32_t>(ioSurface->GetDevicePixelWidth(plane)),
    905        .height = static_cast<uint32_t>(ioSurface->GetDevicePixelHeight(plane)),
    906        .depth_or_array_layers = 1,
    907    };
    908  };
    909  auto yuvPlaneFormat =
    910      [ioSurface](auto numComponents) -> ffi::WGPUTextureFormat {
    911    switch (numComponents) {
    912      case 1:
    913        switch (ioSurface->GetColorDepth()) {
    914          case gfx::ColorDepth::COLOR_8:
    915            return {ffi::WGPUTextureFormat_R8Unorm};
    916          case gfx::ColorDepth::COLOR_10:
    917          case gfx::ColorDepth::COLOR_12:
    918          case gfx::ColorDepth::COLOR_16:
    919            return {ffi::WGPUTextureFormat_R16Unorm};
    920        }
    921      case 2:
    922        switch (ioSurface->GetColorDepth()) {
    923          case gfx::ColorDepth::COLOR_8:
    924            return {ffi::WGPUTextureFormat_Rg8Unorm};
    925          case gfx::ColorDepth::COLOR_10:
    926          case gfx::ColorDepth::COLOR_12:
    927          case gfx::ColorDepth::COLOR_16:
    928            return {ffi::WGPUTextureFormat_Rg16Unorm};
    929        }
    930      default:
    931        MOZ_CRASH("Invalid numComponents");
    932    }
    933  };
    934 
    935  AutoTArray<ffi::WGPUTextureDescriptor, 2> textureDescs;
    936  switch (format) {
    937    case gfx::SurfaceFormat::R8G8B8A8:
    938    case gfx::SurfaceFormat::R8G8B8X8:
    939      textureDescs.AppendElement(ffi::WGPUTextureDescriptor{
    940          .size = planeSize(0),
    941          .mip_level_count = 1,
    942          .sample_count = 1,
    943          .dimension = ffi::WGPUTextureDimension_D2,
    944          .format = {ffi::WGPUTextureFormat_Rgba8Unorm},
    945          .usage = WGPUTextureUsages_TEXTURE_BINDING,
    946          .view_formats = {},
    947      });
    948      break;
    949    case gfx::SurfaceFormat::B8G8R8A8:
    950    case gfx::SurfaceFormat::B8G8R8X8:
    951      textureDescs.AppendElement(ffi::WGPUTextureDescriptor{
    952          .size = planeSize(0),
    953          .mip_level_count = 1,
    954          .sample_count = 1,
    955          .dimension = ffi::WGPUTextureDimension_D2,
    956          .format = {ffi::WGPUTextureFormat_Bgra8Unorm},
    957          .usage = WGPUTextureUsages_TEXTURE_BINDING,
    958          .view_formats = {},
    959      });
    960      break;
    961    case gfx::SurfaceFormat::NV12:
    962    case gfx::SurfaceFormat::P010: {
    963      textureDescs.AppendElement(ffi::WGPUTextureDescriptor{
    964          .size = planeSize(0),
    965          .mip_level_count = 1,
    966          .sample_count = 1,
    967          .dimension = ffi::WGPUTextureDimension_D2,
    968          .format = yuvPlaneFormat(1),
    969          .usage = WGPUTextureUsages_TEXTURE_BINDING,
    970          .view_formats = {},
    971      });
    972      textureDescs.AppendElement(ffi::WGPUTextureDescriptor{
    973          .size = planeSize(1),
    974          .mip_level_count = 1,
    975          .sample_count = 1,
    976          .dimension = ffi::WGPUTextureDimension_D2,
    977          .format = yuvPlaneFormat(2),
    978          .usage = WGPUTextureUsages_TEXTURE_BINDING,
    979          .view_formats = {},
    980      });
    981    } break;
    982    default:
    983      gfxCriticalErrorOnce() << "Unsupported IOSurface format: " << format;
    984      aParent->ReportError(aDeviceId, dom::GPUErrorFilter::Internal,
    985                           nsPrintfCString("Unsupported IOSurface format: %s",
    986                                           mozilla::ToString(format).c_str()));
    987      return CreateError();
    988  }
    989 
    990  AutoTArray<RawId, 2> usedTextureIds;
    991  AutoTArray<RawId, 2> usedViewIds;
    992  for (size_t i = 0; i < textureDescs.Length(); i++) {
    993    usedTextureIds.AppendElement(aDesc.mTextureIds[i]);
    994    usedViewIds.AppendElement(aDesc.mViewIds[i]);
    995    {
    996      ErrorBuffer error;
    997      ffi::wgpu_server_device_import_texture_from_iosurface(
    998          aParent->GetContext(), aDeviceId, aDesc.mTextureIds[i],
    999          &textureDescs[i], ioSurface->GetIOSurfaceID(), i, error.ToFFI());
   1000      // From here on there's no need to return early with `CreateError()` in
   1001      // case of an error, as an error creating a texture or view will be
   1002      // propagated to any views or external textures created from them.
   1003      // Since we have full control over the creation of this texture, any
   1004      // validation error we encounter should be treated as an internal error.
   1005      error.CoerceValidationToInternal();
   1006      aParent->ForwardError(error);
   1007    }
   1008    ffi::WGPUTextureViewDescriptor viewDesc{};
   1009    {
   1010      ErrorBuffer error;
   1011      ffi::wgpu_server_texture_create_view(
   1012          aParent->GetContext(), aDeviceId, aDesc.mTextureIds[i],
   1013          aDesc.mViewIds[i], &viewDesc, error.ToFFI());
   1014      error.CoerceValidationToInternal();
   1015      aParent->ForwardError(error);
   1016    }
   1017  }
   1018  return ExternalTextureSourceHost(usedTextureIds, usedViewIds, aDesc.mSize,
   1019                                   format, colorSpace, aDesc.mSampleTransform,
   1020                                   aDesc.mLoadTransform);
   1021 #else
   1022  MOZ_CRASH();
   1023 #endif
   1024 }
   1025 
   1026 static color::ColorspaceTransform GetColorSpaceTransform(
   1027    gfx::YUVRangedColorSpace aSrcColorSpace,
   1028    ffi::WGPUPredefinedColorSpace aDestColorSpace) {
   1029  const bool rec709GammaAsSrgb =
   1030      StaticPrefs::gfx_color_management_rec709_gamma_as_srgb();
   1031  const bool rec2020GammaAsRec709 =
   1032      StaticPrefs::gfx_color_management_rec2020_gamma_as_rec709();
   1033 
   1034  color::ColorspaceDesc srcColorSpace;
   1035  switch (aSrcColorSpace) {
   1036    case gfx::YUVRangedColorSpace::BT601_Narrow:
   1037      srcColorSpace = {
   1038          .chrom = color::Chromaticities::Rec601_525_Ntsc(),
   1039          .tf = rec709GammaAsSrgb ? color::PiecewiseGammaDesc::Srgb()
   1040                                  : color::PiecewiseGammaDesc::Rec709(),
   1041          .yuv =
   1042              color::YuvDesc{
   1043                  .yCoeffs = color::YuvLumaCoeffs::Rec601(),
   1044                  .ycbcr = color::YcbcrDesc::Narrow8(),
   1045              },
   1046      };
   1047      break;
   1048    case gfx::YUVRangedColorSpace::BT601_Full:
   1049      srcColorSpace = {
   1050          .chrom = color::Chromaticities::Rec601_525_Ntsc(),
   1051          .tf = rec709GammaAsSrgb ? color::PiecewiseGammaDesc::Srgb()
   1052                                  : color::PiecewiseGammaDesc::Rec709(),
   1053          .yuv =
   1054              color::YuvDesc{
   1055                  .yCoeffs = color::YuvLumaCoeffs::Rec601(),
   1056                  .ycbcr = color::YcbcrDesc::Full8(),
   1057              },
   1058      };
   1059      break;
   1060    case gfx::YUVRangedColorSpace::BT709_Narrow:
   1061      srcColorSpace = {
   1062          .chrom = color::Chromaticities::Rec709(),
   1063          .tf = rec709GammaAsSrgb ? color::PiecewiseGammaDesc::Srgb()
   1064                                  : color::PiecewiseGammaDesc::Rec709(),
   1065          .yuv =
   1066              color::YuvDesc{
   1067                  .yCoeffs = color::YuvLumaCoeffs::Rec709(),
   1068                  .ycbcr = color::YcbcrDesc::Narrow8(),
   1069              },
   1070      };
   1071      break;
   1072    case gfx::YUVRangedColorSpace::BT709_Full:
   1073      srcColorSpace = {
   1074          .chrom = color::Chromaticities::Rec709(),
   1075          .tf = rec709GammaAsSrgb ? color::PiecewiseGammaDesc::Srgb()
   1076                                  : color::PiecewiseGammaDesc::Rec709(),
   1077          .yuv =
   1078              color::YuvDesc{
   1079                  .yCoeffs = color::YuvLumaCoeffs::Rec709(),
   1080                  .ycbcr = color::YcbcrDesc::Full8(),
   1081              },
   1082      };
   1083      break;
   1084    case gfx::YUVRangedColorSpace::BT2020_Narrow:
   1085      srcColorSpace = {
   1086          .chrom = color::Chromaticities::Rec2020(),
   1087          .tf = rec2020GammaAsRec709 && rec709GammaAsSrgb
   1088                    ? color::PiecewiseGammaDesc::Srgb()
   1089                    : (rec2020GammaAsRec709
   1090                           ? color::PiecewiseGammaDesc::Rec709()
   1091                           : color::PiecewiseGammaDesc::Rec2020_12bit()),
   1092          .yuv =
   1093              color::YuvDesc{
   1094                  .yCoeffs = color::YuvLumaCoeffs::Rec2020(),
   1095                  .ycbcr = color::YcbcrDesc::Narrow8(),
   1096              },
   1097      };
   1098      break;
   1099    case gfx::YUVRangedColorSpace::BT2020_Full:
   1100      srcColorSpace = {
   1101          .chrom = color::Chromaticities::Rec2020(),
   1102          .tf = rec2020GammaAsRec709 && rec709GammaAsSrgb
   1103                    ? color::PiecewiseGammaDesc::Srgb()
   1104                    : (rec2020GammaAsRec709
   1105                           ? color::PiecewiseGammaDesc::Rec709()
   1106                           : color::PiecewiseGammaDesc::Rec2020_12bit()),
   1107          .yuv =
   1108              color::YuvDesc{
   1109                  .yCoeffs = color::YuvLumaCoeffs::Rec2020(),
   1110                  .ycbcr = color::YcbcrDesc::Full8(),
   1111              },
   1112      };
   1113      break;
   1114    case gfx::YUVRangedColorSpace::GbrIdentity:
   1115      srcColorSpace = {
   1116          .chrom = color::Chromaticities::Rec709(),
   1117          .tf = color::PiecewiseGammaDesc::Rec709(),
   1118          .yuv =
   1119              color::YuvDesc{
   1120                  .yCoeffs = color::YuvLumaCoeffs::Gbr(),
   1121                  .ycbcr = color::YcbcrDesc::Full8(),
   1122              },
   1123      };
   1124      break;
   1125  }
   1126 
   1127  color::ColorspaceDesc destColorSpace{};
   1128  switch (aDestColorSpace) {
   1129    case ffi::WGPUPredefinedColorSpace_Srgb:
   1130      destColorSpace = {.chrom = color::Chromaticities::Srgb(),
   1131                        .tf = color::PiecewiseGammaDesc::Srgb()};
   1132      break;
   1133    case ffi::WGPUPredefinedColorSpace_DisplayP3:
   1134      destColorSpace = {.chrom = color::Chromaticities::DisplayP3(),
   1135                        .tf = color::PiecewiseGammaDesc::DisplayP3()};
   1136      break;
   1137    case ffi::WGPUPredefinedColorSpace_Sentinel:
   1138      MOZ_CRASH("Invalid WGPUPredefinedColorSpace");
   1139  }
   1140 
   1141  return color::ColorspaceTransform::Create(srcColorSpace, destColorSpace);
   1142 }
   1143 
   1144 static ffi::WGPUExternalTextureFormat MapFormat(gfx::SurfaceFormat aFormat) {
   1145  switch (aFormat) {
   1146    case gfx::SurfaceFormat::B8G8R8A8:
   1147    case gfx::SurfaceFormat::B8G8R8X8:
   1148    case gfx::SurfaceFormat::R8G8B8A8:
   1149    case gfx::SurfaceFormat::R8G8B8X8:
   1150      return ffi::WGPUExternalTextureFormat_Rgba;
   1151    case gfx::SurfaceFormat::YUV420:
   1152      return ffi::WGPUExternalTextureFormat_Yu12;
   1153    case gfx::SurfaceFormat::NV12:
   1154    case gfx::SurfaceFormat::P010:
   1155      return ffi::WGPUExternalTextureFormat_Nv12;
   1156    default:
   1157      MOZ_CRASH("Unexpected SurfaceFormat");
   1158  }
   1159 }
   1160 
   1161 static ffi::WGPUExternalTextureTransferFunction MapTransferFunction(
   1162    std::optional<color::PiecewiseGammaDesc> aTf) {
   1163  if (aTf) {
   1164    return ffi::WGPUExternalTextureTransferFunction{
   1165        .a = aTf->a,
   1166        .b = aTf->b,
   1167        .g = aTf->g,
   1168        .k = aTf->k,
   1169    };
   1170  } else {
   1171    return ffi::WGPUExternalTextureTransferFunction{
   1172        .a = 1.0,
   1173        .b = 1.0,
   1174        .g = 1.0,
   1175        .k = 1.0,
   1176    };
   1177  }
   1178 }
   1179 
   1180 ffi::WGPUExternalTextureDescriptorFromSource
   1181 ExternalTextureSourceHost::GetExternalTextureDescriptor(
   1182    ffi::WGPUPredefinedColorSpace aDestColorSpace) const {
   1183  ffi::WGPUExternalTextureDescriptorFromSource desc;
   1184 
   1185  desc.planes = ffi::WGPUFfiSlice_TextureViewId{
   1186      .data = mViewIds.Elements(),
   1187      .length = mViewIds.Length(),
   1188  };
   1189  desc.width = static_cast<uint32_t>(mSize.width);
   1190  desc.height = static_cast<uint32_t>(mSize.height);
   1191  desc.format = MapFormat(mFormat);
   1192 
   1193  auto colorSpaceTransform =
   1194      GetColorSpaceTransform(mColorSpace, aDestColorSpace);
   1195  // Makes a generator for a color::mat that yields its components in
   1196  // column-major order
   1197  auto make_column_major_generator = [](auto mat) {
   1198    return [i = 0, mat]() mutable {
   1199      auto val = mat.at(i / mat.y_rows, i % mat.y_rows);
   1200      i++;
   1201      return val;
   1202    };
   1203  };
   1204  std::generate(
   1205      std::begin(desc.yuv_conversion_matrix),
   1206      std::end(desc.yuv_conversion_matrix),
   1207      make_column_major_generator(colorSpaceTransform.srcRgbTfFromSrc));
   1208  std::generate(
   1209      std::begin(desc.gamut_conversion_matrix),
   1210      std::end(desc.gamut_conversion_matrix),
   1211      make_column_major_generator(colorSpaceTransform.dstRgbLinFromSrcRgbLin));
   1212  desc.src_transfer_function = MapTransferFunction(colorSpaceTransform.srcTf);
   1213  desc.dst_transfer_function = MapTransferFunction(colorSpaceTransform.dstTf);
   1214  std::copy(mSampleTransform.begin(), mSampleTransform.end(),
   1215            desc.sample_transform);
   1216  std::copy(mLoadTransform.begin(), mLoadTransform.end(), desc.load_transform);
   1217 
   1218  return desc;
   1219 }
   1220 
   1221 bool ExternalTextureSourceHost::OnBeforeQueueSubmit(WebGPUParent* aParent,
   1222                                                    RawId aDeviceId,
   1223                                                    RawId aQueueId) {
   1224 #if defined(XP_WIN)
   1225  // Wait on the write fence provided by the decoder, if any, to ensure we don't
   1226  // read from the texture before writes have completed.
   1227  if (mFenceId) {
   1228    const auto* fencesMap = layers::CompositeProcessD3D11FencesHolderMap::Get();
   1229    if (!fencesMap) {
   1230      gfxCriticalErrorOnce()
   1231          << "CompositeProcessD3D11FencesHolderMap is not initialized";
   1232      aParent->ReportError(
   1233          aDeviceId, dom::GPUErrorFilter::Internal,
   1234          "CompositeProcessD3D11FencesHolderMap is not initialized"_ns);
   1235      return false;
   1236    }
   1237    auto [fenceHandle, fenceValue] =
   1238        fencesMap->GetWriteFenceHandleAndValue(*mFenceId);
   1239    if (fenceHandle) {
   1240      const bool success =
   1241          ffi::wgpu_server_device_wait_fence_from_shared_handle(
   1242              aParent->GetContext(), aDeviceId, aQueueId,
   1243              fenceHandle->GetHandle(), fenceValue);
   1244      if (success) {
   1245        // No need to wait next time
   1246        mFenceId.reset();
   1247      } else {
   1248        gfxCriticalErrorOnce() << "Failed to wait on write fence";
   1249        aParent->ReportError(aDeviceId, dom::GPUErrorFilter::Internal,
   1250                             "Failed to wait on write fence"_ns);
   1251        return false;
   1252      }
   1253    }
   1254  }
   1255  return true;
   1256 #else
   1257  return true;
   1258 #endif
   1259 }
   1260 
   1261 }  // namespace mozilla::webgpu