[dawn][emscripten] Implements mapAsync future entry points.

Bug: 369445681
Change-Id: I34f930635da0067d2caa9022e0c4d78ae3979e3b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/207615
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Loko Kung <lokokung@google.com>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/third_party/emdawnwebgpu/library_webgpu.js b/third_party/emdawnwebgpu/library_webgpu.js
index 8cbd00d..5efc983 100644
--- a/third_party/emdawnwebgpu/library_webgpu.js
+++ b/third_party/emdawnwebgpu/library_webgpu.js
@@ -593,7 +593,7 @@
   },
 
   emwgpuAdapterRequestDevice__i53abi: false,
-  emwgpuAdapterRequestDevice__deps: ['$stringToUTF8OnStack', 'emwgpuCreateQueue', 'emwgpuOnDeviceLostCompleted', 'emwgpuOnRequestDeviceCompleted', 'emwgpuOnUncapturedError'],
+  emwgpuAdapterRequestDevice__deps: ['emwgpuCreateQueue', 'emwgpuOnDeviceLostCompleted', 'emwgpuOnRequestDeviceCompleted', 'emwgpuOnUncapturedError'],
   emwgpuAdapterRequestDevice: (
     adapterPtr,
     futureIdL, futureIdH,
@@ -752,7 +752,7 @@
   // Methods of Buffer
   // --------------------------------------------------------------------------
 
-  wgpuBufferDestroy: (bufferPtr) => {
+  emwgpuBufferDestroy: (bufferPtr) => {
     var bufferWrapper = WebGPU._tableGet(bufferPtr);
     {{{ gpu.makeCheckDefined('bufferWrapper') }}}
     if (bufferWrapper.onUnmap) {
@@ -767,8 +767,8 @@
 
   // In webgpu.h offset and size are passed in as size_t.
   // And library_webgpu assumes that size_t is always 32bit in emscripten.
-  wgpuBufferGetConstMappedRange__deps: ['$warnOnce', 'memalign', 'free'],
-  wgpuBufferGetConstMappedRange: (bufferPtr, offset, size) => {
+  emwgpuBufferGetConstMappedRange__deps: ['$warnOnce', 'memalign', 'free'],
+  emwgpuBufferGetConstMappedRange: (bufferPtr, offset, size) => {
     var bufferWrapper = WebGPU._tableGet(bufferPtr);
     {{{ gpu.makeCheckDefined('bufferWrapper') }}}
 
@@ -792,15 +792,10 @@
     return data;
   },
 
-  wgpuBufferGetMapState: (bufferPtr) => {
-    var buffer = WebGPU._tableGet(bufferPtr).object;
-    return WebGPU.Int_BufferMapState[buffer.mapState];
-  },
-
   // In webgpu.h offset and size are passed in as size_t.
   // And library_webgpu assumes that size_t is always 32bit in emscripten.
-  wgpuBufferGetMappedRange__deps: ['$warnOnce', 'memalign', 'free'],
-  wgpuBufferGetMappedRange: (bufferPtr, offset, size) => {
+  emwgpuBufferGetMappedRange__deps: ['$warnOnce', 'memalign', 'free'],
+  emwgpuBufferGetMappedRange: (bufferPtr, offset, size) => {
     var bufferWrapper = WebGPU._tableGet(bufferPtr);
     {{{ gpu.makeCheckDefined('bufferWrapper') }}}
 
@@ -808,14 +803,6 @@
 
     {{{ gpu.convertSentinelToUndefined('size') }}}
 
-    if (bufferWrapper.mapMode !== {{{ gpu.MapMode.Write }}}) {
-#if ASSERTIONS
-      abort("GetMappedRange called, but buffer not mapped for writing");
-#endif
-      // TODO(kainino0x): Somehow inject a validation error?
-      return 0;
-    }
-
     var mapped;
     try {
       mapped = bufferWrapper.object.getMappedRange(offset, size);
@@ -849,31 +836,30 @@
 
   // In webgpu.h offset and size are passed in as size_t.
   // And library_webgpu assumes that size_t is always 32bit in emscripten.
-  wgpuBufferMapAsync__deps: ['$callUserCallback'],
-  wgpuBufferMapAsync: (bufferPtr, mode, offset, size, callback, userdata) => {
+  emwgpuBufferMapAsync__i53abi: false,
+  emwgpuBufferMapAsync__deps: ['emwgpuOnMapAsyncCompleted'],
+  emwgpuBufferMapAsync: (bufferPtr, futureIdL, futureIdH, mode, offset, size) => {
     var bufferWrapper = WebGPU._tableGet(bufferPtr);
     {{{ gpu.makeCheckDefined('bufferWrapper') }}}
-    bufferWrapper.mapMode = mode;
     bufferWrapper.onUnmap = [];
     var buffer = bufferWrapper.object;
 
     {{{ gpu.convertSentinelToUndefined('size') }}}
 
-    // `callback` takes (WGPUBufferMapAsyncStatus status, void * userdata)
-
     {{{ runtimeKeepalivePush() }}}
-    buffer.mapAsync(mode, offset, size).then(() => {
+    WebGPU._futureInsert(futureIdL, futureIdH, buffer.mapAsync(mode, offset, size).then(() => {
       {{{ runtimeKeepalivePop() }}}
-      callUserCallback(() => {
-        {{{ makeDynCall('vip', 'callback') }}}({{{ gpu.BufferMapAsyncStatus.Success }}}, userdata);
-      });
-    }, () => {
+      _emwgpuOnMapAsyncCompleted(futureIdL, futureIdH, {{{ gpu.MapAsyncStatus.Success }}}, 0);
+    }, (ex) => {
       {{{ runtimeKeepalivePop() }}}
-      callUserCallback(() => {
-        // TODO(kainino0x): Figure out how to pick other error status values.
-        {{{ makeDynCall('vip', 'callback') }}}({{{ gpu.BufferMapAsyncStatus.ValidationError }}}, userdata);
-      });
-    });
+      var sp = stackSave();
+      var messagePtr = stringToUTF8OnStack(ex.message);
+      var status =
+        ex instanceof AbortError ? {{{ gpu.MapAsyncStatus.Aborted }}} :
+        ex instanceof OperationError ? {{{ gpu.MapAsyncStatus.Error }}} :
+        {{{ gpu.MapAsyncStatus.Unknown }}};
+        _emwgpuOnMapAsyncCompleted(futureIdL, futureIdH, status, messagePtr);
+    }));
   },
 
   wgpuBufferSetLabel: (bufferPtr, labelPtr) => {
@@ -881,7 +867,7 @@
     buffer.label = UTF8ToString(labelPtr);
   },
 
-  wgpuBufferUnmap: (bufferPtr) => {
+  emwgpuBufferUnmap: (bufferPtr) => {
     var bufferWrapper = WebGPU._tableGet(bufferPtr);
     {{{ gpu.makeCheckDefined('bufferWrapper') }}}
 
@@ -1400,8 +1386,7 @@
     return ptr;
   },
 
-  wgpuDeviceCreateBuffer__deps: ['emwgpuCreateBuffer'],
-  wgpuDeviceCreateBuffer: (devicePtr, descriptor) => {
+  emwgpuDeviceCreateBuffer: (devicePtr, descriptor, bufferPtr) => {
     {{{ gpu.makeCheckDescriptor('descriptor') }}}
 
     var mappedAtCreation = {{{ gpu.makeGetBool('descriptor', C_STRUCTS.WGPUBufferDescriptor.mappedAtCreation) }}};
@@ -1419,13 +1404,10 @@
     var bufferWrapper = {
       object: device.createBuffer(desc),
     };
-    var ptr = _emwgpuCreateBuffer();
-    WebGPU._tableInsert(ptr, bufferWrapper);
+    WebGPU._tableInsert(bufferPtr, bufferWrapper);
     if (mappedAtCreation) {
-      bufferWrapper.mapMode = {{{ gpu.MapMode.Write }}};
       bufferWrapper.onUnmap = [];
     }
-    return ptr;
   },
 
   wgpuDeviceCreateCommandEncoder__deps: ['emwgpuCreateCommandEncoder'],
@@ -1455,7 +1437,7 @@
   },
 
   emwgpuDeviceCreateComputePipelineAsync__i53abi:false,
-  emwgpuDeviceCreateComputePipelineAsync__deps: ['$stringToUTF8OnStack', 'emwgpuCreateComputePipeline', 'emwgpuOnDeviceCreateComputePipelineCompleted'],
+  emwgpuDeviceCreateComputePipelineAsync__deps: ['emwgpuCreateComputePipeline', 'emwgpuOnCreateComputePipelineCompleted'],
   emwgpuDeviceCreateComputePipelineAsync: (devicePtr, futureIdL, futureIdH, descriptor) => {
     var desc = WebGPU.makeComputePipelineDesc(descriptor);
     var device = WebGPU._tableGet(devicePtr);
@@ -1464,7 +1446,7 @@
       {{{ runtimeKeepalivePop() }}}
       var pipelinePtr = _emwgpuCreateComputePipeline();
       WebGPU._tableInsert(pipelinePtr, pipeline);
-      _emwgpuOnDeviceCreateComputePipelineCompleted(futureIdL, futureIdH, {{{ gpu.CreatePipelineAsyncStatus.Success }}}, pipelinePtr, 0);
+      _emwgpuOnCreateComputePipelineCompleted(futureIdL, futureIdH, {{{ gpu.CreatePipelineAsyncStatus.Success }}}, pipelinePtr, 0);
     }, (pipelineError) => {
       {{{ runtimeKeepalivePop() }}}
       var sp = stackSave();
@@ -1473,7 +1455,7 @@
         pipeline.reason === 'validation' ? {{{ gpu.CreatePipelineAsyncStatus.ValidationError }}} :
         pipeline.reason === 'internal' ? {{{ gpu.CreatePipelineAsyncStatus.InternalError }}} :
         {{{ gpu.CreatePipelineAsyncStatus.Unknown }}};
-      _emwgpuOnDeviceCreateComputePipelineCompleted(futureIdL, futureIdH, status, 0, messagePtr);
+      _emwgpuOnCreateComputePipelineCompleted(futureIdL, futureIdH, status, 0, messagePtr);
       stackRestore(sp);
     }));
   },
@@ -1565,7 +1547,7 @@
   },
 
   emwgpuDeviceCreateRenderPipelineAsync__i53abi:false,
-  emwgpuDeviceCreateRenderPipelineAsync__deps: ['$stringToUTF8OnStack', 'emwgpuCreateRenderPipeline', 'emwgpuOnDeviceCreateRenderPipelineCompleted'],
+  emwgpuDeviceCreateRenderPipelineAsync__deps: ['emwgpuCreateRenderPipeline', 'emwgpuOnCreateRenderPipelineCompleted'],
   emwgpuDeviceCreateRenderPipelineAsync: (devicePtr, futureIdL, futureIdH, descriptor) => {
     var desc = WebGPU.makeRenderPipelineDesc(descriptor);
     var device = WebGPU._tableGet(devicePtr);
@@ -1574,7 +1556,7 @@
       {{{ runtimeKeepalivePop() }}}
       var pipelinePtr = _emwgpuCreateRenderPipeline();
       WebGPU._tableInsert(pipelinePtr, pipeline);
-      _emwgpuOnDeviceCreateRenderPipelineCompleted(futureIdL, futureIdH, {{{ gpu.CreatePipelineAsyncStatus.Success }}}, pipelinePtr, 0);
+      _emwgpuOnCreateRenderPipelineCompleted(futureIdL, futureIdH, {{{ gpu.CreatePipelineAsyncStatus.Success }}}, pipelinePtr, 0);
     }, (pipelineError) => {
       {{{ runtimeKeepalivePop() }}}
       var sp = stackSave();
@@ -1583,7 +1565,7 @@
         pipeline.reason === 'validation' ? {{{ gpu.CreatePipelineAsyncStatus.ValidationError }}} :
         pipeline.reason === 'internal' ? {{{ gpu.CreatePipelineAsyncStatus.InternalError }}} :
         {{{ gpu.CreatePipelineAsyncStatus.Unknown }}};
-        _emwgpuOnDeviceCreateRenderPipelineCompleted(futureIdL, futureIdH, status, 0, messagePtr);
+        _emwgpuOnCreateRenderPipelineCompleted(futureIdL, futureIdH, status, 0, messagePtr);
       stackRestore(sp);
     }));
   },
@@ -1840,7 +1822,7 @@
   },
 
   emwgpuInstanceRequestAdapter__i53abi: false,
-  emwgpuInstanceRequestAdapter__deps: ['$callUserCallback', '$stringToUTF8OnStack', 'emwgpuCreateAdapter', 'emwgpuOnRequestAdapterCompleted'],
+  emwgpuInstanceRequestAdapter__deps: ['emwgpuCreateAdapter', 'emwgpuOnRequestAdapterCompleted'],
   emwgpuInstanceRequestAdapter: (instancePtr, futureIdL, futureIdH, options) => {
     var opts;
     if (options) {
diff --git a/third_party/emdawnwebgpu/webgpu.cpp b/third_party/emdawnwebgpu/webgpu.cpp
index efc9a98..55f714c 100644
--- a/third_party/emdawnwebgpu/webgpu.cpp
+++ b/third_party/emdawnwebgpu/webgpu.cpp
@@ -41,6 +41,19 @@
                        uint64_t const* timeoutNSPtr);
 WGPUTextureFormat emwgpuGetPreferredFormat();
 
+// Creation functions to create JS backing objects given a pre-allocated handle.
+void emwgpuDeviceCreateBuffer(WGPUDevice device,
+                              const WGPUBufferDescriptor* descriptor,
+                              WGPUBuffer buffer);
+
+// Buffer mapping operations that has work that needs to be done on the JS side.
+void emwgpuBufferDestroy(WGPUBuffer buffer);
+const void* emwgpuBufferGetConstMappedRange(WGPUBuffer buffer,
+                                            size_t offset,
+                                            size_t size);
+void* emwgpuBufferGetMappedRange(WGPUBuffer buffer, size_t offset, size_t size);
+void emwgpuBufferUnmap(WGPUBuffer buffer);
+
 // Future/async operation that need to be forwarded to JS.
 void emwgpuAdapterRequestDevice(WGPUAdapter adapter,
                                 FutureID futureId,
@@ -48,6 +61,11 @@
                                 WGPUDevice device,
                                 WGPUQueue queue,
                                 const WGPUDeviceDescriptor* descriptor);
+void emwgpuBufferMapAsync(WGPUBuffer buffer,
+                          FutureID futureID,
+                          WGPUMapMode mode,
+                          size_t offset,
+                          size_t size);
 void emwgpuDeviceCreateComputePipelineAsync(
     WGPUDevice device,
     FutureID futureId,
@@ -260,7 +278,6 @@
 #define WGPU_PASSTHROUGH_OBJECTS(X) \
   X(BindGroup)           \
   X(BindGroupLayout)     \
-  X(Buffer)              \
   X(CommandBuffer)       \
   X(CommandEncoder)      \
   X(ComputePassEncoder)  \
@@ -291,6 +308,7 @@
   CreateComputePipeline,
   CreateRenderPipeline,
   DeviceLost,
+  MapAsync,
   RequestAdapter,
   RequestDevice,
 };
@@ -583,6 +601,41 @@
   WGPUAdapterImpl(const EventSource* source);
 };
 
+struct WGPUBufferImpl final : public RefCountedWithExternalCount,
+                              public EventSource {
+ public:
+  WGPUBufferImpl(const EventSource* source, bool mappedAtCreation);
+
+  void Destroy();
+  const void* GetConstMappedRange(size_t offset, size_t size);
+  WGPUBufferMapState GetMapState() const;
+  void* GetMappedRange(size_t offset, size_t size);
+  WGPUFuture MapAsync(WGPUMapMode mode,
+                      size_t offset,
+                      size_t size,
+                      WGPUBufferMapCallbackInfo2 callbackInfo);
+  void Unmap();
+
+ private:
+  friend class MapAsyncEvent;
+
+  void WillDropLastExternalRef() override;
+
+  bool IsPendingMapRequest(FutureID futureID) const;
+  void AbortPendingMap(const char* message);
+
+  // Encapsulates information about a map request. Note that when
+  // futureID == kNullFutureId, there are no pending map requests, however, it
+  // is still possible that we are still "mapped" because of mappedAtCreation
+  // which is not associated with a particular async map / future.
+  struct MapRequest {
+    FutureID futureID = kNullFutureId;
+    WGPUMapMode mode = WGPUMapMode_None;
+  };
+  MapRequest mPendingMapRequest;
+  WGPUBufferMapState mMapState;
+};
+
 // Device is specially implemented in order to handle refcounting the Queue.
 struct WGPUDeviceImpl final : public RefCountedWithExternalCount,
                               public EventSource {
@@ -732,6 +785,65 @@
   std::optional<std::string> mMessage;
 };
 
+class MapAsyncEvent final : public TrackedEvent {
+ public:
+  static constexpr EventType kType = EventType::MapAsync;
+
+  MapAsyncEvent(InstanceID instance,
+                WGPUBuffer buffer,
+                const WGPUBufferMapCallbackInfo2& callbackInfo)
+      : TrackedEvent(instance, callbackInfo.mode), mBuffer(buffer) {}
+
+  EventType GetType() override { return kType; }
+
+  void ReadyHook(WGPUMapAsyncStatus status, const char* message) {
+    // For mapping, this hook may be called more than once if we are not in
+    // Spontaneous mode. The precedence of which status should follow
+    // Success < Error < Aborted. Luckily, the enum is defined such that the
+    // precedence holds true already, so we can exploit that here.
+    static_assert(WGPUMapAsyncStatus_Success < WGPUMapAsyncStatus_Error);
+    static_assert(WGPUMapAsyncStatus_Error < WGPUMapAsyncStatus_Aborted);
+    if (status > mStatus) {
+      mStatus = status;
+      if (message) {
+        mMessage = message;
+      }
+    }
+  }
+
+  void Complete(FutureID futureID, EventCompletionType type) override {
+    if (type == EventCompletionType::Shutdown) {
+      mStatus = WGPUMapAsyncStatus_InstanceDropped;
+      mMessage = "A valid external Instance reference no longer exists.";
+    }
+
+    if (mBuffer->IsPendingMapRequest(futureID)) {
+      if (mStatus == WGPUMapAsyncStatus_Success) {
+        mBuffer->mMapState = WGPUBufferMapState_Mapped;
+      } else {
+        mBuffer->mMapState = WGPUBufferMapState_Unmapped;
+        mBuffer->mPendingMapRequest = {};
+      }
+    } else {
+      assert(mStatus != WGPUMapAsyncStatus_Success);
+    }
+
+    if (mCallback) {
+      mCallback(mStatus, mMessage ? mMessage->c_str() : nullptr, mUserdata1,
+                mUserdata2);
+    }
+  }
+
+ private:
+  WGPUBufferMapCallback2 mCallback = nullptr;
+  void* mUserdata1 = nullptr;
+  void* mUserdata2 = nullptr;
+
+  Ref<WGPUBuffer> mBuffer;
+  WGPUMapAsyncStatus mStatus = WGPUMapAsyncStatus_Success;
+  std::optional<std::string> mMessage = std::nullopt;
+};
+
 class RequestAdapterEvent final : public TrackedEvent {
  public:
   static constexpr EventType kType = EventType::RequestAdapter;
@@ -834,8 +946,106 @@
 // WGPUAdapterImpl implementations.
 // ----------------------------------------------------------------------------
 
-WGPUAdapterImpl::WGPUAdapterImpl(const EventSource* source)
-    : EventSource(source->GetInstanceId()) {}
+WGPUAdapterImpl::WGPUAdapterImpl(const EventSource* instance)
+    : EventSource(instance->GetInstanceId()) {}
+
+// ----------------------------------------------------------------------------
+// WGPUBuffer implementations.
+// ----------------------------------------------------------------------------
+
+WGPUBufferImpl::WGPUBufferImpl(const EventSource* source, bool mappedAtCreation)
+    : EventSource(source->GetInstanceId()),
+      mMapState(mappedAtCreation ? WGPUBufferMapState_Mapped
+                                 : WGPUBufferMapState_Unmapped) {
+  if (mappedAtCreation) {
+    mPendingMapRequest = {kNullFutureId, WGPUMapMode_Write};
+  }
+}
+
+void WGPUBufferImpl::Destroy() {
+  emwgpuBufferDestroy(this);
+  AbortPendingMap("Buffer was destroyed before mapping was resolved.");
+}
+
+const void* WGPUBufferImpl::GetConstMappedRange(size_t offset, size_t size) {
+  if (mMapState != WGPUBufferMapState_Mapped) {
+    return nullptr;
+  }
+  return emwgpuBufferGetConstMappedRange(this, offset, size);
+}
+
+WGPUBufferMapState WGPUBufferImpl::GetMapState() const {
+  return mMapState;
+}
+
+void* WGPUBufferImpl::GetMappedRange(size_t offset, size_t size) {
+  if (mMapState != WGPUBufferMapState_Mapped) {
+    return nullptr;
+  }
+  if (mPendingMapRequest.mode != WGPUMapMode_Write) {
+    assert(false);
+    return nullptr;
+  }
+
+  return emwgpuBufferGetMappedRange(this, offset, size);
+}
+
+WGPUFuture WGPUBufferImpl::MapAsync(WGPUMapMode mode,
+                                    size_t offset,
+                                    size_t size,
+                                    WGPUBufferMapCallbackInfo2 callbackInfo) {
+  auto [futureId, tracked] = GetEventManager().TrackEvent(
+      std::make_unique<MapAsyncEvent>(GetInstanceId(), this, callbackInfo));
+  if (!tracked) {
+    return WGPUFuture{kNullFutureId};
+  }
+
+  if (mMapState == WGPUBufferMapState_Pending) {
+    GetEventManager().SetFutureReady<MapAsyncEvent>(
+        futureId, WGPUMapAsyncStatus_Error,
+        "Buffer already has an outstanding map pending.");
+    return WGPUFuture{futureId};
+  }
+
+  assert(mPendingMapRequest.mode == WGPUMapMode_None);
+  mMapState = WGPUBufferMapState_Pending;
+  mPendingMapRequest = {futureId, mode};
+
+  emwgpuBufferMapAsync(this, futureId, mode, offset, size);
+  return WGPUFuture{futureId};
+}
+
+void WGPUBufferImpl::Unmap() {
+  emwgpuBufferUnmap(this);
+  AbortPendingMap("Buffer was unmapped before mapping was resolved.");
+}
+
+bool WGPUBufferImpl::IsPendingMapRequest(FutureID futureID) const {
+  assert(futureID != kNullFutureId);
+  return mPendingMapRequest.futureID == futureID;
+}
+
+void WGPUBufferImpl::AbortPendingMap(const char* message) {
+  if (mMapState == WGPUBufferMapState_Unmapped) {
+    return;
+  }
+
+  mMapState = WGPUBufferMapState_Unmapped;
+
+  FutureID futureId = mPendingMapRequest.futureID;
+  if (futureId == kNullFutureId) {
+    // If we were mappedAtCreation, then there is no pending map request so we
+    // don't need to resolve any futures.
+    return;
+  }
+  mPendingMapRequest = {};
+  GetEventManager().SetFutureReady<MapAsyncEvent>(
+      futureId, WGPUMapAsyncStatus_Aborted, message);
+}
+
+void WGPUBufferImpl::WillDropLastExternalRef() {
+  AbortPendingMap("Buffer was destroyed before mapping was resolved.");
+}
 
 // ----------------------------------------------------------------------------
 // WGPUDeviceImpl implementations.
@@ -934,7 +1144,7 @@
 }
 
 // Future event callbacks.
-void emwgpuOnDeviceCreateComputePipelineCompleted(
+void emwgpuOnCreateComputePipelineCompleted(
     FutureID futureId,
     WGPUCreatePipelineAsyncStatus status,
     WGPUComputePipeline pipeline,
@@ -942,11 +1152,10 @@
   GetEventManager().SetFutureReady<CreateComputePipelineEvent>(
       futureId, status, pipeline, message);
 }
-void emwgpuOnDeviceCreateRenderPipelineCompleted(
-    FutureID futureId,
-    WGPUCreatePipelineAsyncStatus status,
-    WGPURenderPipeline pipeline,
-    const char* message) {
+void emwgpuOnCreateRenderPipelineCompleted(FutureID futureId,
+                                           WGPUCreatePipelineAsyncStatus status,
+                                           WGPURenderPipeline pipeline,
+                                           const char* message) {
   GetEventManager().SetFutureReady<CreateRenderPipelineEvent>(
       futureId, status, pipeline, message);
 }
@@ -955,6 +1164,11 @@
                                  const char* message) {
   GetEventManager().SetFutureReady<DeviceLostEvent>(futureId, reason, message);
 }
+void emwgpuOnMapAsyncCompleted(FutureID futureId,
+                               WGPUMapAsyncStatus status,
+                               const char* message) {
+  GetEventManager().SetFutureReady<MapAsyncEvent>(futureId, status, message);
+}
 void emwgpuOnRequestAdapterCompleted(FutureID futureId,
                                      WGPURequestAdapterStatus status,
                                      WGPUAdapter adapter,
@@ -1098,6 +1312,36 @@
 // Methods of Buffer
 // ----------------------------------------------------------------------------
 
+void wgpuBufferDestroy(WGPUBuffer buffer) {
+  buffer->Destroy();
+}
+
+const void* wgpuBufferGetConstMappedRange(WGPUBuffer buffer,
+                                          size_t offset,
+                                          size_t size) {
+  return buffer->GetConstMappedRange(offset, size);
+}
+
+WGPUBufferMapState wgpuBufferGetMapState(WGPUBuffer buffer) {
+  return buffer->GetMapState();
+}
+
+void* wgpuBufferGetMappedRange(WGPUBuffer buffer, size_t offset, size_t size) {
+  return buffer->GetMappedRange(offset, size);
+}
+
+WGPUFuture wgpuBufferMapAsync2(WGPUBuffer buffer,
+                               WGPUMapMode mode,
+                               size_t offset,
+                               size_t size,
+                               WGPUBufferMapCallbackInfo2 callbackInfo) {
+  return buffer->MapAsync(mode, offset, size, callbackInfo);
+}
+
+void wgpuBufferUnmap(WGPUBuffer buffer) {
+  buffer->Unmap();
+}
+
 // ----------------------------------------------------------------------------
 // Methods of CommandBuffer
 // ----------------------------------------------------------------------------
@@ -1118,6 +1362,13 @@
 // Methods of Device
 // ----------------------------------------------------------------------------
 
+WGPUBuffer wgpuDeviceCreateBuffer(WGPUDevice device,
+                                  const WGPUBufferDescriptor* descriptor) {
+  WGPUBuffer buffer = new WGPUBufferImpl(device, descriptor->mappedAtCreation);
+  emwgpuDeviceCreateBuffer(device, descriptor, buffer);
+  return buffer;
+}
+
 void wgpuDeviceCreateComputePipelineAsync(
     WGPUDevice device,
     const WGPUComputePipelineDescriptor* descriptor,