tor-browser

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

Device.cpp (36744B)


      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 "Device.h"
      7 
      8 #include "Adapter.h"
      9 #include "BindGroup.h"
     10 #include "Buffer.h"
     11 #include "CommandEncoder.h"
     12 #include "CompilationInfo.h"
     13 #include "ComputePipeline.h"
     14 #include "DeviceLostInfo.h"
     15 #include "ExternalTexture.h"
     16 #include "InternalError.h"
     17 #include "OutOfMemoryError.h"
     18 #include "PipelineLayout.h"
     19 #include "QuerySet.h"
     20 #include "Queue.h"
     21 #include "RenderBundleEncoder.h"
     22 #include "RenderPipeline.h"
     23 #include "Sampler.h"
     24 #include "SupportedFeatures.h"
     25 #include "SupportedLimits.h"
     26 #include "Texture.h"
     27 #include "TextureView.h"
     28 #include "Utility.h"
     29 #include "ValidationError.h"
     30 #include "ipc/WebGPUChild.h"
     31 #include "js/ArrayBuffer.h"
     32 #include "js/Value.h"
     33 #include "mozilla/Attributes.h"
     34 #include "mozilla/ErrorResult.h"
     35 #include "mozilla/Logging.h"
     36 #include "mozilla/RefPtr.h"
     37 #include "mozilla/dom/Console.h"
     38 #include "mozilla/dom/Promise.h"
     39 #include "mozilla/dom/VideoFrame.h"
     40 #include "mozilla/dom/WebGPUBinding.h"
     41 #include "mozilla/gfx/gfxVars.h"
     42 #include "nsGlobalWindowInner.h"
     43 
     44 namespace mozilla::webgpu {
     45 
     46 mozilla::LazyLogModule gWebGPULog("WebGPU");
     47 
     48 NS_IMPL_CYCLE_COLLECTION_WEAK_PTR_INHERITED(Device, DOMEventTargetHelper,
     49                                            mQueue, mFeatures, mLimits,
     50                                            mAdapterInfo, mLostPromise);
     51 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(Device, DOMEventTargetHelper)
     52 GPU_IMPL_JS_WRAP(Device)
     53 
     54 /* static */ CheckedInt<uint32_t> Device::BufferStrideWithMask(
     55    const gfx::IntSize& aSize, const gfx::SurfaceFormat& aFormat) {
     56  constexpr uint32_t kBufferAlignmentMask = 0xff;
     57  return CheckedInt<uint32_t>(aSize.width) * gfx::BytesPerPixel(aFormat) +
     58         kBufferAlignmentMask;
     59 }
     60 
     61 Device::Device(Adapter* const aParent, RawId aDeviceId, RawId aQueueId,
     62               RefPtr<SupportedFeatures> aFeatures,
     63               RefPtr<SupportedLimits> aLimits,
     64               RefPtr<webgpu::AdapterInfo> aAdapterInfo,
     65               RefPtr<dom::Promise> aLostPromise)
     66    : DOMEventTargetHelper(aParent->GetParentObject()),
     67      ObjectBase(aParent->GetChild(), aDeviceId, ffi::wgpu_client_drop_device),
     68      mFeatures(std::move(aFeatures)),
     69      mLimits(std::move(aLimits)),
     70      mAdapterInfo(std::move(aAdapterInfo)),
     71      mSupportSharedTextureInSwapChain(
     72          aParent->SupportSharedTextureInSwapChain()),
     73      mLostPromise(std::move(aLostPromise)),
     74      mQueue(new class Queue(this, aQueueId)) {
     75  GetChild()->RegisterDevice(this);
     76  KeepAliveIfHasListenersFor(nsGkAtoms::onuncapturederror);
     77 }
     78 
     79 Device::~Device() { GetChild()->UnregisterDevice(GetId()); }
     80 
     81 void Device::TrackBuffer(Buffer* aBuffer) { mTrackedBuffers.Insert(aBuffer); }
     82 
     83 void Device::UntrackBuffer(Buffer* aBuffer) { mTrackedBuffers.Remove(aBuffer); }
     84 
     85 dom::Promise* Device::GetLost(ErrorResult& aRv) {
     86  aRv = NS_OK;
     87  return mLostPromise;
     88 }
     89 
     90 void Device::ResolveLost(dom::GPUDeviceLostReason aReason,
     91                         const nsAString& aMessage) {
     92  if (mLostPromise->State() != dom::Promise::PromiseState::Pending) {
     93    // The lost promise was already resolved or rejected.
     94    return;
     95  }
     96  RefPtr<DeviceLostInfo> info =
     97      MakeRefPtr<DeviceLostInfo>(GetParentObject(), aReason, aMessage);
     98  mLostPromise->MaybeResolve(info);
     99 }
    100 
    101 already_AddRefed<Buffer> Device::CreateBuffer(
    102    const dom::GPUBufferDescriptor& aDesc, ErrorResult& aRv) {
    103  return Buffer::Create(this, GetId(), aDesc, aRv);
    104 }
    105 
    106 already_AddRefed<Texture> Device::CreateTextureForSwapChain(
    107    const dom::GPUCanvasConfiguration* const aConfig,
    108    const gfx::IntSize& aCanvasSize, layers::RemoteTextureOwnerId aOwnerId) {
    109  MOZ_ASSERT(aConfig);
    110 
    111  dom::GPUTextureDescriptor desc;
    112  desc.mDimension = dom::GPUTextureDimension::_2d;
    113  auto& sizeDict = desc.mSize.SetAsGPUExtent3DDict();
    114  sizeDict.mWidth = aCanvasSize.width;
    115  sizeDict.mHeight = aCanvasSize.height;
    116  sizeDict.mDepthOrArrayLayers = 1;
    117  desc.mFormat = aConfig->mFormat;
    118  desc.mMipLevelCount = 1;
    119  desc.mSampleCount = 1;
    120  desc.mUsage = aConfig->mUsage | dom::GPUTextureUsage_Binding::COPY_SRC;
    121  desc.mViewFormats = aConfig->mViewFormats;
    122 
    123  return CreateTexture(desc, Some(aOwnerId));
    124 }
    125 
    126 already_AddRefed<Texture> Device::CreateTexture(
    127    const dom::GPUTextureDescriptor& aDesc) {
    128  return CreateTexture(aDesc, /* aOwnerId */ Nothing());
    129 }
    130 
    131 already_AddRefed<Texture> Device::CreateTexture(
    132    const dom::GPUTextureDescriptor& aDesc,
    133    Maybe<layers::RemoteTextureOwnerId> aOwnerId) {
    134  ffi::WGPUTextureDescriptor desc = {};
    135 
    136  webgpu::StringHelper label(aDesc.mLabel);
    137  desc.label = label.Get();
    138 
    139  if (aDesc.mSize.IsRangeEnforcedUnsignedLongSequence()) {
    140    const auto& seq = aDesc.mSize.GetAsRangeEnforcedUnsignedLongSequence();
    141    desc.size.width = seq.Length() > 0 ? seq[0] : 1;
    142    desc.size.height = seq.Length() > 1 ? seq[1] : 1;
    143    desc.size.depth_or_array_layers = seq.Length() > 2 ? seq[2] : 1;
    144  } else if (aDesc.mSize.IsGPUExtent3DDict()) {
    145    const auto& dict = aDesc.mSize.GetAsGPUExtent3DDict();
    146    desc.size.width = dict.mWidth;
    147    desc.size.height = dict.mHeight;
    148    desc.size.depth_or_array_layers = dict.mDepthOrArrayLayers;
    149  } else {
    150    MOZ_CRASH("Unexpected union");
    151  }
    152  desc.mip_level_count = aDesc.mMipLevelCount;
    153  desc.sample_count = aDesc.mSampleCount;
    154  desc.dimension = ffi::WGPUTextureDimension(aDesc.mDimension);
    155  desc.format = ConvertTextureFormat(aDesc.mFormat);
    156  desc.usage = aDesc.mUsage;
    157 
    158  AutoTArray<ffi::WGPUTextureFormat, 8> viewFormats;
    159  for (auto format : aDesc.mViewFormats) {
    160    viewFormats.AppendElement(ConvertTextureFormat(format));
    161  }
    162  desc.view_formats = {viewFormats.Elements(), viewFormats.Length()};
    163 
    164  Maybe<ffi::WGPUSwapChainId> ownerId;
    165  if (aOwnerId.isSome()) {
    166    ownerId = Some(ffi::WGPUSwapChainId{aOwnerId->mId});
    167  }
    168 
    169  RawId id = ffi::wgpu_client_create_texture(GetClient(), GetId(), &desc,
    170                                             ownerId.ptrOr(nullptr));
    171 
    172  RefPtr<Texture> texture = new Texture(this, id, aDesc);
    173  texture->SetLabel(aDesc.mLabel);
    174  return texture.forget();
    175 }
    176 
    177 already_AddRefed<ExternalTexture> Device::ImportExternalTexture(
    178    const dom::GPUExternalTextureDescriptor& aDesc, ErrorResult& aRv) {
    179  if (!gfx::gfxVars::AllowWebGPUExternalTexture()) {
    180    aRv.ThrowNotSupportedError("WebGPU external textures are disabled");
    181    return nullptr;
    182  }
    183 
    184  RefPtr<ExternalTexture> externalTexture =
    185      mExternalTextureCache.GetOrCreate(this, aDesc, aRv);
    186 
    187  switch (aDesc.mSource.GetType()) {
    188    case dom::OwningHTMLVideoElementOrVideoFrame::Type::eHTMLVideoElement: {
    189      // Add the texture to the list of textures to be expired in the next
    190      // automatic expiry task, scheduling the task if required.
    191      // Using RunInStableState ensures it runs after any microtasks that may
    192      // be scheduled during the current task.
    193      if (mExternalTexturesToExpire.IsEmpty()) {
    194        nsContentUtils::RunInStableState(
    195            NewRunnableMethod("webgpu::Device::ExpireExternalTextures", this,
    196                              &Device::ExpireExternalTextures));
    197      }
    198      mExternalTexturesToExpire.AppendElement(externalTexture);
    199    } break;
    200    case dom::OwningHTMLVideoElementOrVideoFrame::Type::eVideoFrame: {
    201      // Ensure the VideoFrame knows about the external texture, so that it can
    202      // expire it when the VideoFrame is closed.
    203      const auto& videoFrame = aDesc.mSource.GetAsVideoFrame();
    204      videoFrame->TrackWebGPUExternalTexture(externalTexture.get());
    205    } break;
    206  }
    207 
    208  return externalTexture.forget();
    209 }
    210 
    211 void Device::ExpireExternalTextures() {
    212  MOZ_ASSERT(!mExternalTexturesToExpire.IsEmpty(),
    213             "Task should not have been scheduled if there are no external "
    214             "textures to expire");
    215  for (const auto& weakExternalTexture : mExternalTexturesToExpire) {
    216    if (auto* externalTexture = weakExternalTexture.get()) {
    217      externalTexture->Expire();
    218    }
    219  }
    220  mExternalTexturesToExpire.Clear();
    221 }
    222 
    223 already_AddRefed<Sampler> Device::CreateSampler(
    224    const dom::GPUSamplerDescriptor& aDesc) {
    225  ffi::WGPUSamplerDescriptor desc = {};
    226  webgpu::StringHelper label(aDesc.mLabel);
    227 
    228  desc.label = label.Get();
    229  desc.address_modes[0] = ffi::WGPUAddressMode(aDesc.mAddressModeU);
    230  desc.address_modes[1] = ffi::WGPUAddressMode(aDesc.mAddressModeV);
    231  desc.address_modes[2] = ffi::WGPUAddressMode(aDesc.mAddressModeW);
    232  desc.mag_filter = ffi::WGPUFilterMode(aDesc.mMagFilter);
    233  desc.min_filter = ffi::WGPUFilterMode(aDesc.mMinFilter);
    234  desc.mipmap_filter = ffi::WGPUMipmapFilterMode(aDesc.mMipmapFilter);
    235  desc.lod_min_clamp = aDesc.mLodMinClamp;
    236  desc.lod_max_clamp = aDesc.mLodMaxClamp;
    237  desc.max_anisotropy = aDesc.mMaxAnisotropy;
    238 
    239  ffi::WGPUCompareFunction comparison = ffi::WGPUCompareFunction_Sentinel;
    240  if (aDesc.mCompare.WasPassed()) {
    241    comparison = ConvertCompareFunction(aDesc.mCompare.Value());
    242    desc.compare = &comparison;
    243  }
    244 
    245  RawId id = ffi::wgpu_client_create_sampler(GetClient(), GetId(), &desc);
    246 
    247  RefPtr<Sampler> sampler = new Sampler(this, id);
    248  sampler->SetLabel(aDesc.mLabel);
    249  return sampler.forget();
    250 }
    251 
    252 already_AddRefed<CommandEncoder> Device::CreateCommandEncoder(
    253    const dom::GPUCommandEncoderDescriptor& aDesc) {
    254  ffi::WGPUCommandEncoderDescriptor desc = {};
    255 
    256  webgpu::StringHelper label(aDesc.mLabel);
    257  desc.label = label.Get();
    258 
    259  RawId id =
    260      ffi::wgpu_client_create_command_encoder(GetClient(), GetId(), &desc);
    261 
    262  RefPtr<CommandEncoder> encoder = new CommandEncoder(this, id);
    263  encoder->SetLabel(aDesc.mLabel);
    264  return encoder.forget();
    265 }
    266 
    267 already_AddRefed<RenderBundleEncoder> Device::CreateRenderBundleEncoder(
    268    const dom::GPURenderBundleEncoderDescriptor& aDesc) {
    269  auto id = ffi::wgpu_client_make_render_bundle_encoder_id(GetClient());
    270  RefPtr<RenderBundleEncoder> encoder =
    271      new RenderBundleEncoder(this, id, aDesc);
    272  encoder->SetLabel(aDesc.mLabel);
    273  return encoder.forget();
    274 }
    275 
    276 already_AddRefed<QuerySet> Device::CreateQuerySet(
    277    const dom::GPUQuerySetDescriptor& aDesc, ErrorResult& aRv) {
    278  ffi::WGPURawQuerySetDescriptor desc = {};
    279 
    280  webgpu::StringHelper label(aDesc.mLabel);
    281  desc.label = label.Get();
    282  ffi::WGPURawQueryType type;
    283  switch (aDesc.mType) {
    284    case dom::GPUQueryType::Occlusion:
    285      type = ffi::WGPURawQueryType_Occlusion;
    286      break;
    287    case dom::GPUQueryType::Timestamp:
    288      type = ffi::WGPURawQueryType_Timestamp;
    289      if (!mFeatures->Features().count(dom::GPUFeatureName::Timestamp_query)) {
    290        aRv.ThrowTypeError(
    291            "requested query set of type `timestamp`, but the "
    292            "`timestamp-query` feature is not enabled on the device");
    293        return nullptr;
    294      }
    295      break;
    296  };
    297  desc.ty = type;
    298  desc.count = aDesc.mCount;
    299 
    300  RawId id = ffi::wgpu_client_create_query_set(GetClient(), GetId(), &desc);
    301 
    302  RefPtr<QuerySet> querySet = new QuerySet(this, aDesc, id);
    303  querySet->SetLabel(aDesc.mLabel);
    304  return querySet.forget();
    305 }
    306 
    307 already_AddRefed<BindGroupLayout> Device::CreateBindGroupLayout(
    308    const dom::GPUBindGroupLayoutDescriptor& aDesc) {
    309  struct OptionalData {
    310    ffi::WGPUTextureViewDimension dim;
    311    ffi::WGPURawTextureSampleType type;
    312    ffi::WGPUTextureFormat format;
    313  };
    314  nsTArray<OptionalData> optional(aDesc.mEntries.Length());
    315  for (const auto& entry : aDesc.mEntries) {
    316    OptionalData data = {};
    317    if (entry.mTexture.WasPassed()) {
    318      const auto& texture = entry.mTexture.Value();
    319      data.dim = ffi::WGPUTextureViewDimension(texture.mViewDimension);
    320      switch (texture.mSampleType) {
    321        case dom::GPUTextureSampleType::Float:
    322          data.type = ffi::WGPURawTextureSampleType_Float;
    323          break;
    324        case dom::GPUTextureSampleType::Unfilterable_float:
    325          data.type = ffi::WGPURawTextureSampleType_UnfilterableFloat;
    326          break;
    327        case dom::GPUTextureSampleType::Uint:
    328          data.type = ffi::WGPURawTextureSampleType_Uint;
    329          break;
    330        case dom::GPUTextureSampleType::Sint:
    331          data.type = ffi::WGPURawTextureSampleType_Sint;
    332          break;
    333        case dom::GPUTextureSampleType::Depth:
    334          data.type = ffi::WGPURawTextureSampleType_Depth;
    335          break;
    336      }
    337    }
    338    if (entry.mStorageTexture.WasPassed()) {
    339      const auto& texture = entry.mStorageTexture.Value();
    340      data.dim = ffi::WGPUTextureViewDimension(texture.mViewDimension);
    341      data.format = ConvertTextureFormat(texture.mFormat);
    342    }
    343    optional.AppendElement(data);
    344  }
    345 
    346  nsTArray<ffi::WGPUBindGroupLayoutEntry> entries(aDesc.mEntries.Length());
    347  for (size_t i = 0; i < aDesc.mEntries.Length(); ++i) {
    348    const auto& entry = aDesc.mEntries[i];
    349    ffi::WGPUBindGroupLayoutEntry e = {};
    350    e.binding = entry.mBinding;
    351    e.visibility = entry.mVisibility;
    352    if (entry.mBuffer.WasPassed()) {
    353      switch (entry.mBuffer.Value().mType) {
    354        case dom::GPUBufferBindingType::Uniform:
    355          e.ty = ffi::WGPURawBindingType_UniformBuffer;
    356          break;
    357        case dom::GPUBufferBindingType::Storage:
    358          e.ty = ffi::WGPURawBindingType_StorageBuffer;
    359          break;
    360        case dom::GPUBufferBindingType::Read_only_storage:
    361          e.ty = ffi::WGPURawBindingType_ReadonlyStorageBuffer;
    362          break;
    363      }
    364      e.has_dynamic_offset = entry.mBuffer.Value().mHasDynamicOffset;
    365      e.min_binding_size = entry.mBuffer.Value().mMinBindingSize;
    366    }
    367    if (entry.mTexture.WasPassed()) {
    368      e.ty = ffi::WGPURawBindingType_SampledTexture;
    369      e.view_dimension = &optional[i].dim;
    370      e.texture_sample_type = &optional[i].type;
    371      e.multisampled = entry.mTexture.Value().mMultisampled;
    372    }
    373    if (entry.mStorageTexture.WasPassed()) {
    374      switch (entry.mStorageTexture.Value().mAccess) {
    375        case dom::GPUStorageTextureAccess::Write_only: {
    376          e.ty = ffi::WGPURawBindingType_WriteonlyStorageTexture;
    377          break;
    378        }
    379        case dom::GPUStorageTextureAccess::Read_only: {
    380          e.ty = ffi::WGPURawBindingType_ReadonlyStorageTexture;
    381          break;
    382        }
    383        case dom::GPUStorageTextureAccess::Read_write: {
    384          e.ty = ffi::WGPURawBindingType_ReadWriteStorageTexture;
    385          break;
    386        }
    387        default: {
    388          MOZ_ASSERT_UNREACHABLE();
    389        }
    390      }
    391      e.view_dimension = &optional[i].dim;
    392      e.storage_texture_format = &optional[i].format;
    393    }
    394    if (entry.mSampler.WasPassed()) {
    395      e.ty = ffi::WGPURawBindingType_Sampler;
    396      switch (entry.mSampler.Value().mType) {
    397        case dom::GPUSamplerBindingType::Filtering:
    398          e.sampler_filter = true;
    399          break;
    400        case dom::GPUSamplerBindingType::Non_filtering:
    401          break;
    402        case dom::GPUSamplerBindingType::Comparison:
    403          e.sampler_compare = true;
    404          break;
    405      }
    406    }
    407    if (entry.mExternalTexture.WasPassed()) {
    408      e.ty = ffi::WGPURawBindingType_ExternalTexture;
    409    }
    410    entries.AppendElement(e);
    411  }
    412 
    413  ffi::WGPUBindGroupLayoutDescriptor desc = {};
    414 
    415  webgpu::StringHelper label(aDesc.mLabel);
    416  desc.label = label.Get();
    417  desc.entries = {entries.Elements(), entries.Length()};
    418 
    419  RawId id =
    420      ffi::wgpu_client_create_bind_group_layout(GetClient(), GetId(), &desc);
    421 
    422  RefPtr<BindGroupLayout> object = new BindGroupLayout(this, id);
    423  object->SetLabel(aDesc.mLabel);
    424  return object.forget();
    425 }
    426 
    427 already_AddRefed<PipelineLayout> Device::CreatePipelineLayout(
    428    const dom::GPUPipelineLayoutDescriptor& aDesc) {
    429  nsTArray<ffi::WGPUBindGroupLayoutId> bindGroupLayouts(
    430      aDesc.mBindGroupLayouts.Length());
    431 
    432  for (const auto& layout : aDesc.mBindGroupLayouts) {
    433    bindGroupLayouts.AppendElement(layout->GetId());
    434  }
    435 
    436  ffi::WGPUPipelineLayoutDescriptor desc = {};
    437 
    438  webgpu::StringHelper label(aDesc.mLabel);
    439  desc.label = label.Get();
    440  desc.bind_group_layouts = {bindGroupLayouts.Elements(),
    441                             bindGroupLayouts.Length()};
    442 
    443  RawId id =
    444      ffi::wgpu_client_create_pipeline_layout(GetClient(), GetId(), &desc);
    445 
    446  RefPtr<PipelineLayout> object = new PipelineLayout(this, id);
    447  object->SetLabel(aDesc.mLabel);
    448  return object.forget();
    449 }
    450 
    451 already_AddRefed<BindGroup> Device::CreateBindGroup(
    452    const dom::GPUBindGroupDescriptor& aDesc) {
    453  nsTArray<ffi::WGPUBindGroupEntry> entries(aDesc.mEntries.Length());
    454  CanvasContextArray canvasContexts;
    455  nsTArray<RefPtr<ExternalTexture>> externalTextures;
    456  for (const auto& entry : aDesc.mEntries) {
    457    ffi::WGPUBindGroupEntry e = {};
    458    e.binding = entry.mBinding;
    459    auto setTextureViewBinding =
    460        [&e, &canvasContexts](const TextureView& texture_view) {
    461          e.texture_view = texture_view.GetId();
    462          auto context = texture_view.GetTargetContext();
    463          if (context) {
    464            canvasContexts.AppendElement(context);
    465          }
    466        };
    467    if (entry.mResource.IsGPUBuffer()) {
    468      const auto& buffer = entry.mResource.GetAsGPUBuffer();
    469      if (!buffer->GetId()) {
    470        NS_WARNING("Buffer has no id -- ignoring.");
    471        continue;
    472      }
    473      e.buffer = buffer->GetId();
    474      e.offset = 0;
    475      e.size_passed = false;
    476      e.size = 0;
    477    } else if (entry.mResource.IsGPUBufferBinding()) {
    478      const auto& bufBinding = entry.mResource.GetAsGPUBufferBinding();
    479      if (!bufBinding.mBuffer->GetId()) {
    480        NS_WARNING("Buffer binding has no id -- ignoring.");
    481        continue;
    482      }
    483      e.buffer = bufBinding.mBuffer->GetId();
    484      e.offset = bufBinding.mOffset;
    485      e.size_passed = bufBinding.mSize.WasPassed();
    486      if (e.size_passed) {
    487        e.size = bufBinding.mSize.Value();
    488      } else {
    489        e.size = 0;
    490      }
    491    } else if (entry.mResource.IsGPUTexture()) {
    492      auto texture = entry.mResource.GetAsGPUTexture();
    493      const dom::GPUTextureViewDescriptor defaultDesc{};
    494      RefPtr<TextureView> texture_view = texture->CreateView(defaultDesc);
    495      setTextureViewBinding(*texture_view);
    496    } else if (entry.mResource.IsGPUTextureView()) {
    497      auto texture_view = entry.mResource.GetAsGPUTextureView();
    498      setTextureViewBinding(texture_view);
    499    } else if (entry.mResource.IsGPUSampler()) {
    500      e.sampler = entry.mResource.GetAsGPUSampler()->GetId();
    501    } else if (entry.mResource.IsGPUExternalTexture()) {
    502      const RefPtr<ExternalTexture> externalTexture =
    503          entry.mResource.GetAsGPUExternalTexture();
    504      e.external_texture = externalTexture->GetId();
    505      externalTextures.AppendElement(externalTexture);
    506    } else {
    507      // Not a buffer, nor a texture view, nor a sampler, nor an external
    508      // texture. If we pass this to wgpu_client, it'll panic. Log a warning
    509      // instead and ignore this entry.
    510      NS_WARNING("Bind group entry has unknown type.");
    511      continue;
    512    }
    513    entries.AppendElement(e);
    514  }
    515 
    516  ffi::WGPUBindGroupDescriptor desc = {};
    517 
    518  webgpu::StringHelper label(aDesc.mLabel);
    519  desc.label = label.Get();
    520  desc.layout = aDesc.mLayout->GetId();
    521  desc.entries = {entries.Elements(), entries.Length()};
    522 
    523  RawId id = ffi::wgpu_client_create_bind_group(GetClient(), GetId(), &desc);
    524 
    525  RefPtr<BindGroup> object = new BindGroup(this, id, std::move(canvasContexts),
    526                                           std::move(externalTextures));
    527  object->SetLabel(aDesc.mLabel);
    528 
    529  return object.forget();
    530 }
    531 
    532 void reportCompilationMessagesToConsole(
    533    const RefPtr<ShaderModule>& aShaderModule,
    534    const nsTArray<WebGPUCompilationMessage>& aMessages) {
    535  auto* global = aShaderModule->GetParentObject();
    536 
    537  dom::AutoJSAPI api;
    538  if (!api.Init(global)) {
    539    return;
    540  }
    541 
    542  const auto& cx = api.cx();
    543  dom::GlobalObject globalObj(cx, global->GetGlobalJSObject());
    544 
    545  dom::Sequence<JS::Value> args;
    546  dom::SequenceRooter<JS::Value> msgArgsRooter(cx, &args);
    547  auto SetSingleStrAsArgs =
    548      [&](const nsString& message, dom::Sequence<JS::Value>* args)
    549          MOZ_CAN_RUN_SCRIPT {
    550            args->Clear();
    551            JS::Rooted<JSString*> jsStr(
    552                cx, JS_NewUCStringCopyN(cx, message.Data(), message.Length()));
    553            if (!jsStr) {
    554              return;
    555            }
    556            JS::Rooted<JS::Value> val(cx, JS::StringValue(jsStr));
    557            if (!args->AppendElement(val, fallible)) {
    558              return;
    559            }
    560          };
    561 
    562  nsString label;
    563  aShaderModule->GetLabel(label);
    564  auto appendNiceLabelIfPresent = [&label](nsString* buf) MOZ_CAN_RUN_SCRIPT {
    565    if (!label.IsEmpty()) {
    566      buf->AppendLiteral(u" \"");
    567      buf->Append(label);
    568      buf->AppendLiteral(u"\"");
    569    }
    570  };
    571 
    572  // We haven't actually inspected a message for severity, but
    573  // it doesn't actually matter, since we don't do anything at
    574  // this level.
    575  auto highestSeveritySeen = WebGPUCompilationMessageType::Info;
    576  uint64_t errorCount = 0;
    577  uint64_t warningCount = 0;
    578  uint64_t infoCount = 0;
    579  for (const auto& message : aMessages) {
    580    bool higherThanSeen =
    581        static_cast<std::underlying_type_t<WebGPUCompilationMessageType>>(
    582            message.messageType) <
    583        static_cast<std::underlying_type_t<WebGPUCompilationMessageType>>(
    584            highestSeveritySeen);
    585    if (higherThanSeen) {
    586      highestSeveritySeen = message.messageType;
    587    }
    588    switch (message.messageType) {
    589      case WebGPUCompilationMessageType::Error:
    590        errorCount += 1;
    591        break;
    592      case WebGPUCompilationMessageType::Warning:
    593        warningCount += 1;
    594        break;
    595      case WebGPUCompilationMessageType::Info:
    596        infoCount += 1;
    597        break;
    598    }
    599  }
    600  switch (highestSeveritySeen) {
    601    case WebGPUCompilationMessageType::Info:
    602      // shouldn't happen, but :shrug:
    603      break;
    604    case WebGPUCompilationMessageType::Warning: {
    605      nsString msg(
    606          u"Encountered one or more warnings while creating shader module");
    607      appendNiceLabelIfPresent(&msg);
    608      SetSingleStrAsArgs(msg, &args);
    609      dom::Console::Warn(globalObj, args);
    610      break;
    611    }
    612    case WebGPUCompilationMessageType::Error: {
    613      nsString msg(
    614          u"Encountered one or more errors while creating shader module");
    615      appendNiceLabelIfPresent(&msg);
    616      SetSingleStrAsArgs(msg, &args);
    617      dom::Console::Error(globalObj, args);
    618      break;
    619    }
    620  }
    621 
    622  nsString header;
    623  header.AppendLiteral(u"WebGPU compilation info for shader module");
    624  appendNiceLabelIfPresent(&header);
    625  header.AppendLiteral(u" (");
    626  header.AppendInt(errorCount);
    627  header.AppendLiteral(u" error(s), ");
    628  header.AppendInt(warningCount);
    629  header.AppendLiteral(u" warning(s), ");
    630  header.AppendInt(infoCount);
    631  header.AppendLiteral(u" info)");
    632  SetSingleStrAsArgs(header, &args);
    633  dom::Console::GroupCollapsed(globalObj, args);
    634 
    635  for (const auto& message : aMessages) {
    636    SetSingleStrAsArgs(message.message, &args);
    637    switch (message.messageType) {
    638      case WebGPUCompilationMessageType::Error:
    639        dom::Console::Error(globalObj, args);
    640        break;
    641      case WebGPUCompilationMessageType::Warning:
    642        dom::Console::Warn(globalObj, args);
    643        break;
    644      case WebGPUCompilationMessageType::Info:
    645        dom::Console::Info(globalObj, args);
    646        break;
    647    }
    648  }
    649  dom::Console::GroupEnd(globalObj);
    650 }
    651 
    652 already_AddRefed<ShaderModule> Device::CreateShaderModule(
    653    const dom::GPUShaderModuleDescriptor& aDesc, ErrorResult& aRv) {
    654  RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv);
    655  if (NS_WARN_IF(aRv.Failed())) {
    656    return nullptr;
    657  }
    658 
    659  webgpu::StringHelper label(aDesc.mLabel);
    660 
    661  RawId moduleId = ffi::wgpu_client_create_shader_module(
    662      GetClient(), GetId(), label.Get(), &aDesc.mCode);
    663 
    664  RefPtr<ShaderModule> shaderModule = new ShaderModule(this, moduleId, promise);
    665 
    666  shaderModule->SetLabel(aDesc.mLabel);
    667 
    668  auto pending_promise = WebGPUChild::PendingCreateShaderModulePromise{
    669      RefPtr(promise), RefPtr(this), RefPtr(shaderModule)};
    670  GetChild()->mPendingCreateShaderModulePromises.push_back(
    671      std::move(pending_promise));
    672 
    673  return shaderModule.forget();
    674 }
    675 
    676 RawId CreateComputePipelineImpl(RawId deviceId, WebGPUChild* aChild,
    677                                const dom::GPUComputePipelineDescriptor& aDesc,
    678                                bool isAsync) {
    679  ffi::WGPUComputePipelineDescriptor desc = {};
    680  nsCString entryPoint;
    681  nsTArray<nsCString> constantKeys;
    682  nsTArray<ffi::WGPUConstantEntry> constants;
    683 
    684  webgpu::StringHelper label(aDesc.mLabel);
    685  desc.label = label.Get();
    686 
    687  if (aDesc.mLayout.IsGPUAutoLayoutMode()) {
    688    desc.layout = 0;
    689  } else if (aDesc.mLayout.IsGPUPipelineLayout()) {
    690    desc.layout = aDesc.mLayout.GetAsGPUPipelineLayout()->GetId();
    691  } else {
    692    MOZ_ASSERT_UNREACHABLE();
    693  }
    694  desc.stage.module = aDesc.mCompute.mModule->GetId();
    695  if (aDesc.mCompute.mEntryPoint.WasPassed()) {
    696    CopyUTF16toUTF8(aDesc.mCompute.mEntryPoint.Value(), entryPoint);
    697    desc.stage.entry_point = entryPoint.get();
    698  } else {
    699    desc.stage.entry_point = nullptr;
    700  }
    701  if (aDesc.mCompute.mConstants.WasPassed()) {
    702    const auto& descConstants = aDesc.mCompute.mConstants.Value().Entries();
    703    constantKeys.SetCapacity(descConstants.Length());
    704    constants.SetCapacity(descConstants.Length());
    705    for (const auto& entry : descConstants) {
    706      ffi::WGPUConstantEntry constantEntry = {};
    707      nsCString key = NS_ConvertUTF16toUTF8(entry.mKey);
    708      constantKeys.AppendElement(key);
    709      constantEntry.key = key.get();
    710      constantEntry.value = entry.mValue;
    711      constants.AppendElement(constantEntry);
    712    }
    713    desc.stage.constants = {constants.Elements(), constants.Length()};
    714  }
    715 
    716  RawId id = ffi::wgpu_client_create_compute_pipeline(aChild->GetClient(),
    717                                                      deviceId, &desc, isAsync);
    718 
    719  return id;
    720 }
    721 
    722 RawId CreateRenderPipelineImpl(RawId deviceId, WebGPUChild* aChild,
    723                               const dom::GPURenderPipelineDescriptor& aDesc,
    724                               bool isAsync) {
    725  // A bunch of stack locals that we can have pointers into
    726  nsTArray<ffi::WGPUVertexBufferLayout> vertexBuffers;
    727  nsTArray<ffi::WGPUVertexAttribute> vertexAttributes;
    728  ffi::WGPURenderPipelineDescriptor desc = {};
    729  nsCString vsEntry, fsEntry;
    730  nsTArray<nsCString> vsConstantKeys, fsConstantKeys;
    731  nsTArray<ffi::WGPUConstantEntry> vsConstants, fsConstants;
    732  ffi::WGPUIndexFormat stripIndexFormat = ffi::WGPUIndexFormat_Uint16;
    733  ffi::WGPUFace cullFace = ffi::WGPUFace_Front;
    734  ffi::WGPUVertexState vertexState = {};
    735  ffi::WGPUFragmentState fragmentState = {};
    736  nsTArray<ffi::WGPUColorTargetState> colorStates;
    737  nsTArray<ffi::WGPUBlendState> blendStates;
    738 
    739  webgpu::StringHelper label(aDesc.mLabel);
    740  desc.label = label.Get();
    741 
    742  if (aDesc.mLayout.IsGPUAutoLayoutMode()) {
    743    desc.layout = 0;
    744  } else if (aDesc.mLayout.IsGPUPipelineLayout()) {
    745    desc.layout = aDesc.mLayout.GetAsGPUPipelineLayout()->GetId();
    746  } else {
    747    MOZ_ASSERT_UNREACHABLE();
    748  }
    749 
    750  {
    751    const auto& stage = aDesc.mVertex;
    752    vertexState.stage.module = stage.mModule->GetId();
    753    if (stage.mEntryPoint.WasPassed()) {
    754      CopyUTF16toUTF8(stage.mEntryPoint.Value(), vsEntry);
    755      vertexState.stage.entry_point = vsEntry.get();
    756    } else {
    757      vertexState.stage.entry_point = nullptr;
    758    }
    759    if (stage.mConstants.WasPassed()) {
    760      const auto& descConstants = stage.mConstants.Value().Entries();
    761      vsConstantKeys.SetCapacity(descConstants.Length());
    762      vsConstants.SetCapacity(descConstants.Length());
    763      for (const auto& entry : descConstants) {
    764        ffi::WGPUConstantEntry constantEntry = {};
    765        nsCString key = NS_ConvertUTF16toUTF8(entry.mKey);
    766        vsConstantKeys.AppendElement(key);
    767        constantEntry.key = key.get();
    768        constantEntry.value = entry.mValue;
    769        vsConstants.AppendElement(constantEntry);
    770      }
    771      vertexState.stage.constants = {vsConstants.Elements(),
    772                                     vsConstants.Length()};
    773    }
    774 
    775    for (const auto& vertex_desc : stage.mBuffers) {
    776      ffi::WGPUVertexBufferLayout vb_desc = {};
    777      if (!vertex_desc.IsNull()) {
    778        const auto& vd = vertex_desc.Value();
    779        vb_desc.array_stride = vd.mArrayStride;
    780        vb_desc.step_mode = ffi::WGPUVertexStepMode(vd.mStepMode);
    781        // Note: we are setting the length but not the pointer
    782        vb_desc.attributes = {nullptr, vd.mAttributes.Length()};
    783        for (const auto& vat : vd.mAttributes) {
    784          ffi::WGPUVertexAttribute ad = {};
    785          ad.offset = vat.mOffset;
    786          ad.format = ConvertVertexFormat(vat.mFormat);
    787          ad.shader_location = vat.mShaderLocation;
    788          vertexAttributes.AppendElement(ad);
    789        }
    790      }
    791      vertexBuffers.AppendElement(vb_desc);
    792    }
    793    // Now patch up all the pointers to attribute lists.
    794    size_t numAttributes = 0;
    795    for (auto& vb_desc : vertexBuffers) {
    796      vb_desc.attributes.data = vertexAttributes.Elements() + numAttributes;
    797      numAttributes += vb_desc.attributes.length;
    798    }
    799 
    800    vertexState.buffers = {vertexBuffers.Elements(), vertexBuffers.Length()};
    801    desc.vertex = &vertexState;
    802  }
    803 
    804  if (aDesc.mFragment.WasPassed()) {
    805    const auto& stage = aDesc.mFragment.Value();
    806    fragmentState.stage.module = stage.mModule->GetId();
    807    if (stage.mEntryPoint.WasPassed()) {
    808      CopyUTF16toUTF8(stage.mEntryPoint.Value(), fsEntry);
    809      fragmentState.stage.entry_point = fsEntry.get();
    810    } else {
    811      fragmentState.stage.entry_point = nullptr;
    812    }
    813    if (stage.mConstants.WasPassed()) {
    814      const auto& descConstants = stage.mConstants.Value().Entries();
    815      fsConstantKeys.SetCapacity(descConstants.Length());
    816      fsConstants.SetCapacity(descConstants.Length());
    817      for (const auto& entry : descConstants) {
    818        ffi::WGPUConstantEntry constantEntry = {};
    819        nsCString key = NS_ConvertUTF16toUTF8(entry.mKey);
    820        fsConstantKeys.AppendElement(key);
    821        constantEntry.key = key.get();
    822        constantEntry.value = entry.mValue;
    823        fsConstants.AppendElement(constantEntry);
    824      }
    825      fragmentState.stage.constants = {fsConstants.Elements(),
    826                                       fsConstants.Length()};
    827    }
    828 
    829    // Note: we pre-collect the blend states into a different array
    830    // so that we can have non-stale pointers into it.
    831    for (const auto& colorState : stage.mTargets) {
    832      ffi::WGPUColorTargetState desc = {};
    833      desc.format = ConvertTextureFormat(colorState.mFormat);
    834      desc.write_mask = colorState.mWriteMask;
    835      colorStates.AppendElement(desc);
    836      ffi::WGPUBlendState bs = {};
    837      if (colorState.mBlend.WasPassed()) {
    838        const auto& blend = colorState.mBlend.Value();
    839        bs.alpha = ConvertBlendComponent(blend.mAlpha);
    840        bs.color = ConvertBlendComponent(blend.mColor);
    841      }
    842      blendStates.AppendElement(bs);
    843    }
    844    for (size_t i = 0; i < colorStates.Length(); ++i) {
    845      if (stage.mTargets[i].mBlend.WasPassed()) {
    846        colorStates[i].blend = &blendStates[i];
    847      }
    848    }
    849 
    850    fragmentState.targets = {colorStates.Elements(), colorStates.Length()};
    851    desc.fragment = &fragmentState;
    852  }
    853 
    854  {
    855    const auto& prim = aDesc.mPrimitive;
    856    desc.primitive.topology = ffi::WGPUPrimitiveTopology(prim.mTopology);
    857    if (prim.mStripIndexFormat.WasPassed()) {
    858      stripIndexFormat = ffi::WGPUIndexFormat(prim.mStripIndexFormat.Value());
    859      desc.primitive.strip_index_format = &stripIndexFormat;
    860    }
    861    desc.primitive.front_face = ffi::WGPUFrontFace(prim.mFrontFace);
    862    if (prim.mCullMode != dom::GPUCullMode::None) {
    863      cullFace = prim.mCullMode == dom::GPUCullMode::Front ? ffi::WGPUFace_Front
    864                                                           : ffi::WGPUFace_Back;
    865      desc.primitive.cull_mode = &cullFace;
    866    }
    867    desc.primitive.unclipped_depth = prim.mUnclippedDepth;
    868  }
    869  desc.multisample = ConvertMultisampleState(aDesc.mMultisample);
    870 
    871  ffi::WGPUDepthStencilState depthStencilState = {};
    872  if (aDesc.mDepthStencil.WasPassed()) {
    873    depthStencilState = ConvertDepthStencilState(aDesc.mDepthStencil.Value());
    874    desc.depth_stencil = &depthStencilState;
    875  }
    876 
    877  RawId id = ffi::wgpu_client_create_render_pipeline(aChild->GetClient(),
    878                                                     deviceId, &desc, isAsync);
    879 
    880  return id;
    881 }
    882 
    883 already_AddRefed<ComputePipeline> Device::CreateComputePipeline(
    884    const dom::GPUComputePipelineDescriptor& aDesc) {
    885  RawId pipelineId =
    886      CreateComputePipelineImpl(GetId(), GetChild(), aDesc, false);
    887 
    888  RefPtr<ComputePipeline> object = new ComputePipeline(this, pipelineId);
    889  object->SetLabel(aDesc.mLabel);
    890  return object.forget();
    891 }
    892 
    893 already_AddRefed<RenderPipeline> Device::CreateRenderPipeline(
    894    const dom::GPURenderPipelineDescriptor& aDesc) {
    895  RawId pipelineId =
    896      CreateRenderPipelineImpl(GetId(), GetChild(), aDesc, false);
    897 
    898  RefPtr<RenderPipeline> object = new RenderPipeline(this, pipelineId);
    899  object->SetLabel(aDesc.mLabel);
    900 
    901  return object.forget();
    902 }
    903 
    904 already_AddRefed<dom::Promise> Device::CreateComputePipelineAsync(
    905    const dom::GPUComputePipelineDescriptor& aDesc, ErrorResult& aRv) {
    906  RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv);
    907  if (NS_WARN_IF(aRv.Failed())) {
    908    return nullptr;
    909  }
    910 
    911  RawId pipelineId =
    912      CreateComputePipelineImpl(GetId(), GetChild(), aDesc, true);
    913 
    914  auto pending_promise = WebGPUChild::PendingCreatePipelinePromise{
    915      RefPtr(promise), RefPtr(this), false, pipelineId, aDesc.mLabel};
    916  GetChild()->mPendingCreatePipelinePromises.push_back(
    917      std::move(pending_promise));
    918 
    919  return promise.forget();
    920 }
    921 
    922 already_AddRefed<dom::Promise> Device::CreateRenderPipelineAsync(
    923    const dom::GPURenderPipelineDescriptor& aDesc, ErrorResult& aRv) {
    924  RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv);
    925  if (NS_WARN_IF(aRv.Failed())) {
    926    return nullptr;
    927  }
    928 
    929  RawId pipelineId = CreateRenderPipelineImpl(GetId(), GetChild(), aDesc, true);
    930 
    931  auto pending_promise = WebGPUChild::PendingCreatePipelinePromise{
    932      RefPtr(promise), RefPtr(this), true, pipelineId, aDesc.mLabel};
    933  GetChild()->mPendingCreatePipelinePromises.push_back(
    934      std::move(pending_promise));
    935 
    936  return promise.forget();
    937 }
    938 
    939 already_AddRefed<Texture> Device::InitSwapChain(
    940    const dom::GPUCanvasConfiguration* const aConfig,
    941    const layers::RemoteTextureOwnerId aOwnerId,
    942    bool aUseSharedTextureInSwapChain, gfx::SurfaceFormat aFormat,
    943    gfx::IntSize aCanvasSize) {
    944  MOZ_ASSERT(aConfig);
    945 
    946  // Check that aCanvasSize and aFormat will generate a texture stride
    947  // within limits.
    948  const auto bufferStrideWithMask = BufferStrideWithMask(aCanvasSize, aFormat);
    949  if (!bufferStrideWithMask.isValid()) {
    950    return nullptr;
    951  }
    952 
    953  const layers::RGBDescriptor rgbDesc(aCanvasSize, aFormat);
    954 
    955  ffi::wgpu_client_create_swap_chain(
    956      GetClient(), GetId(), mQueue->GetId(), rgbDesc.size().Width(),
    957      rgbDesc.size().Height(), (int8_t)rgbDesc.format(), aOwnerId.mId,
    958      aUseSharedTextureInSwapChain);
    959 
    960  // TODO: `mColorSpace`: <https://bugzilla.mozilla.org/show_bug.cgi?id=1846608>
    961  // TODO: `mAlphaMode`: <https://bugzilla.mozilla.org/show_bug.cgi?id=1846605>
    962  return CreateTextureForSwapChain(aConfig, aCanvasSize, aOwnerId);
    963 }
    964 
    965 bool Device::CheckNewWarning(const nsACString& aMessage) {
    966  return mKnownWarnings.EnsureInserted(aMessage);
    967 }
    968 
    969 void Device::Destroy() {
    970  // Unmap all buffers from this device, as specified by
    971  // https://gpuweb.github.io/gpuweb/#dom-gpudevice-destroy.
    972  dom::AutoJSAPI jsapi;
    973  if (jsapi.Init(GetOwnerGlobal())) {
    974    IgnoredErrorResult rv;
    975    for (const auto& buffer : mTrackedBuffers) {
    976      buffer->Unmap(jsapi.cx(), rv);
    977    }
    978 
    979    mTrackedBuffers.Clear();
    980  }
    981 
    982  ffi::wgpu_client_destroy_device(GetClient(), GetId());
    983 
    984  if (mLostPromise->State() != dom::Promise::PromiseState::Pending) {
    985    return;
    986  }
    987  RefPtr<dom::Promise> pending_promise = mLostPromise;
    988  GetChild()->mPendingDeviceLostPromises.insert(
    989      {GetId(), std::move(pending_promise)});
    990 }
    991 
    992 void Device::PushErrorScope(const dom::GPUErrorFilter& aFilter) {
    993  ffi::wgpu_client_push_error_scope(GetClient(), GetId(), (uint8_t)aFilter);
    994 }
    995 
    996 already_AddRefed<dom::Promise> Device::PopErrorScope(ErrorResult& aRv) {
    997  RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv);
    998  if (NS_WARN_IF(aRv.Failed())) {
    999    return nullptr;
   1000  }
   1001 
   1002  ffi::wgpu_client_pop_error_scope(GetClient(), GetId());
   1003 
   1004  auto pending_promise =
   1005      WebGPUChild::PendingPopErrorScopePromise{RefPtr(promise), RefPtr(this)};
   1006  GetChild()->mPendingPopErrorScopePromises.push_back(
   1007      std::move(pending_promise));
   1008 
   1009  return promise.forget();
   1010 }
   1011 
   1012 }  // namespace mozilla::webgpu