tor-browser

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

WebGPUParent.cpp (64037B)


      1 /* -*- Mode: C++; tab-width: 20; 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 "WebGPUParent.h"
      7 
      8 #include <unordered_set>
      9 
     10 #include "ExternalTexture.h"
     11 #include "mozilla/ScopeExit.h"
     12 #include "mozilla/dom/WebGPUBinding.h"
     13 #include "mozilla/gfx/FileHandleWrapper.h"
     14 #include "mozilla/gfx/gfxVars.h"
     15 #include "mozilla/layers/CompositorThread.h"
     16 #include "mozilla/layers/ImageDataSerializer.h"
     17 #include "mozilla/layers/RemoteTextureMap.h"
     18 #include "mozilla/layers/TextureHost.h"
     19 #include "mozilla/layers/WebRenderImageHost.h"
     20 #include "mozilla/layers/WebRenderTextureHost.h"
     21 #include "mozilla/webgpu/SharedTexture.h"
     22 #include "mozilla/webgpu/ffi/wgpu.h"
     23 
     24 #if defined(XP_WIN)
     25 #  include "mozilla/gfx/DeviceManagerDx.h"
     26 #  include "mozilla/webgpu/SharedTextureD3D11.h"
     27 #endif
     28 
     29 #if defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)
     30 #  include "mozilla/webgpu/SharedTextureDMABuf.h"
     31 #endif
     32 
     33 #if defined(XP_MACOSX)
     34 #  include "mozilla/webgpu/SharedTextureMacIOSurface.h"
     35 #endif
     36 
     37 namespace mozilla::webgpu {
     38 
     39 const uint64_t POLL_TIME_MS = 100;
     40 
     41 static mozilla::LazyLogModule sLogger("WebGPU");
     42 
     43 namespace ffi {
     44 
     45 extern bool wgpu_server_use_shared_texture_for_swap_chain(
     46    WGPUWebGPUParentPtr aParent, WGPUSwapChainId aSwapChainId) {
     47  auto* parent = static_cast<WebGPUParent*>(aParent);
     48 
     49  return parent->UseSharedTextureForSwapChain(aSwapChainId);
     50 }
     51 
     52 extern void wgpu_server_disable_shared_texture_for_swap_chain(
     53    WGPUWebGPUParentPtr aParent, WGPUSwapChainId aSwapChainId) {
     54  auto* parent = static_cast<WebGPUParent*>(aParent);
     55 
     56  parent->DisableSharedTextureForSwapChain(aSwapChainId);
     57 }
     58 
     59 extern bool wgpu_server_ensure_shared_texture_for_swap_chain(
     60    WGPUWebGPUParentPtr aParent, WGPUSwapChainId aSwapChainId,
     61    WGPUDeviceId aDeviceId, WGPUTextureId aTextureId, uint32_t aWidth,
     62    uint32_t aHeight, struct WGPUTextureFormat aFormat,
     63    WGPUTextureUsages aUsage) {
     64  auto* parent = static_cast<WebGPUParent*>(aParent);
     65 
     66  return parent->EnsureSharedTextureForSwapChain(
     67      aSwapChainId, aDeviceId, aTextureId, aWidth, aHeight, aFormat, aUsage);
     68 }
     69 
     70 extern void wgpu_server_ensure_shared_texture_for_readback(
     71    WGPUWebGPUParentPtr aParent, WGPUSwapChainId aSwapChainId,
     72    WGPUDeviceId aDeviceId, WGPUTextureId aTextureId, uint32_t aWidth,
     73    uint32_t aHeight, struct WGPUTextureFormat aFormat,
     74    WGPUTextureUsages aUsage) {
     75  auto* parent = static_cast<WebGPUParent*>(aParent);
     76 
     77  parent->EnsureSharedTextureForReadBackPresent(
     78      aSwapChainId, aDeviceId, aTextureId, aWidth, aHeight, aFormat, aUsage);
     79 }
     80 
     81 #ifdef XP_WIN
     82 extern void* wgpu_server_get_shared_texture_handle(WGPUWebGPUParentPtr aParent,
     83                                                   WGPUTextureId aId) {
     84  auto* parent = static_cast<WebGPUParent*>(aParent);
     85 
     86  auto texture = parent->GetSharedTexture(aId);
     87  if (!texture) {
     88    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
     89    return nullptr;
     90  }
     91 
     92  auto* textureD3D11 = texture->AsSharedTextureD3D11();
     93  if (!textureD3D11) {
     94    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
     95    return nullptr;
     96  }
     97  void* sharedHandle = textureD3D11->GetSharedTextureHandle();
     98  if (!sharedHandle) {
     99    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
    100    gfxCriticalNoteOnce << "Failed to get shared handle";
    101    return nullptr;
    102  }
    103 
    104  return sharedHandle;
    105 }
    106 #endif
    107 
    108 #if defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)
    109 extern int32_t wgpu_server_get_dma_buf_fd(WGPUWebGPUParentPtr aParent,
    110                                          WGPUTextureId aId) {
    111  auto* parent = static_cast<WebGPUParent*>(aParent);
    112 
    113  auto texture = parent->GetSharedTexture(aId);
    114  if (!texture) {
    115    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
    116    return -1;
    117  }
    118 
    119  auto* textureDMABuf = texture->AsSharedTextureDMABuf();
    120  if (!textureDMABuf) {
    121    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
    122    return -1;
    123  }
    124  auto fd = textureDMABuf->CloneDmaBufFd();
    125  // fd should be closed by the caller.
    126  return fd.release();
    127 }
    128 #endif
    129 
    130 #if defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)
    131 extern const WGPUVkImageHandle* wgpu_server_get_vk_image_handle(
    132    WGPUWebGPUParentPtr aParent, WGPUTextureId aId) {
    133  auto* parent = static_cast<WebGPUParent*>(aParent);
    134 
    135  auto texture = parent->GetSharedTexture(aId);
    136  if (!texture) {
    137    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
    138    return nullptr;
    139  }
    140 
    141  auto* textureDMABuf = texture->AsSharedTextureDMABuf();
    142  if (!textureDMABuf) {
    143    return nullptr;
    144  }
    145  return textureDMABuf->GetHandle();
    146 }
    147 #endif
    148 
    149 #if defined(XP_MACOSX)
    150 extern uint32_t wgpu_server_get_external_io_surface_id(
    151    WGPUWebGPUParentPtr aParent, WGPUTextureId aId) {
    152  auto* parent = static_cast<WebGPUParent*>(aParent);
    153 
    154  auto texture = parent->GetSharedTexture(aId);
    155  if (!texture) {
    156    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
    157    return 0;
    158  }
    159 
    160  auto* textureIOSurface = texture->AsSharedTextureMacIOSurface();
    161  if (!textureIOSurface) {
    162    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
    163    return 0;
    164  }
    165  return textureIOSurface->GetIOSurfaceId();
    166 }
    167 #endif
    168 
    169 extern void wgpu_server_remove_shared_texture(WGPUWebGPUParentPtr aParent,
    170                                              WGPUTextureId aId) {
    171  auto* parent = static_cast<WebGPUParent*>(aParent);
    172  parent->RemoveSharedTexture(aId);
    173 }
    174 
    175 extern bool wgpu_parent_is_external_texture_enabled() {
    176  return gfx::gfxVars::AllowWebGPUExternalTexture();
    177 }
    178 
    179 extern ffi::WGPUExternalTextureDescriptorFromSource
    180 wgpu_parent_external_texture_source_get_external_texture_descriptor(
    181    void* aParent, WGPUExternalTextureSourceId aId,
    182    ffi::WGPUPredefinedColorSpace aDestColorSpace) {
    183  auto* parent = static_cast<WebGPUParent*>(aParent);
    184  const auto& source = parent->GetExternalTextureSource(aId);
    185  return source.GetExternalTextureDescriptor(aDestColorSpace);
    186 }
    187 
    188 extern void wgpu_parent_destroy_external_texture_source(
    189    WGPUWebGPUParentPtr aParent, WGPUExternalTextureSourceId aId) {
    190  auto* const parent = static_cast<WebGPUParent*>(aParent);
    191  parent->DestroyExternalTextureSource(aId);
    192 }
    193 
    194 extern void wgpu_parent_drop_external_texture_source(
    195    WGPUWebGPUParentPtr aParent, WGPUExternalTextureSourceId aId) {
    196  auto* const parent = static_cast<WebGPUParent*>(aParent);
    197  parent->DropExternalTextureSource(aId);
    198 }
    199 
    200 extern void wgpu_server_dealloc_buffer_shmem(WGPUWebGPUParentPtr aParent,
    201                                             WGPUBufferId aId) {
    202  auto* parent = static_cast<WebGPUParent*>(aParent);
    203  parent->DeallocBufferShmem(aId);
    204 }
    205 
    206 extern void wgpu_server_pre_device_drop(WGPUWebGPUParentPtr aParent,
    207                                        WGPUDeviceId aId) {
    208  auto* parent = static_cast<WebGPUParent*>(aParent);
    209  parent->PreDeviceDrop(aId);
    210 }
    211 
    212 extern void wgpu_server_set_buffer_map_data(
    213    WGPUWebGPUParentPtr aParent, WGPUDeviceId aDeviceId, WGPUBufferId aBufferId,
    214    bool aHasMapFlags, uint64_t aMappedOffset, uint64_t aMappedSize,
    215    uintptr_t aShmemIndex) {
    216  auto* parent = static_cast<WebGPUParent*>(aParent);
    217 
    218  auto mapping = parent->mTempMappings.at(aShmemIndex);
    219 
    220  auto data = WebGPUParent::BufferMapData{
    221      mapping, aHasMapFlags, aMappedOffset, aMappedSize, aDeviceId,
    222  };
    223 
    224  parent->mSharedMemoryMap.insert({aBufferId, std::move(data)});
    225 }
    226 
    227 extern void wgpu_server_device_push_error_scope(WGPUWebGPUParentPtr aParent,
    228                                                WGPUDeviceId aDeviceId,
    229                                                uint8_t aFilter) {
    230  auto* parent = static_cast<WebGPUParent*>(aParent);
    231  parent->DevicePushErrorScope(aDeviceId, (dom::GPUErrorFilter)aFilter);
    232 }
    233 
    234 extern void wgpu_server_device_pop_error_scope(WGPUWebGPUParentPtr aParent,
    235                                               WGPUDeviceId aDeviceId,
    236                                               uint8_t* aOutType,
    237                                               nsCString* aOutMessage) {
    238  auto* parent = static_cast<WebGPUParent*>(aParent);
    239  auto result = parent->DevicePopErrorScope(aDeviceId);
    240  *aOutType = (uint8_t)result.resultType;
    241  *aOutMessage = std::move(result.message);
    242 }
    243 
    244 extern void wgpu_parent_buffer_unmap(WGPUWebGPUParentPtr aParent,
    245                                     WGPUDeviceId aDeviceId,
    246                                     WGPUBufferId aBufferId, bool aFlush) {
    247  auto* parent = static_cast<WebGPUParent*>(aParent);
    248  parent->BufferUnmap(aDeviceId, aBufferId, aFlush);
    249 }
    250 
    251 extern void wgpu_parent_queue_submit(
    252    WGPUWebGPUParentPtr aParent, WGPUDeviceId aDeviceId, WGPUQueueId aQueueId,
    253    const WGPUCommandBufferId* aCommandBufferIds,
    254    uintptr_t aCommandBufferIdsLength, const WGPUTextureId* aTextureIds,
    255    uintptr_t aTextureIdsLength,
    256    const WGPUExternalTextureSourceId* aExternalTextureSourceIds,
    257    uintptr_t aExternalTextureSourceIdsLength) {
    258  auto* parent = static_cast<WebGPUParent*>(aParent);
    259  auto command_buffers = Span(aCommandBufferIds, aCommandBufferIdsLength);
    260  auto textures = Span(aTextureIds, aTextureIdsLength);
    261  auto externalTextureSources =
    262      Span(aExternalTextureSourceIds, aExternalTextureSourceIdsLength);
    263  parent->QueueSubmit(aQueueId, aDeviceId, command_buffers, textures,
    264                      externalTextureSources);
    265 }
    266 
    267 extern void wgpu_parent_create_swap_chain(
    268    WGPUWebGPUParentPtr aParent, WGPUDeviceId aDeviceId, WGPUQueueId aQueueId,
    269    int32_t aWidth, int32_t aHeight, WGPUSurfaceFormat aFormat,
    270    const WGPUBufferId* aBufferIds, uintptr_t aBufferIdsLength,
    271    WGPURemoteTextureOwnerId aRemoteTextureOwnerId,
    272    bool aUseSharedTextureInSwapChain) {
    273  auto* parent = static_cast<WebGPUParent*>(aParent);
    274  auto buffer_ids_span = Span(aBufferIds, aBufferIdsLength);
    275  auto buffer_ids = nsTArray<RawId>(aBufferIdsLength);
    276  for (const RawId id : buffer_ids_span) {
    277    buffer_ids.AppendElement(id);
    278  }
    279  auto size = gfx::IntSize(aWidth, aHeight);
    280  auto format = gfx::SurfaceFormat(aFormat);
    281  auto desc = layers::RGBDescriptor(size, format);
    282  auto owner = layers::RemoteTextureOwnerId{aRemoteTextureOwnerId};
    283  parent->DeviceCreateSwapChain(aDeviceId, aQueueId, desc, buffer_ids, owner,
    284                                aUseSharedTextureInSwapChain);
    285 }
    286 
    287 extern void wgpu_parent_swap_chain_present(
    288    WGPUWebGPUParentPtr aParent, WGPUTextureId aTextureId,
    289    WGPUCommandEncoderId aCommandEncoderId,
    290    WGPUCommandBufferId aCommandBufferId, WGPURemoteTextureId aRemoteTextureId,
    291    WGPURemoteTextureOwnerId aRemoteTextureOwnerId) {
    292  auto* parent = static_cast<WebGPUParent*>(aParent);
    293  auto remote_texture = layers::RemoteTextureId{aRemoteTextureId};
    294  auto owner = layers::RemoteTextureOwnerId{aRemoteTextureOwnerId};
    295  parent->SwapChainPresent(aTextureId, aCommandEncoderId, aCommandBufferId,
    296                           remote_texture, owner);
    297 }
    298 
    299 extern void wgpu_parent_swap_chain_drop(
    300    WGPUWebGPUParentPtr aParent, WGPURemoteTextureOwnerId aRemoteTextureOwnerId,
    301    WGPURemoteTextureTxnType aTxnType, WGPURemoteTextureTxnId aTxnId) {
    302  auto* parent = static_cast<WebGPUParent*>(aParent);
    303  auto owner = layers::RemoteTextureOwnerId{aRemoteTextureOwnerId};
    304  parent->SwapChainDrop(owner, aTxnType, aTxnId);
    305 }
    306 
    307 #ifdef XP_WIN
    308 extern void wgpu_parent_get_compositor_device_luid(
    309    struct WGPUFfiLUID* aOutLuid) {
    310  auto luid = WebGPUParent::GetCompositorDeviceLuid();
    311  if (luid.isSome()) {
    312    *aOutLuid = luid.extract();
    313  } else {
    314    aOutLuid = nullptr;
    315  }
    316 }
    317 #endif
    318 
    319 extern void wgpu_parent_post_request_device(WGPUWebGPUParentPtr aParent,
    320                                            WGPUDeviceId aDeviceId) {
    321  auto* parent = static_cast<WebGPUParent*>(aParent);
    322  parent->PostAdapterRequestDevice(aDeviceId);
    323 }
    324 
    325 extern ffi::WGPUBufferMapClosure wgpu_parent_build_buffer_map_closure(
    326    WGPUWebGPUParentPtr aParent, RawId aDeviceId, RawId aBufferId,
    327    ffi::WGPUHostMap aMode, uint64_t aOffset, uint64_t aSize) {
    328  auto* parent = static_cast<WebGPUParent*>(aParent);
    329 
    330  std::unique_ptr<WebGPUParent::MapRequest> request(
    331      new WebGPUParent::MapRequest{parent, aDeviceId, aBufferId, aMode, aOffset,
    332                                   aSize});
    333 
    334  ffi::WGPUBufferMapClosure closure = {
    335      &WebGPUParent::MapCallback,
    336      reinterpret_cast<uint8_t*>(request.release())};
    337 
    338  return closure;
    339 }
    340 
    341 extern ffi::WGPUSubmittedWorkDoneClosure
    342 wgpu_parent_build_submitted_work_done_closure(WGPUWebGPUParentPtr aParent,
    343                                              WGPUQueueId aQueueId) {
    344  auto* parent = static_cast<WebGPUParent*>(aParent);
    345 
    346  std::unique_ptr<WebGPUParent::OnSubmittedWorkDoneRequest> request(
    347      new WebGPUParent::OnSubmittedWorkDoneRequest{parent, aQueueId});
    348 
    349  ffi::WGPUSubmittedWorkDoneClosure closure = {
    350      &WebGPUParent::OnSubmittedWorkDoneCallback,
    351      reinterpret_cast<uint8_t*>(request.release())};
    352 
    353  return closure;
    354 }
    355 
    356 extern void wgpu_parent_handle_error(WGPUWebGPUParentPtr aParent,
    357                                     WGPUDeviceId aDeviceId,
    358                                     WGPUErrorBufferType aTy,
    359                                     const nsCString* aMessage) {
    360  auto* parent = static_cast<WebGPUParent*>(aParent);
    361 
    362  dom::GPUErrorFilter ty;
    363  switch (aTy) {
    364    case ffi::WGPUErrorBufferType_Internal:
    365      ty = dom::GPUErrorFilter::Internal;
    366      break;
    367    case ffi::WGPUErrorBufferType_Validation:
    368      ty = dom::GPUErrorFilter::Validation;
    369      break;
    370    case ffi::WGPUErrorBufferType_OutOfMemory:
    371      ty = dom::GPUErrorFilter::Out_of_memory;
    372      break;
    373    default:
    374      MOZ_CRASH("invalid `ErrorBufferType`");
    375  }
    376 
    377  parent->ReportError(aDeviceId, ty, *aMessage);
    378 }
    379 
    380 extern void wgpu_parent_send_server_message(WGPUWebGPUParentPtr aParent,
    381                                            struct WGPUByteBuf* aMessage) {
    382  auto* parent = static_cast<WebGPUParent*>(aParent);
    383  auto* message = FromFFI(aMessage);
    384  if (!parent->SendServerMessage(std::move(*message))) {
    385    NS_ERROR("SendServerMessage failed");
    386  }
    387 }
    388 
    389 }  // namespace ffi
    390 
    391 ErrorBuffer::ErrorBuffer() { mMessageUtf8[0] = 0; }
    392 
    393 ErrorBuffer::~ErrorBuffer() { MOZ_ASSERT(!mAwaitingGetError); }
    394 
    395 ffi::WGPUErrorBuffer ErrorBuffer::ToFFI() {
    396  mAwaitingGetError = true;
    397  ffi::WGPUErrorBuffer errorBuf = {&mType, mMessageUtf8, BUFFER_SIZE,
    398                                   &mDeviceId};
    399  return errorBuf;
    400 }
    401 
    402 ffi::WGPUErrorBufferType ErrorBuffer::GetType() { return mType; }
    403 
    404 Maybe<dom::GPUErrorFilter> ErrorBuffer::ErrorTypeToFilterType(
    405    ffi::WGPUErrorBufferType aType) {
    406  switch (aType) {
    407    case ffi::WGPUErrorBufferType_None:
    408    case ffi::WGPUErrorBufferType_DeviceLost:
    409      return {};
    410    case ffi::WGPUErrorBufferType_Internal:
    411      return Some(dom::GPUErrorFilter::Internal);
    412    case ffi::WGPUErrorBufferType_Validation:
    413      return Some(dom::GPUErrorFilter::Validation);
    414    case ffi::WGPUErrorBufferType_OutOfMemory:
    415      return Some(dom::GPUErrorFilter::Out_of_memory);
    416    case ffi::WGPUErrorBufferType_Sentinel:
    417      break;
    418  }
    419 
    420  MOZ_CRASH("invalid `ErrorBufferType`");
    421 }
    422 
    423 Maybe<ErrorBuffer::Error> ErrorBuffer::GetError() {
    424  mAwaitingGetError = false;
    425  if (mType == ffi::WGPUErrorBufferType_DeviceLost) {
    426    // This error is for a lost device, so we return an Error struct
    427    // with the isDeviceLost bool set to true. It doesn't matter what
    428    // GPUErrorFilter type we use, so we just use Validation. The error
    429    // will not be reported.
    430    return Some(Error{dom::GPUErrorFilter::Validation, true,
    431                      nsCString{mMessageUtf8}, mDeviceId});
    432  }
    433  auto filterType = ErrorTypeToFilterType(mType);
    434  if (!filterType) {
    435    return {};
    436  }
    437  return Some(Error{*filterType, false, nsCString{mMessageUtf8}, mDeviceId});
    438 }
    439 
    440 void ErrorBuffer::CoerceValidationToInternal() {
    441  if (mType == ffi::WGPUErrorBufferType_Validation) {
    442    mType = ffi::WGPUErrorBufferType_Internal;
    443  }
    444 }
    445 
    446 struct PendingSwapChainDrop {
    447  layers::RemoteTextureTxnType mTxnType;
    448  layers::RemoteTextureTxnId mTxnId;
    449 };
    450 
    451 class PresentationData {
    452  NS_INLINE_DECL_REFCOUNTING(PresentationData);
    453 
    454 public:
    455  WeakPtr<WebGPUParent> mParent;
    456  bool mUseSharedTextureInSwapChain;
    457  const RawId mDeviceId;
    458  const RawId mQueueId;
    459  Maybe<RawId> mLastSubmittedTextureId;
    460  const layers::RGBDescriptor mDesc;
    461 
    462  uint64_t mSubmissionIndex = 0;
    463 
    464  std::deque<std::shared_ptr<SharedTexture>> mRecycledSharedTextures;
    465 
    466  std::unordered_set<layers::RemoteTextureId, layers::RemoteTextureId::HashFn>
    467      mWaitingReadbackTexturesForPresent;
    468  Maybe<PendingSwapChainDrop> mPendingSwapChainDrop;
    469 
    470  const uint32_t mSourcePitch;
    471  std::vector<RawId> mUnassignedBufferIds;
    472  std::vector<RawId> mAvailableBufferIds;
    473  std::vector<RawId> mQueuedBufferIds;
    474 
    475  bool mReadbackSnapshotCallbackCalled = false;
    476 
    477  PresentationData(WebGPUParent* aParent, bool aUseSharedTextureInSwapChain,
    478                   RawId aDeviceId, RawId aQueueId,
    479                   const layers::RGBDescriptor& aDesc, uint32_t aSourcePitch,
    480                   const nsTArray<RawId>& aBufferIds)
    481      : mParent(aParent),
    482        mUseSharedTextureInSwapChain(aUseSharedTextureInSwapChain),
    483        mDeviceId(aDeviceId),
    484        mQueueId(aQueueId),
    485        mDesc(aDesc),
    486        mSourcePitch(aSourcePitch) {
    487    MOZ_COUNT_CTOR(PresentationData);
    488 
    489    for (const RawId id : aBufferIds) {
    490      mUnassignedBufferIds.push_back(id);
    491    }
    492  }
    493 
    494 private:
    495  ~PresentationData() { MOZ_COUNT_DTOR(PresentationData); }
    496 };
    497 
    498 WebGPUParent::WebGPUParent(const dom::ContentParentId& aContentId)
    499    : mContentId(aContentId), mContext(ffi::wgpu_server_new(this)) {
    500  mTimer.Start(base::TimeDelta::FromMilliseconds(POLL_TIME_MS), this,
    501               &WebGPUParent::MaintainDevices);
    502 }
    503 
    504 WebGPUParent::~WebGPUParent() = default;
    505 
    506 void WebGPUParent::MaintainDevices() {
    507  ffi::wgpu_server_poll_all_devices(mContext.get(), false);
    508 }
    509 
    510 void WebGPUParent::LoseDevice(const RawId aDeviceId, uint8_t aReason,
    511                              const nsACString& aMessage) {
    512  if (mActiveDeviceIds.Contains(aDeviceId)) {
    513    mActiveDeviceIds.Remove(aDeviceId);
    514  }
    515  // Check to see if we've already sent a DeviceLost message to aDeviceId.
    516  if (mLostDeviceIds.Contains(aDeviceId)) {
    517    return;
    518  }
    519 
    520  // If the connection has been dropped, there is nobody to receive
    521  // the DeviceLost message anyway.
    522  if (!CanSend()) {
    523    return;
    524  }
    525 
    526  if (!SendDeviceLost(aDeviceId, aReason, aMessage)) {
    527    NS_ERROR("SendDeviceLost failed");
    528    return;
    529  }
    530 
    531  mLostDeviceIds.Insert(aDeviceId);
    532 }
    533 
    534 bool WebGPUParent::ForwardError(ErrorBuffer& aError) {
    535  if (auto error = aError.GetError()) {
    536    // If this is a error has isDeviceLost true, then instead of reporting
    537    // the error, we swallow it and call LoseDevice if we have an
    538    // aDeviceID. This is to comply with the spec declaration in
    539    // https://gpuweb.github.io/gpuweb/#lose-the-device
    540    // "No errors are generated after device loss."
    541    if (error->isDeviceLost) {
    542      if (error->deviceId) {
    543        LoseDevice(error->deviceId,
    544                   static_cast<uint8_t>(dom::GPUDeviceLostReason::Unknown),
    545                   error->message);
    546      }
    547    } else {
    548      ReportError(error->deviceId, error->type, error->message);
    549    }
    550    return true;
    551  }
    552  return false;
    553 }
    554 
    555 // Generate an error on the Device timeline of aDeviceId.
    556 // aMessage is interpreted as UTF-8.
    557 void WebGPUParent::ReportError(RawId aDeviceId, const GPUErrorFilter aType,
    558                               const nsCString& aMessage) {
    559  // find the appropriate error scope
    560  if (aDeviceId) {
    561    const auto& itr = mErrorScopeStackByDevice.find(aDeviceId);
    562    if (itr != mErrorScopeStackByDevice.end()) {
    563      auto& stack = itr->second;
    564      for (auto& scope : Reversed(stack)) {
    565        if (scope.filter != aType) {
    566          continue;
    567        }
    568        if (!scope.firstMessage) {
    569          scope.firstMessage = Some(aMessage);
    570        }
    571        return;
    572      }
    573    }
    574  }
    575  // No error scope found, so fall back to the uncaptured error handler
    576  if (!SendUncapturedError(aDeviceId, aMessage)) {
    577    NS_ERROR("SendDeviceUncapturedError failed");
    578  }
    579 }
    580 
    581 struct OnDeviceLostRequest {
    582  WeakPtr<WebGPUParent> mParent;
    583  RawId mDeviceId;
    584 };
    585 
    586 static void DeviceLostCleanupCallback(uint8_t* aUserData) {
    587  auto req = std::unique_ptr<OnDeviceLostRequest>(
    588      reinterpret_cast<OnDeviceLostRequest*>(aUserData));
    589 }
    590 
    591 /* static */ void WebGPUParent::DeviceLostCallback(uint8_t* aUserData,
    592                                                   uint8_t aReason,
    593                                                   const char* aMessage) {
    594  auto req = std::unique_ptr<OnDeviceLostRequest>(
    595      reinterpret_cast<OnDeviceLostRequest*>(aUserData));
    596  if (!req->mParent) {
    597    // Parent is dead, never mind.
    598    return;
    599  }
    600 
    601  RawId deviceId = req->mDeviceId;
    602 
    603  // NOTE: Based on `u8` discriminant values provided for `DeviceLostReason` in
    604  // `wgpu_bindings`.
    605  uint8_t reason;
    606  switch (aReason) {
    607    case 0:
    608      reason = static_cast<uint8_t>(dom::GPUDeviceLostReason::Unknown);
    609      break;
    610    case 1:
    611      reason = static_cast<uint8_t>(dom::GPUDeviceLostReason::Destroyed);
    612      break;
    613    default:
    614      MOZ_CRASH_UNSAFE_PRINTF(
    615          "invalid `aReason` from device lost callback: %hhu", aReason);
    616      break;
    617  }
    618  nsAutoCString message(aMessage);
    619  req->mParent->LoseDevice(deviceId, reason, message);
    620 
    621  auto it = req->mParent->mDeviceFenceHandles.find(deviceId);
    622  if (it != req->mParent->mDeviceFenceHandles.end()) {
    623    req->mParent->mDeviceFenceHandles.erase(it);
    624  }
    625 }
    626 
    627 void WebGPUParent::PostAdapterRequestDevice(RawId aDeviceId) {
    628  mErrorScopeStackByDevice.insert({aDeviceId, {}});
    629 
    630  std::unique_ptr<OnDeviceLostRequest> request(
    631      new OnDeviceLostRequest{this, aDeviceId});
    632  ffi::WGPUDeviceLostClosure closure = {
    633      &DeviceLostCallback, &DeviceLostCleanupCallback,
    634      reinterpret_cast<uint8_t*>(request.release())};
    635  ffi::wgpu_server_set_device_lost_callback(mContext.get(), aDeviceId, closure);
    636 
    637 #if defined(XP_WIN)
    638  HANDLE handle =
    639      wgpu_server_get_device_fence_handle(mContext.get(), aDeviceId);
    640  if (handle) {
    641    RefPtr<gfx::FileHandleWrapper> fenceHandle =
    642        new gfx::FileHandleWrapper(UniqueFileHandle(handle));
    643    mDeviceFenceHandles.emplace(aDeviceId, std::move(fenceHandle));
    644  }
    645 #endif
    646 
    647  MOZ_ASSERT(!mActiveDeviceIds.Contains(aDeviceId));
    648  mActiveDeviceIds.Insert(aDeviceId);
    649 }
    650 
    651 void WebGPUParent::PreDeviceDrop(RawId aDeviceId) {
    652  if (mActiveDeviceIds.Contains(aDeviceId)) {
    653    mActiveDeviceIds.Remove(aDeviceId);
    654  }
    655  mErrorScopeStackByDevice.erase(aDeviceId);
    656  mLostDeviceIds.Remove(aDeviceId);
    657 }
    658 
    659 WebGPUParent::BufferMapData* WebGPUParent::GetBufferMapData(RawId aBufferId) {
    660  const auto iter = mSharedMemoryMap.find(aBufferId);
    661  if (iter == mSharedMemoryMap.end()) {
    662    return nullptr;
    663  }
    664 
    665  return &iter->second;
    666 }
    667 
    668 static const char* MapStatusString(ffi::WGPUBufferMapAsyncStatus status) {
    669  switch (status) {
    670    case ffi::WGPUBufferMapAsyncStatus_Success:
    671      return "Success";
    672    case ffi::WGPUBufferMapAsyncStatus_AlreadyMapped:
    673      return "Already mapped";
    674    case ffi::WGPUBufferMapAsyncStatus_MapAlreadyPending:
    675      return "Map is already pending";
    676    case ffi::WGPUBufferMapAsyncStatus_ContextLost:
    677      return "Context lost";
    678    case ffi::WGPUBufferMapAsyncStatus_Invalid:
    679      return "Invalid buffer";
    680    case ffi::WGPUBufferMapAsyncStatus_InvalidRange:
    681      return "Invalid range";
    682    case ffi::WGPUBufferMapAsyncStatus_InvalidAlignment:
    683      return "Invalid alignment";
    684    case ffi::WGPUBufferMapAsyncStatus_InvalidUsageFlags:
    685      return "Invalid usage flags";
    686    case ffi::WGPUBufferMapAsyncStatus_Error:
    687      return "Map failed";
    688    case ffi::WGPUBufferMapAsyncStatus_Sentinel:  // For -Wswitch
    689      break;
    690  }
    691 
    692  MOZ_CRASH("Bad ffi::WGPUBufferMapAsyncStatus");
    693 }
    694 
    695 void WebGPUParent::MapCallback(uint8_t* aUserData,
    696                               ffi::WGPUBufferMapAsyncStatus aStatus) {
    697  auto req =
    698      std::unique_ptr<MapRequest>(reinterpret_cast<MapRequest*>(aUserData));
    699 
    700  if (!req->mParent) {
    701    return;
    702  }
    703  if (!req->mParent->CanSend()) {
    704    return;
    705  }
    706 
    707  ipc::ByteBuf bb;
    708 
    709  if (aStatus != ffi::WGPUBufferMapAsyncStatus_Success) {
    710    // A buffer map operation that fails with a DeviceError gets
    711    // mapped to the ContextLost status. If we have this status, we
    712    // need to lose the device.
    713    if (aStatus == ffi::WGPUBufferMapAsyncStatus_ContextLost) {
    714      req->mParent->LoseDevice(
    715          req->mDeviceId,
    716          static_cast<uint8_t>(dom::GPUDeviceLostReason::Unknown),
    717          nsPrintfCString("Buffer %" PRIu64 " invalid", req->mBufferId));
    718    }
    719    auto error = nsPrintfCString("Mapping WebGPU buffer failed: %s",
    720                                 MapStatusString(aStatus));
    721 
    722    ffi::wgpu_server_pack_buffer_map_error(req->mBufferId, &error, ToFFI(&bb));
    723  } else {
    724    auto* mapData = req->mParent->GetBufferMapData(req->mBufferId);
    725    MOZ_RELEASE_ASSERT(mapData);
    726 
    727    auto size = req->mSize;
    728    auto offset = req->mOffset;
    729 
    730    if (req->mHostMap == ffi::WGPUHostMap_Read && size > 0) {
    731      ErrorBuffer error;
    732      const auto src = ffi::wgpu_server_buffer_get_mapped_range(
    733          req->mParent->GetContext(), mapData->mDeviceId, req->mBufferId,
    734          offset, size, error.ToFFI());
    735 
    736      MOZ_RELEASE_ASSERT(!error.GetError());
    737 
    738      MOZ_RELEASE_ASSERT(mapData->mShmem->Size() >= offset + size);
    739      if (src.ptr != nullptr && src.length >= size) {
    740        auto dst = mapData->mShmem->DataAsSpan<uint8_t>().Subspan(offset, size);
    741        memcpy(dst.data(), src.ptr, size);
    742      }
    743    }
    744 
    745    bool is_writable = req->mHostMap == ffi::WGPUHostMap_Write;
    746    ffi::wgpu_server_pack_buffer_map_success(req->mBufferId, is_writable,
    747                                             offset, size, ToFFI(&bb));
    748 
    749    mapData->mMappedOffset = offset;
    750    mapData->mMappedSize = size;
    751  }
    752 
    753  if (!req->mParent->SendServerMessage(std::move(bb))) {
    754    NS_ERROR("SendServerMessage failed");
    755  }
    756 }
    757 
    758 void WebGPUParent::BufferUnmap(RawId aDeviceId, RawId aBufferId, bool aFlush) {
    759  MOZ_LOG(sLogger, LogLevel::Info,
    760          ("RecvBufferUnmap %" PRIu64 " flush=%d\n", aBufferId, aFlush));
    761 
    762  auto* mapData = GetBufferMapData(aBufferId);
    763 
    764  if (mapData && aFlush) {
    765    uint64_t offset = mapData->mMappedOffset;
    766    uint64_t size = mapData->mMappedSize;
    767 
    768    ErrorBuffer getRangeError;
    769    const auto mapped = ffi::wgpu_server_buffer_get_mapped_range(
    770        mContext.get(), aDeviceId, aBufferId, offset, size,
    771        getRangeError.ToFFI());
    772    ForwardError(getRangeError);
    773 
    774    if (mapped.ptr != nullptr && mapped.length >= size) {
    775      auto shmSize = mapData->mShmem->Size();
    776      MOZ_RELEASE_ASSERT(offset <= shmSize);
    777      MOZ_RELEASE_ASSERT(size <= shmSize - offset);
    778 
    779      auto src = mapData->mShmem->DataAsSpan<uint8_t>().Subspan(offset, size);
    780      memcpy(mapped.ptr, src.data(), size);
    781    }
    782 
    783    mapData->mMappedOffset = 0;
    784    mapData->mMappedSize = 0;
    785  }
    786 
    787  ErrorBuffer unmapError;
    788  ffi::wgpu_server_buffer_unmap(mContext.get(), aDeviceId, aBufferId,
    789                                unmapError.ToFFI());
    790  ForwardError(unmapError);
    791 
    792  if (mapData && !mapData->mHasMapFlags) {
    793    // We get here if the buffer was mapped at creation without map flags.
    794    // We don't need the shared memory anymore.
    795    DeallocBufferShmem(aBufferId);
    796  }
    797 }
    798 
    799 void WebGPUParent::DeallocBufferShmem(RawId aBufferId) {
    800  const auto iter = mSharedMemoryMap.find(aBufferId);
    801  if (iter != mSharedMemoryMap.end()) {
    802    mSharedMemoryMap.erase(iter);
    803  }
    804 }
    805 
    806 void WebGPUParent::RemoveSharedTexture(RawId aTextureId) {
    807  auto it = mSharedTextures.find(aTextureId);
    808  if (it != mSharedTextures.end()) {
    809    mSharedTextures.erase(it);
    810  }
    811 }
    812 
    813 const ExternalTextureSourceHost& WebGPUParent::GetExternalTextureSource(
    814    ffi::WGPUExternalTextureSourceId aId) const {
    815  return mExternalTextureSources.at(aId);
    816 }
    817 
    818 void WebGPUParent::DestroyExternalTextureSource(RawId aSourceId) {
    819  auto it = mExternalTextureSources.find(aSourceId);
    820  if (it != mExternalTextureSources.end()) {
    821    for (const auto textureId : it->second.TextureIds()) {
    822      ffi::wgpu_server_texture_destroy(mContext.get(), textureId);
    823    }
    824  }
    825 }
    826 
    827 void WebGPUParent::DropExternalTextureSource(RawId aSourceId) {
    828  auto it = mExternalTextureSources.find(aSourceId);
    829  if (it != mExternalTextureSources.end()) {
    830    for (const auto viewId : it->second.ViewIds()) {
    831      ffi::wgpu_server_texture_view_drop(mContext.get(), viewId);
    832    }
    833    for (const auto textureId : it->second.TextureIds()) {
    834      ffi::wgpu_server_texture_drop(mContext.get(), textureId);
    835    }
    836    mExternalTextureSources.erase(it);
    837  }
    838 }
    839 
    840 void WebGPUParent::QueueSubmit(RawId aQueueId, RawId aDeviceId,
    841                               Span<const RawId> aCommandBuffers,
    842                               Span<const RawId> aTextureIds,
    843                               Span<const RawId> aExternalTextureSourceIds) {
    844  for (const auto& textureId : aTextureIds) {
    845    auto it = mSharedTextures.find(textureId);
    846    if (it != mSharedTextures.end()) {
    847      auto& sharedTexture = it->second;
    848      sharedTexture->onBeforeQueueSubmit(aQueueId);
    849    }
    850  }
    851 
    852  for (const auto& sourceId : aExternalTextureSourceIds) {
    853    auto it = mExternalTextureSources.find(sourceId);
    854    if (it != mExternalTextureSources.end()) {
    855      auto& source = it->second;
    856      if (!source.OnBeforeQueueSubmit(this, aDeviceId, aQueueId)) {
    857        // If the above call failed we cannot submit the command buffers, as
    858        // it would be invalid to read from the external textures.
    859        return;
    860      }
    861    }
    862  }
    863 
    864  ErrorBuffer error;
    865  auto index = ffi::wgpu_server_queue_submit(
    866      mContext.get(), aDeviceId, aQueueId,
    867      {aCommandBuffers.Elements(), aCommandBuffers.Length()}, error.ToFFI());
    868  // Check if index is valid. 0 means error.
    869  if (index != 0) {
    870    for (const auto& textureId : aTextureIds) {
    871      auto it = mSharedTextures.find(textureId);
    872      if (it != mSharedTextures.end()) {
    873        auto& sharedTexture = it->second;
    874 
    875        sharedTexture->SetSubmissionIndex(index);
    876        // Update mLastSubmittedTextureId
    877        auto ownerId = sharedTexture->GetOwnerId();
    878        const auto& lookup = mPresentationDataMap.find(ownerId);
    879        if (lookup != mPresentationDataMap.end()) {
    880          RefPtr<PresentationData> data = lookup->second.get();
    881          data->mLastSubmittedTextureId = Some(textureId);
    882        }
    883      }
    884    }
    885  }
    886  ForwardError(error);
    887 }
    888 
    889 void WebGPUParent::OnSubmittedWorkDoneCallback(uint8_t* userdata) {
    890  auto req = std::unique_ptr<OnSubmittedWorkDoneRequest>(
    891      reinterpret_cast<OnSubmittedWorkDoneRequest*>(userdata));
    892  if (!req->mParent) {
    893    return;
    894  }
    895  if (!req->mParent->CanSend()) {
    896    return;
    897  }
    898 
    899  ipc::ByteBuf bb;
    900  ffi::wgpu_server_pack_work_done(ToFFI(&bb), req->mQueueId);
    901  if (!req->mParent->SendServerMessage(std::move(bb))) {
    902    NS_ERROR("SendServerMessage failed");
    903  }
    904 }
    905 
    906 // TODO: proper destruction
    907 
    908 void WebGPUParent::DeviceCreateSwapChain(
    909    RawId aDeviceId, RawId aQueueId, const RGBDescriptor& aDesc,
    910    const nsTArray<RawId>& aBufferIds,
    911    const layers::RemoteTextureOwnerId& aOwnerId,
    912    bool aUseSharedTextureInSwapChain) {
    913  switch (aDesc.format()) {
    914    case gfx::SurfaceFormat::R8G8B8A8:
    915    case gfx::SurfaceFormat::B8G8R8A8:
    916      break;
    917    default:
    918      MOZ_ASSERT_UNREACHABLE("Invalid surface format!");
    919      return;
    920  }
    921 
    922  const auto bufferStrideWithMask =
    923      Device::BufferStrideWithMask(aDesc.size(), aDesc.format());
    924  if (!bufferStrideWithMask.isValid()) {
    925    MOZ_ASSERT_UNREACHABLE("Invalid width / buffer stride!");
    926    return;
    927  }
    928 
    929  constexpr uint32_t kBufferAlignmentMask = 0xff;
    930  const uint32_t bufferStride =
    931      bufferStrideWithMask.value() & ~kBufferAlignmentMask;
    932 
    933  const auto rows = CheckedInt<uint32_t>(aDesc.size().height);
    934  if (!rows.isValid()) {
    935    MOZ_ASSERT_UNREACHABLE("Invalid height!");
    936    return;
    937  }
    938 
    939  if (!mRemoteTextureOwner) {
    940    mRemoteTextureOwner =
    941        MakeRefPtr<layers::RemoteTextureOwnerClient>(OtherPid());
    942  }
    943  mRemoteTextureOwner->RegisterTextureOwner(aOwnerId);
    944 
    945  auto data = MakeRefPtr<PresentationData>(this, aUseSharedTextureInSwapChain,
    946                                           aDeviceId, aQueueId, aDesc,
    947                                           bufferStride, aBufferIds);
    948  if (!mPresentationDataMap.emplace(aOwnerId, data).second) {
    949    NS_ERROR("External image is already registered as WebGPU canvas!");
    950  }
    951 }
    952 
    953 struct ReadbackPresentRequest {
    954  ReadbackPresentRequest(
    955      const ffi::WGPUGlobal* aContext, RefPtr<PresentationData>& aData,
    956      RefPtr<layers::RemoteTextureOwnerClient>& aRemoteTextureOwner,
    957      const layers::RemoteTextureId aTextureId,
    958      const layers::RemoteTextureOwnerId aOwnerId)
    959      : mContext(aContext),
    960        mData(aData),
    961        mRemoteTextureOwner(aRemoteTextureOwner),
    962        mTextureId(aTextureId),
    963        mOwnerId(aOwnerId) {}
    964 
    965  const ffi::WGPUGlobal* mContext;
    966  RefPtr<PresentationData> mData;
    967  RefPtr<layers::RemoteTextureOwnerClient> mRemoteTextureOwner;
    968  const layers::RemoteTextureId mTextureId;
    969  const layers::RemoteTextureOwnerId mOwnerId;
    970 };
    971 
    972 static void ReadbackPresentCallback(uint8_t* userdata,
    973                                    ffi::WGPUBufferMapAsyncStatus status) {
    974  UniquePtr<ReadbackPresentRequest> req(
    975      reinterpret_cast<ReadbackPresentRequest*>(userdata));
    976 
    977  const auto onExit = mozilla::MakeScopeExit([&]() {
    978    auto& waitingTextures = req->mData->mWaitingReadbackTexturesForPresent;
    979    auto it = waitingTextures.find(req->mTextureId);
    980    MOZ_ASSERT(it != waitingTextures.end());
    981    if (it != waitingTextures.end()) {
    982      waitingTextures.erase(it);
    983    }
    984    if (req->mData->mPendingSwapChainDrop.isSome() && waitingTextures.empty()) {
    985      if (req->mData->mParent) {
    986        auto& pendingDrop = req->mData->mPendingSwapChainDrop.ref();
    987        req->mData->mParent->SwapChainDrop(req->mOwnerId, pendingDrop.mTxnType,
    988                                           pendingDrop.mTxnId);
    989        req->mData->mPendingSwapChainDrop = Nothing();
    990      }
    991    }
    992  });
    993 
    994  if (!req->mRemoteTextureOwner->IsRegistered(req->mOwnerId)) {
    995    // SwapChain is already Destroyed
    996    return;
    997  }
    998 
    999  RefPtr<PresentationData> data = req->mData;
   1000  // get the buffer ID
   1001  RawId bufferId;
   1002  {
   1003    bufferId = data->mQueuedBufferIds.back();
   1004    data->mQueuedBufferIds.pop_back();
   1005  }
   1006 
   1007  // Ensure we'll make the bufferId available for reuse
   1008  data->mAvailableBufferIds.push_back(bufferId);
   1009 
   1010  MOZ_LOG(sLogger, LogLevel::Info,
   1011          ("ReadbackPresentCallback for buffer %" PRIu64 " status=%d\n",
   1012           bufferId, status));
   1013  // copy the data
   1014  if (status == ffi::WGPUBufferMapAsyncStatus_Success) {
   1015    const auto bufferSize = data->mDesc.size().height * data->mSourcePitch;
   1016    ErrorBuffer getRangeError;
   1017    const auto mapped = ffi::wgpu_server_buffer_get_mapped_range(
   1018        req->mContext, data->mDeviceId, bufferId, 0, bufferSize,
   1019        getRangeError.ToFFI());
   1020    getRangeError.CoerceValidationToInternal();
   1021    if (req->mData->mParent) {
   1022      req->mData->mParent->ForwardError(getRangeError);
   1023    }
   1024    if (auto innerError = getRangeError.GetError()) {
   1025      MOZ_LOG(sLogger, LogLevel::Info,
   1026              ("WebGPU present: buffer get_mapped_range for internal "
   1027               "presentation readback failed: %s\n",
   1028               innerError->message.get()));
   1029      return;
   1030    }
   1031 
   1032    MOZ_RELEASE_ASSERT(mapped.length >= bufferSize);
   1033    auto textureData =
   1034        req->mRemoteTextureOwner->CreateOrRecycleBufferTextureData(
   1035            data->mDesc.size(), data->mDesc.format(), req->mOwnerId);
   1036    if (!textureData) {
   1037      gfxCriticalNoteOnce << "Failed to allocate BufferTextureData";
   1038      return;
   1039    }
   1040    layers::MappedTextureData mappedData;
   1041    if (textureData && textureData->BorrowMappedData(mappedData)) {
   1042      uint8_t* src = mapped.ptr;
   1043      uint8_t* dst = mappedData.data;
   1044      for (auto row = 0; row < data->mDesc.size().height; ++row) {
   1045        memcpy(dst, src, mappedData.stride);
   1046        dst += mappedData.stride;
   1047        src += data->mSourcePitch;
   1048      }
   1049      req->mRemoteTextureOwner->PushTexture(req->mTextureId, req->mOwnerId,
   1050                                            std::move(textureData));
   1051    } else {
   1052      NS_WARNING("WebGPU present skipped: the swapchain is resized!");
   1053    }
   1054    ErrorBuffer unmapError;
   1055    wgpu_server_buffer_unmap(req->mContext, data->mDeviceId, bufferId,
   1056                             unmapError.ToFFI());
   1057    unmapError.CoerceValidationToInternal();
   1058    if (req->mData->mParent) {
   1059      req->mData->mParent->ForwardError(unmapError);
   1060    }
   1061    if (auto innerError = unmapError.GetError()) {
   1062      MOZ_LOG(sLogger, LogLevel::Info,
   1063              ("WebGPU present: buffer unmap for internal presentation "
   1064               "readback failed: %s\n",
   1065               innerError->message.get()));
   1066    }
   1067  } else {
   1068    // TODO: better handle errors
   1069    NS_WARNING("WebGPU frame mapping failed!");
   1070  }
   1071 }
   1072 
   1073 struct ReadbackSnapshotRequest {
   1074  ReadbackSnapshotRequest(const ffi::WGPUGlobal* aContext,
   1075                          RefPtr<PresentationData>& aData,
   1076                          ffi::WGPUBufferId aBufferId,
   1077                          const ipc::Shmem& aDestShmem)
   1078      : mContext(aContext),
   1079        mData(aData),
   1080        mBufferId(aBufferId),
   1081        mDestShmem(aDestShmem) {}
   1082 
   1083  const ffi::WGPUGlobal* mContext;
   1084  RefPtr<PresentationData> mData;
   1085  const ffi::WGPUBufferId mBufferId;
   1086  const ipc::Shmem& mDestShmem;
   1087 };
   1088 
   1089 static void ReadbackSnapshotCallback(uint8_t* userdata,
   1090                                     ffi::WGPUBufferMapAsyncStatus status) {
   1091  UniquePtr<ReadbackSnapshotRequest> req(
   1092      reinterpret_cast<ReadbackSnapshotRequest*>(userdata));
   1093 
   1094  RefPtr<PresentationData> data = req->mData;
   1095  data->mReadbackSnapshotCallbackCalled = true;
   1096 
   1097  // Ensure we'll make the bufferId available for reuse
   1098  data->mAvailableBufferIds.push_back(req->mBufferId);
   1099 
   1100  MOZ_LOG(sLogger, LogLevel::Info,
   1101          ("ReadbackSnapshotCallback for buffer %" PRIu64 " status=%d\n",
   1102           req->mBufferId, status));
   1103  if (status != ffi::WGPUBufferMapAsyncStatus_Success) {
   1104    return;
   1105  }
   1106  // copy the data
   1107  const auto bufferSize = data->mDesc.size().height * data->mSourcePitch;
   1108  ErrorBuffer getRangeError;
   1109  const auto mapped = ffi::wgpu_server_buffer_get_mapped_range(
   1110      req->mContext, data->mDeviceId, req->mBufferId, 0, bufferSize,
   1111      getRangeError.ToFFI());
   1112  getRangeError.CoerceValidationToInternal();
   1113  if (req->mData->mParent) {
   1114    req->mData->mParent->ForwardError(getRangeError);
   1115  }
   1116  if (auto innerError = getRangeError.GetError()) {
   1117    MOZ_LOG(sLogger, LogLevel::Info,
   1118            ("WebGPU present: buffer get_mapped_range for internal "
   1119             "presentation readback failed: %s\n",
   1120             innerError->message.get()));
   1121    return;
   1122  }
   1123 
   1124  MOZ_RELEASE_ASSERT(mapped.length >= bufferSize);
   1125 
   1126  uint8_t* src = mapped.ptr;
   1127  uint8_t* dst = req->mDestShmem.get<uint8_t>();
   1128  const uint32_t stride = layers::ImageDataSerializer::ComputeRGBStride(
   1129      gfx::SurfaceFormat::B8G8R8A8, data->mDesc.size().width);
   1130 
   1131  for (auto row = 0; row < data->mDesc.size().height; ++row) {
   1132    memcpy(dst, src, stride);
   1133    src += data->mSourcePitch;
   1134    dst += stride;
   1135  }
   1136 
   1137  ErrorBuffer unmapError;
   1138  wgpu_server_buffer_unmap(req->mContext, data->mDeviceId, req->mBufferId,
   1139                           unmapError.ToFFI());
   1140  unmapError.CoerceValidationToInternal();
   1141  if (req->mData->mParent) {
   1142    req->mData->mParent->ForwardError(unmapError);
   1143  }
   1144  if (auto innerError = unmapError.GetError()) {
   1145    MOZ_LOG(sLogger, LogLevel::Info,
   1146            ("WebGPU snapshot: buffer unmap for internal presentation "
   1147             "readback failed: %s\n",
   1148             innerError->message.get()));
   1149  }
   1150 }
   1151 
   1152 ipc::IPCResult WebGPUParent::GetFrontBufferSnapshot(
   1153    IProtocol* aProtocol, const layers::RemoteTextureOwnerId& aOwnerId,
   1154    const RawId& aCommandEncoderId, const RawId& aCommandBufferId,
   1155    Maybe<Shmem>& aShmem, gfx::IntSize& aSize, uint32_t& aByteStride) {
   1156  const auto& lookup = mPresentationDataMap.find(aOwnerId);
   1157  if (lookup == mPresentationDataMap.end()) {
   1158    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1159    return IPC_OK();
   1160  }
   1161 
   1162  RefPtr<PresentationData> data = lookup->second.get();
   1163  data->mReadbackSnapshotCallbackCalled = false;
   1164  aSize = data->mDesc.size();
   1165  uint32_t stride = layers::ImageDataSerializer::ComputeRGBStride(
   1166      data->mDesc.format(), aSize.width);
   1167  aByteStride = stride;
   1168  uint32_t len = data->mDesc.size().height * stride;
   1169  Shmem shmem;
   1170  if (!AllocShmem(len, &shmem)) {
   1171    return IPC_OK();
   1172  }
   1173 
   1174  if (data->mLastSubmittedTextureId.isNothing()) {
   1175    return IPC_OK();
   1176  }
   1177 
   1178  auto it = mSharedTextures.find(data->mLastSubmittedTextureId.ref());
   1179  // Shared texture is already invalid and posted to RemoteTextureMap
   1180  if (it == mSharedTextures.end()) {
   1181    if (!mRemoteTextureOwner || !mRemoteTextureOwner->IsRegistered(aOwnerId)) {
   1182      MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1183      return IPC_OK();
   1184    }
   1185    if (!data->mUseSharedTextureInSwapChain) {
   1186      ffi::wgpu_server_device_poll(mContext.get(), data->mDeviceId, true);
   1187    }
   1188    mRemoteTextureOwner->GetLatestBufferSnapshot(aOwnerId, shmem, aSize);
   1189    aShmem.emplace(std::move(shmem));
   1190    return IPC_OK();
   1191  }
   1192 
   1193  // Readback synchronously
   1194 
   1195  RawId bufferId = 0;
   1196  const auto& size = data->mDesc.size();
   1197  const auto bufferSize = data->mDesc.size().height * data->mSourcePitch;
   1198 
   1199  // step 1: find an available staging buffer, or create one
   1200  {
   1201    if (!data->mAvailableBufferIds.empty()) {
   1202      bufferId = data->mAvailableBufferIds.back();
   1203      data->mAvailableBufferIds.pop_back();
   1204    } else if (!data->mUnassignedBufferIds.empty()) {
   1205      bufferId = data->mUnassignedBufferIds.back();
   1206      data->mUnassignedBufferIds.pop_back();
   1207 
   1208      ffi::WGPUBufferUsages usage =
   1209          WGPUBufferUsages_COPY_DST | WGPUBufferUsages_MAP_READ;
   1210 
   1211      ErrorBuffer error;
   1212      ffi::wgpu_server_device_create_buffer(mContext.get(), data->mDeviceId,
   1213                                            bufferId, nullptr, bufferSize,
   1214                                            usage, false, error.ToFFI());
   1215      if (ForwardError(error)) {
   1216        return IPC_OK();
   1217      }
   1218    } else {
   1219      bufferId = 0;
   1220    }
   1221  }
   1222 
   1223  MOZ_LOG(sLogger, LogLevel::Info,
   1224          ("GetFrontBufferSnapshot with buffer %" PRIu64 "\n", bufferId));
   1225  if (!bufferId) {
   1226    // TODO: add a warning - no buffer are available!
   1227    return IPC_OK();
   1228  }
   1229 
   1230  // step 3: submit a copy command for the frame
   1231  ffi::WGPUCommandEncoderDescriptor encoderDesc = {};
   1232  {
   1233    ErrorBuffer error;
   1234    ffi::wgpu_server_device_create_encoder(mContext.get(), data->mDeviceId,
   1235                                           &encoderDesc, aCommandEncoderId,
   1236                                           error.ToFFI());
   1237    if (ForwardError(error)) {
   1238      return IPC_OK();
   1239    }
   1240  }
   1241 
   1242  if (data->mLastSubmittedTextureId.isNothing()) {
   1243    return IPC_OK();
   1244  }
   1245 
   1246  const ffi::WGPUTexelCopyTextureInfo texView = {
   1247      data->mLastSubmittedTextureId.ref(),
   1248  };
   1249  const ffi::WGPUTexelCopyBufferLayout bufLayout = {
   1250      0,
   1251      &data->mSourcePitch,
   1252      nullptr,
   1253  };
   1254  const ffi::WGPUExtent3d extent = {
   1255      static_cast<uint32_t>(size.width),
   1256      static_cast<uint32_t>(size.height),
   1257      1,
   1258  };
   1259 
   1260  {
   1261    ErrorBuffer error;
   1262    ffi::wgpu_server_encoder_copy_texture_to_buffer(
   1263        mContext.get(), data->mDeviceId, aCommandEncoderId, &texView, bufferId,
   1264        &bufLayout, &extent, error.ToFFI());
   1265    if (ForwardError(error)) {
   1266      return IPC_OK();
   1267    }
   1268  }
   1269  ffi::WGPUCommandBufferDescriptor commandDesc = {};
   1270  {
   1271    ErrorBuffer error;
   1272    ffi::wgpu_server_encoder_finish(mContext.get(), data->mDeviceId,
   1273                                    aCommandEncoderId, aCommandBufferId,
   1274                                    &commandDesc, error.ToFFI());
   1275    if (ForwardError(error)) {
   1276      ffi::wgpu_server_command_encoder_drop(mContext.get(), aCommandEncoderId);
   1277      ffi::wgpu_server_command_buffer_drop(mContext.get(), aCommandBufferId);
   1278      return IPC_OK();
   1279    }
   1280  }
   1281 
   1282  {
   1283    ErrorBuffer error;
   1284    ffi::wgpu_server_queue_submit(mContext.get(), data->mDeviceId,
   1285                                  data->mQueueId, {&aCommandBufferId, 1},
   1286                                  error.ToFFI());
   1287    ffi::wgpu_server_command_encoder_drop(mContext.get(), aCommandEncoderId);
   1288    ffi::wgpu_server_command_buffer_drop(mContext.get(), aCommandBufferId);
   1289    if (ForwardError(error)) {
   1290      return IPC_OK();
   1291    }
   1292  }
   1293 
   1294  auto snapshotRequest = MakeUnique<ReadbackSnapshotRequest>(
   1295      mContext.get(), data, bufferId, shmem);
   1296 
   1297  ffi::WGPUBufferMapClosure closure = {
   1298      &ReadbackSnapshotCallback,
   1299      reinterpret_cast<uint8_t*>(snapshotRequest.release())};
   1300 
   1301  ErrorBuffer error;
   1302  ffi::wgpu_server_buffer_map(mContext.get(), data->mDeviceId, bufferId, 0,
   1303                              bufferSize, ffi::WGPUHostMap_Read, closure,
   1304                              error.ToFFI());
   1305  if (ForwardError(error)) {
   1306    return IPC_OK();
   1307  }
   1308 
   1309  // Callback should be called during the poll.
   1310  ffi::wgpu_server_poll_all_devices(mContext.get(), true);
   1311 
   1312  // Check if ReadbackSnapshotCallback is called.
   1313  MOZ_RELEASE_ASSERT(data->mReadbackSnapshotCallbackCalled == true);
   1314 
   1315  aShmem.emplace(std::move(shmem));
   1316  return IPC_OK();
   1317 }
   1318 
   1319 void WebGPUParent::PostSharedTexture(
   1320    const std::shared_ptr<SharedTexture>&& aSharedTexture,
   1321    const layers::RemoteTextureId aRemoteTextureId,
   1322    const layers::RemoteTextureOwnerId aOwnerId) {
   1323  const auto& lookup = mPresentationDataMap.find(aOwnerId);
   1324  if (lookup == mPresentationDataMap.end() || !mRemoteTextureOwner ||
   1325      !mRemoteTextureOwner->IsRegistered(aOwnerId)) {
   1326    NS_WARNING("WebGPU presenting on a destroyed swap chain!");
   1327    return;
   1328  }
   1329 
   1330  const auto surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
   1331  const auto size = aSharedTexture->GetSize();
   1332 
   1333  RefPtr<PresentationData> data = lookup->second.get();
   1334 
   1335  Maybe<layers::SurfaceDescriptor> desc = aSharedTexture->ToSurfaceDescriptor();
   1336  if (!desc) {
   1337    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1338    return;
   1339  }
   1340 
   1341  mRemoteTextureOwner->PushTexture(aRemoteTextureId, aOwnerId, aSharedTexture,
   1342                                   size, surfaceFormat, *desc);
   1343 
   1344  auto recycledTexture = mRemoteTextureOwner->GetRecycledSharedTexture(
   1345      size, surfaceFormat, desc->type(), aOwnerId);
   1346  if (recycledTexture) {
   1347    recycledTexture->CleanForRecycling();
   1348    data->mRecycledSharedTextures.push_back(recycledTexture);
   1349  }
   1350 }
   1351 
   1352 RefPtr<gfx::FileHandleWrapper> WebGPUParent::GetDeviceFenceHandle(
   1353    const RawId aDeviceId) {
   1354  auto it = mDeviceFenceHandles.find(aDeviceId);
   1355  if (it == mDeviceFenceHandles.end()) {
   1356    return nullptr;
   1357  }
   1358  return it->second;
   1359 }
   1360 
   1361 void WebGPUParent::SwapChainPresent(
   1362    RawId aTextureId, RawId aCommandEncoderId, RawId aCommandBufferId,
   1363    const layers::RemoteTextureId& aRemoteTextureId,
   1364    const layers::RemoteTextureOwnerId& aOwnerId) {
   1365  // step 0: get the data associated with the swapchain
   1366  const auto& lookup = mPresentationDataMap.find(aOwnerId);
   1367  if (lookup == mPresentationDataMap.end() || !mRemoteTextureOwner ||
   1368      !mRemoteTextureOwner->IsRegistered(aOwnerId)) {
   1369    NS_WARNING("WebGPU presenting on a destroyed swap chain!");
   1370    return;
   1371  }
   1372 
   1373  RefPtr<PresentationData> data = lookup->second.get();
   1374 
   1375  if (data->mUseSharedTextureInSwapChain) {
   1376    auto it = mSharedTextures.find(aTextureId);
   1377    if (it == mSharedTextures.end()) {
   1378      MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1379      return;
   1380    }
   1381    std::shared_ptr<SharedTexture> sharedTexture = it->second;
   1382    mSharedTextures.erase(it);
   1383 
   1384    MOZ_ASSERT(sharedTexture->GetOwnerId() == aOwnerId);
   1385 
   1386    PostSharedTexture(std::move(sharedTexture), aRemoteTextureId, aOwnerId);
   1387    return;
   1388  }
   1389 
   1390  RawId bufferId = 0;
   1391  const auto& size = data->mDesc.size();
   1392  const auto bufferSize = data->mDesc.size().height * data->mSourcePitch;
   1393 
   1394  // step 1: find an available staging buffer, or create one
   1395  {
   1396    if (!data->mAvailableBufferIds.empty()) {
   1397      bufferId = data->mAvailableBufferIds.back();
   1398      data->mAvailableBufferIds.pop_back();
   1399    } else if (!data->mUnassignedBufferIds.empty()) {
   1400      bufferId = data->mUnassignedBufferIds.back();
   1401      data->mUnassignedBufferIds.pop_back();
   1402 
   1403      ffi::WGPUBufferUsages usage =
   1404          WGPUBufferUsages_COPY_DST | WGPUBufferUsages_MAP_READ;
   1405 
   1406      ErrorBuffer error;
   1407      ffi::wgpu_server_device_create_buffer(mContext.get(), data->mDeviceId,
   1408                                            bufferId, nullptr, bufferSize,
   1409                                            usage, false, error.ToFFI());
   1410      if (ForwardError(error)) {
   1411        return;
   1412      }
   1413    } else {
   1414      bufferId = 0;
   1415    }
   1416 
   1417    if (bufferId) {
   1418      data->mQueuedBufferIds.insert(data->mQueuedBufferIds.begin(), bufferId);
   1419    }
   1420  }
   1421 
   1422  MOZ_LOG(sLogger, LogLevel::Info,
   1423          ("RecvSwapChainPresent with buffer %" PRIu64 "\n", bufferId));
   1424  if (!bufferId) {
   1425    // TODO: add a warning - no buffer are available!
   1426    return;
   1427  }
   1428 
   1429  // step 3: submit a copy command for the frame
   1430  ffi::WGPUCommandEncoderDescriptor encoderDesc = {};
   1431  {
   1432    ErrorBuffer error;
   1433    ffi::wgpu_server_device_create_encoder(mContext.get(), data->mDeviceId,
   1434                                           &encoderDesc, aCommandEncoderId,
   1435                                           error.ToFFI());
   1436    if (ForwardError(error)) {
   1437      return;
   1438    }
   1439  }
   1440 
   1441  const ffi::WGPUTexelCopyTextureInfo texView = {
   1442      aTextureId,
   1443  };
   1444  const ffi::WGPUTexelCopyBufferLayout bufLayout = {
   1445      0,
   1446      &data->mSourcePitch,
   1447      nullptr,
   1448  };
   1449  const ffi::WGPUExtent3d extent = {
   1450      static_cast<uint32_t>(size.width),
   1451      static_cast<uint32_t>(size.height),
   1452      1,
   1453  };
   1454 
   1455  {
   1456    ErrorBuffer error;
   1457    ffi::wgpu_server_encoder_copy_texture_to_buffer(
   1458        mContext.get(), data->mDeviceId, aCommandEncoderId, &texView, bufferId,
   1459        &bufLayout, &extent, error.ToFFI());
   1460    if (ForwardError(error)) {
   1461      return;
   1462    }
   1463  }
   1464  ffi::WGPUCommandBufferDescriptor commandDesc = {};
   1465  {
   1466    ErrorBuffer error;
   1467    ffi::wgpu_server_encoder_finish(mContext.get(), data->mDeviceId,
   1468                                    aCommandEncoderId, aCommandBufferId,
   1469                                    &commandDesc, error.ToFFI());
   1470    if (ForwardError(error)) {
   1471      ffi::wgpu_server_command_encoder_drop(mContext.get(), aCommandEncoderId);
   1472      ffi::wgpu_server_command_buffer_drop(mContext.get(), aCommandBufferId);
   1473      return;
   1474    }
   1475  }
   1476 
   1477  {
   1478    ErrorBuffer error;
   1479    ffi::wgpu_server_queue_submit(mContext.get(), data->mDeviceId,
   1480                                  data->mQueueId, {&aCommandBufferId, 1},
   1481                                  error.ToFFI());
   1482    ffi::wgpu_server_command_encoder_drop(mContext.get(), aCommandEncoderId);
   1483    ffi::wgpu_server_command_buffer_drop(mContext.get(), aCommandBufferId);
   1484    if (ForwardError(error)) {
   1485      return;
   1486    }
   1487  }
   1488 
   1489  auto& waitingTextures = data->mWaitingReadbackTexturesForPresent;
   1490  auto it = waitingTextures.find(aRemoteTextureId);
   1491  MOZ_ASSERT(it == waitingTextures.end());
   1492  if (it == waitingTextures.end()) {
   1493    waitingTextures.emplace(aRemoteTextureId);
   1494  }
   1495 
   1496  // step 4: request the pixels to be copied into the shared texture
   1497  // TODO: this isn't strictly necessary. When WR wants to Lock() the external
   1498  // texture,
   1499  // we can just give it the contents of the last mapped buffer instead of the
   1500  // copy.
   1501  auto presentRequest = MakeUnique<ReadbackPresentRequest>(
   1502      mContext.get(), data, mRemoteTextureOwner, aRemoteTextureId, aOwnerId);
   1503 
   1504  ffi::WGPUBufferMapClosure closure = {
   1505      &ReadbackPresentCallback,
   1506      reinterpret_cast<uint8_t*>(presentRequest.release())};
   1507 
   1508  ErrorBuffer error;
   1509  ffi::wgpu_server_buffer_map(mContext.get(), data->mDeviceId, bufferId, 0,
   1510                              bufferSize, ffi::WGPUHostMap_Read, closure,
   1511                              error.ToFFI());
   1512  if (ForwardError(error)) {
   1513    return;
   1514  }
   1515 }
   1516 
   1517 void WebGPUParent::SwapChainDrop(const layers::RemoteTextureOwnerId& aOwnerId,
   1518                                 layers::RemoteTextureTxnType aTxnType,
   1519                                 layers::RemoteTextureTxnId aTxnId) {
   1520  const auto& lookup = mPresentationDataMap.find(aOwnerId);
   1521  MOZ_ASSERT(lookup != mPresentationDataMap.end());
   1522  if (lookup == mPresentationDataMap.end()) {
   1523    NS_WARNING("WebGPU presenting on a destroyed swap chain!");
   1524    return;
   1525  }
   1526 
   1527  RefPtr<PresentationData> data = lookup->second.get();
   1528 
   1529  auto waitingCount = data->mWaitingReadbackTexturesForPresent.size();
   1530  if (waitingCount > 0) {
   1531    // Defer SwapChainDrop until readback complete
   1532    data->mPendingSwapChainDrop = Some(PendingSwapChainDrop{aTxnType, aTxnId});
   1533    return;
   1534  }
   1535 
   1536  if (mRemoteTextureOwner) {
   1537    if (aTxnType && aTxnId) {
   1538      mRemoteTextureOwner->WaitForTxn(aOwnerId, aTxnType, aTxnId);
   1539    }
   1540    mRemoteTextureOwner->UnregisterTextureOwner(aOwnerId);
   1541  }
   1542 
   1543  mPresentationDataMap.erase(lookup);
   1544 
   1545  for (const auto bid : data->mAvailableBufferIds) {
   1546    ffi::wgpu_server_buffer_drop(mContext.get(), bid);
   1547    data->mUnassignedBufferIds.push_back(bid);
   1548  }
   1549  for (const auto bid : data->mQueuedBufferIds) {
   1550    ffi::wgpu_server_buffer_drop(mContext.get(), bid);
   1551    data->mUnassignedBufferIds.push_back(bid);
   1552  }
   1553 
   1554  ipc::ByteBuf bb;
   1555  ffi::wgpu_server_pack_free_swap_chain_buffer_ids(
   1556      ToFFI(&bb),
   1557      {data->mUnassignedBufferIds.data(), data->mUnassignedBufferIds.size()});
   1558  if (!SendServerMessage(std::move(bb))) {
   1559    NS_ERROR("SendServerMessage failed");
   1560  }
   1561 }
   1562 
   1563 void WebGPUParent::ActorDestroy(ActorDestroyReason aWhy) {
   1564  mTimer.Stop();
   1565  mPresentationDataMap.clear();
   1566  if (mRemoteTextureOwner) {
   1567    mRemoteTextureOwner->UnregisterAllTextureOwners();
   1568    mRemoteTextureOwner = nullptr;
   1569  }
   1570  mActiveDeviceIds.Clear();
   1571  mContext = nullptr;
   1572 }
   1573 
   1574 ipc::IPCResult WebGPUParent::RecvMessages(
   1575    uint32_t nrOfMessages, ipc::ByteBuf&& aSerializedMessages,
   1576    nsTArray<ipc::ByteBuf>&& aDataBuffers,
   1577    nsTArray<MutableSharedMemoryHandle>&& aShmems) {
   1578  MOZ_ASSERT(mTempMappings.empty());
   1579 
   1580  mTempMappings.reserve(aShmems.Length());
   1581 
   1582  nsTArray<ffi::WGPUFfiSlice_u8> shmem_mappings(aShmems.Length());
   1583 
   1584  for (const auto& shmem : aShmems) {
   1585    auto mapping = shmem.Map();
   1586 
   1587    auto* ptr = mapping.DataAs<uint8_t>();
   1588    auto len = mapping.Size();
   1589    ffi::WGPUFfiSlice_u8 byte_slice{ptr, len};
   1590    shmem_mappings.AppendElement(std::move(byte_slice));
   1591 
   1592    // `aShmem` may be an invalid handle, however this will simply result in an
   1593    // invalid mapping with 0 size, which we use safely.
   1594    mTempMappings.push_back(
   1595        std::make_shared<ipc::SharedMemoryMapping>(std::move(mapping)));
   1596  }
   1597 
   1598  ffi::WGPUFfiSlice_ByteBuf data_buffers{ToFFI(aDataBuffers.Elements()),
   1599                                         aDataBuffers.Length()};
   1600 
   1601  ffi::WGPUFfiSlice_FfiSlice_u8 shmem_mapping_slices{shmem_mappings.Elements(),
   1602                                                     shmem_mappings.Length()};
   1603 
   1604  ffi::wgpu_server_messages(mContext.get(), nrOfMessages,
   1605                            ToFFI(&aSerializedMessages), data_buffers,
   1606                            shmem_mapping_slices);
   1607 
   1608  mTempMappings.clear();
   1609 
   1610  return IPC_OK();
   1611 }
   1612 
   1613 ipc::IPCResult WebGPUParent::RecvCreateExternalTextureSource(
   1614    RawId aDeviceId, RawId aQueueId, RawId aExternalTextureSourceId,
   1615    const ExternalTextureSourceDescriptor& aDesc) {
   1616  MOZ_RELEASE_ASSERT(mExternalTextureSources.find(aExternalTextureSourceId) ==
   1617                     mExternalTextureSources.end());
   1618  mExternalTextureSources.emplace(
   1619      aExternalTextureSourceId,
   1620      ExternalTextureSourceHost::Create(this, aDeviceId, aQueueId, aDesc));
   1621 
   1622  return IPC_OK();
   1623 }
   1624 
   1625 void WebGPUParent::DevicePushErrorScope(RawId aDeviceId,
   1626                                        const dom::GPUErrorFilter aFilter) {
   1627  const auto& itr = mErrorScopeStackByDevice.find(aDeviceId);
   1628  if (itr == mErrorScopeStackByDevice.end()) {
   1629    // Content can cause this simply by destroying a device and then
   1630    // calling `pushErrorScope`.
   1631    return;
   1632  }
   1633  auto& stack = itr->second;
   1634 
   1635  // Let's prevent `while (true) { pushErrorScope(); }`.
   1636  constexpr size_t MAX_ERROR_SCOPE_STACK_SIZE = 1'000'000;
   1637  if (stack.size() >= MAX_ERROR_SCOPE_STACK_SIZE) {
   1638    nsPrintfCString m("pushErrorScope: Hit MAX_ERROR_SCOPE_STACK_SIZE of %zu",
   1639                      MAX_ERROR_SCOPE_STACK_SIZE);
   1640    ReportError(aDeviceId, dom::GPUErrorFilter::Out_of_memory, m);
   1641    return;
   1642  }
   1643 
   1644  const auto newScope = ErrorScope{aFilter};
   1645  stack.push_back(newScope);
   1646 }
   1647 
   1648 PopErrorScopeResult WebGPUParent::DevicePopErrorScope(RawId aDeviceId) {
   1649  const auto popResult = [&]() {
   1650    const auto& itr = mErrorScopeStackByDevice.find(aDeviceId);
   1651    if (itr == mErrorScopeStackByDevice.end()) {
   1652      // Content can cause this simply by destroying a device and then
   1653      // calling `popErrorScope`.
   1654      return PopErrorScopeResult{PopErrorScopeResultType::DeviceLost};
   1655    }
   1656 
   1657    auto& stack = itr->second;
   1658    if (!stack.size()) {
   1659      // Content can cause this simply by calling `popErrorScope` when
   1660      // there is no error scope pushed.
   1661      return PopErrorScopeResult{PopErrorScopeResultType::ThrowOperationError,
   1662                                 "popErrorScope on empty stack"_ns};
   1663    }
   1664 
   1665    const auto& scope = stack.back();
   1666    const auto popLater = MakeScopeExit([&]() { stack.pop_back(); });
   1667 
   1668    auto ret = PopErrorScopeResult{PopErrorScopeResultType::NoError};
   1669    if (scope.firstMessage) {
   1670      ret.message = *scope.firstMessage;
   1671      switch (scope.filter) {
   1672        case dom::GPUErrorFilter::Validation:
   1673          ret.resultType = PopErrorScopeResultType::ValidationError;
   1674          break;
   1675        case dom::GPUErrorFilter::Out_of_memory:
   1676          ret.resultType = PopErrorScopeResultType::OutOfMemory;
   1677          break;
   1678        case dom::GPUErrorFilter::Internal:
   1679          ret.resultType = PopErrorScopeResultType::InternalError;
   1680          break;
   1681      }
   1682    }
   1683    return ret;
   1684  }();
   1685  return popResult;
   1686 }
   1687 
   1688 bool WebGPUParent::UseSharedTextureForSwapChain(
   1689    ffi::WGPUSwapChainId aSwapChainId) {
   1690  auto ownerId = layers::RemoteTextureOwnerId{aSwapChainId._0};
   1691  const auto& lookup = mPresentationDataMap.find(ownerId);
   1692  if (lookup == mPresentationDataMap.end()) {
   1693    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1694    return false;
   1695  }
   1696 
   1697  RefPtr<PresentationData> data = lookup->second.get();
   1698 
   1699  return data->mUseSharedTextureInSwapChain;
   1700 }
   1701 
   1702 void WebGPUParent::DisableSharedTextureForSwapChain(
   1703    ffi::WGPUSwapChainId aSwapChainId) {
   1704  auto ownerId = layers::RemoteTextureOwnerId{aSwapChainId._0};
   1705  const auto& lookup = mPresentationDataMap.find(ownerId);
   1706  if (lookup == mPresentationDataMap.end()) {
   1707    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1708    return;
   1709  }
   1710 
   1711  RefPtr<PresentationData> data = lookup->second.get();
   1712 
   1713  if (data->mUseSharedTextureInSwapChain) {
   1714    gfxCriticalNote << "Disable SharedTexture for SwapChain:  "
   1715                    << aSwapChainId._0;
   1716  }
   1717 
   1718  data->mUseSharedTextureInSwapChain = false;
   1719 }
   1720 
   1721 bool WebGPUParent::EnsureSharedTextureForSwapChain(
   1722    ffi::WGPUSwapChainId aSwapChainId, ffi::WGPUDeviceId aDeviceId,
   1723    ffi::WGPUTextureId aTextureId, uint32_t aWidth, uint32_t aHeight,
   1724    struct ffi::WGPUTextureFormat aFormat, ffi::WGPUTextureUsages aUsage) {
   1725  auto ownerId = layers::RemoteTextureOwnerId{aSwapChainId._0};
   1726  const auto& lookup = mPresentationDataMap.find(ownerId);
   1727  if (lookup == mPresentationDataMap.end()) {
   1728    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1729    return false;
   1730  }
   1731 
   1732  RefPtr<PresentationData> data = lookup->second.get();
   1733  if (!data->mUseSharedTextureInSwapChain) {
   1734    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1735    return false;
   1736  }
   1737 
   1738  // Recycled SharedTexture if it exists.
   1739  if (!data->mRecycledSharedTextures.empty()) {
   1740    std::shared_ptr<SharedTexture> texture =
   1741        data->mRecycledSharedTextures.front();
   1742    // Check if the texture is recyclable.
   1743    if (texture->mWidth == aWidth && texture->mHeight == aHeight &&
   1744        texture->mFormat.tag == aFormat.tag && texture->mUsage == aUsage) {
   1745      texture->SetOwnerId(ownerId);
   1746      data->mRecycledSharedTextures.pop_front();
   1747      mSharedTextures.emplace(aTextureId, texture);
   1748      return true;
   1749    }
   1750    data->mRecycledSharedTextures.clear();
   1751  }
   1752 
   1753  auto sharedTexture = CreateSharedTexture(ownerId, aDeviceId, aTextureId,
   1754                                           aWidth, aHeight, aFormat, aUsage);
   1755  return static_cast<bool>(sharedTexture);
   1756 }
   1757 
   1758 void WebGPUParent::EnsureSharedTextureForReadBackPresent(
   1759    ffi::WGPUSwapChainId aSwapChainId, ffi::WGPUDeviceId aDeviceId,
   1760    ffi::WGPUTextureId aTextureId, uint32_t aWidth, uint32_t aHeight,
   1761    struct ffi::WGPUTextureFormat aFormat, ffi::WGPUTextureUsages aUsage) {
   1762  auto ownerId = layers::RemoteTextureOwnerId{aSwapChainId._0};
   1763  const auto& lookup = mPresentationDataMap.find(ownerId);
   1764  if (lookup == mPresentationDataMap.end()) {
   1765    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1766    return;
   1767  }
   1768 
   1769  RefPtr<PresentationData> data = lookup->second.get();
   1770  if (data->mUseSharedTextureInSwapChain) {
   1771    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1772    return;
   1773  }
   1774 
   1775  UniquePtr<SharedTexture> texture =
   1776      SharedTextureReadBackPresent::Create(aWidth, aHeight, aFormat, aUsage);
   1777  if (!texture) {
   1778    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1779    return;
   1780  }
   1781 
   1782  texture->SetOwnerId(ownerId);
   1783  std::shared_ptr<SharedTexture> shared(texture.release());
   1784  mSharedTextures[aTextureId] = shared;
   1785 }
   1786 
   1787 std::shared_ptr<SharedTexture> WebGPUParent::CreateSharedTexture(
   1788    const layers::RemoteTextureOwnerId& aOwnerId, ffi::WGPUDeviceId aDeviceId,
   1789    ffi::WGPUTextureId aTextureId, uint32_t aWidth, uint32_t aHeight,
   1790    const struct ffi::WGPUTextureFormat aFormat,
   1791    ffi::WGPUTextureUsages aUsage) {
   1792  MOZ_RELEASE_ASSERT(mSharedTextures.find(aTextureId) == mSharedTextures.end());
   1793 
   1794  UniquePtr<SharedTexture> texture =
   1795      SharedTexture::Create(this, aDeviceId, aWidth, aHeight, aFormat, aUsage);
   1796  if (!texture) {
   1797    return nullptr;
   1798  }
   1799 
   1800  texture->SetOwnerId(aOwnerId);
   1801  std::shared_ptr<SharedTexture> shared(texture.release());
   1802  mSharedTextures.emplace(aTextureId, shared);
   1803 
   1804  return shared;
   1805 }
   1806 
   1807 std::shared_ptr<SharedTexture> WebGPUParent::GetSharedTexture(
   1808    ffi::WGPUTextureId aId) {
   1809  auto it = mSharedTextures.find(aId);
   1810  if (it == mSharedTextures.end()) {
   1811    return nullptr;
   1812  }
   1813  return it->second;
   1814 }
   1815 
   1816 #if defined(XP_WIN)
   1817 /* static */
   1818 Maybe<ffi::WGPUFfiLUID> WebGPUParent::GetCompositorDeviceLuid() {
   1819  const RefPtr<ID3D11Device> d3d11Device =
   1820      gfx::DeviceManagerDx::Get()->GetCompositorDevice();
   1821  if (!d3d11Device) {
   1822    gfxCriticalNoteOnce << "CompositorDevice does not exist";
   1823    return Nothing();
   1824  }
   1825 
   1826  RefPtr<IDXGIDevice> dxgiDevice;
   1827  d3d11Device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
   1828 
   1829  RefPtr<IDXGIAdapter> dxgiAdapter;
   1830  dxgiDevice->GetAdapter(getter_AddRefs(dxgiAdapter));
   1831 
   1832  DXGI_ADAPTER_DESC desc;
   1833  if (FAILED(dxgiAdapter->GetDesc(&desc))) {
   1834    gfxCriticalNoteOnce << "Failed to get DXGI_ADAPTER_DESC";
   1835    return Nothing();
   1836  }
   1837 
   1838  return Some(
   1839      ffi::WGPUFfiLUID{desc.AdapterLuid.LowPart, desc.AdapterLuid.HighPart});
   1840 }
   1841 #endif
   1842 
   1843 #if defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)
   1844 VkImageHandle::~VkImageHandle() {
   1845  if (!mParent) {
   1846    return;
   1847  }
   1848  auto* context = mParent->GetContext();
   1849  if (context && mParent->IsDeviceActive(mDeviceId) && mVkImageHandle) {
   1850    wgpu_vkimage_destroy(context, mDeviceId, mVkImageHandle);
   1851  }
   1852  wgpu_vkimage_delete(mVkImageHandle);
   1853 }
   1854 
   1855 VkSemaphoreHandle::~VkSemaphoreHandle() {
   1856  if (!mParent) {
   1857    return;
   1858  }
   1859  auto* context = mParent->GetContext();
   1860  if (context && mParent->IsDeviceActive(mDeviceId) && mVkSemaphoreHandle) {
   1861    wgpu_vksemaphore_destroy(context, mDeviceId, mVkSemaphoreHandle);
   1862  }
   1863  wgpu_vksemaphore_delete(mVkSemaphoreHandle);
   1864 }
   1865 #endif
   1866 
   1867 }  // namespace mozilla::webgpu