Also reject mapAsync if the buffer is being mapped

To reflect the WebGPU spec change at
https://github.com/gpuweb/gpuweb/pull/3348

Bug: chromium:1355994
Change-Id: I0159cbd9e1977a05453e3c562a2b0649a0ff930f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/110448
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Takahiro <hogehoge@gachapin.jp>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/dawn/native/Buffer.cpp b/src/dawn/native/Buffer.cpp
index 9256829..d2bdb62 100644
--- a/src/dawn/native/Buffer.cpp
+++ b/src/dawn/native/Buffer.cpp
@@ -37,25 +37,24 @@
 
 namespace {
 struct MapRequestTask : TrackTaskCallback {
-    MapRequestTask(dawn::platform::Platform* platform, Ref<BufferBase> buffer, MapRequestID id)
-        : TrackTaskCallback(platform), buffer(std::move(buffer)), id(id) {}
+    MapRequestTask(dawn::platform::Platform* platform, Ref<BufferBase> buffer)
+        : TrackTaskCallback(platform), buffer(std::move(buffer)) {}
     void Finish() override {
         ASSERT(mSerial != kMaxExecutionSerial);
         TRACE_EVENT1(mPlatform, General, "Buffer::TaskInFlight::Finished", "serial",
                      uint64_t(mSerial));
-        buffer->OnMapRequestCompleted(id, WGPUBufferMapAsyncStatus_Success);
+        buffer->OnMapRequestCompleted(WGPUBufferMapAsyncStatus_Success);
     }
     void HandleDeviceLoss() override {
-        buffer->OnMapRequestCompleted(id, WGPUBufferMapAsyncStatus_DeviceLost);
+        buffer->OnMapRequestCompleted(WGPUBufferMapAsyncStatus_DeviceLost);
     }
     void HandleShutDown() override {
-        buffer->OnMapRequestCompleted(id, WGPUBufferMapAsyncStatus_DestroyedBeforeCallback);
+        buffer->OnMapRequestCompleted(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback);
     }
     ~MapRequestTask() override = default;
 
   private:
     Ref<BufferBase> buffer;
-    MapRequestID id;
 };
 
 class ErrorBuffer final : public BufferBase {
@@ -194,7 +193,7 @@
 }
 
 void BufferBase::DestroyImpl() {
-    if (mState == BufferState::Mapped) {
+    if (mState == BufferState::Mapped || mState == BufferState::PendingMap) {
         UnmapInternal(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback);
     } else if (mState == BufferState::MappedAtCreation) {
         if (mStagingBuffer != nullptr) {
@@ -311,15 +310,17 @@
         case BufferState::Mapped:
         case BufferState::MappedAtCreation:
             return DAWN_VALIDATION_ERROR("%s used in submit while mapped.", this);
+        case BufferState::PendingMap:
+            return DAWN_VALIDATION_ERROR("%s used in submit while pending map.", this);
         case BufferState::Unmapped:
             return {};
     }
     UNREACHABLE();
 }
 
-void BufferBase::CallMapCallback(MapRequestID mapID, WGPUBufferMapAsyncStatus status) {
+void BufferBase::CallMapCallback(WGPUBufferMapAsyncStatus status) {
     ASSERT(!IsError());
-    if (mMapCallback != nullptr && mapID == mLastMapID) {
+    if (mMapCallback != nullptr) {
         // Tag the callback as fired before firing it, otherwise it could fire a second time if
         // for example buffer.Unmap() is called inside the application-provided callback.
         WGPUBufferMapCallback callback = mMapCallback;
@@ -330,6 +331,8 @@
         } else {
             callback(status, mMapUserdata);
         }
+
+        mMapUserdata = 0;
     }
 }
 
@@ -356,20 +359,19 @@
     }
     ASSERT(!IsError());
 
-    mLastMapID++;
     mMapMode = mode;
     mMapOffset = offset;
     mMapSize = size;
     mMapCallback = callback;
     mMapUserdata = userdata;
-    mState = BufferState::Mapped;
+    mState = BufferState::PendingMap;
 
     if (GetDevice()->ConsumedError(MapAsyncImpl(mode, offset, size))) {
-        CallMapCallback(mLastMapID, WGPUBufferMapAsyncStatus_DeviceLost);
+        CallMapCallback(WGPUBufferMapAsyncStatus_DeviceLost);
         return;
     }
     std::unique_ptr<MapRequestTask> request =
-        std::make_unique<MapRequestTask>(GetDevice()->GetPlatform(), this, mLastMapID);
+        std::make_unique<MapRequestTask>(GetDevice()->GetPlatform(), this);
     TRACE_EVENT1(GetDevice()->GetPlatform(), General, "Buffer::APIMapAsync", "serial",
                  uint64_t(GetDevice()->GetPendingCommandSerial()));
     GetDevice()->GetQueue()->TrackTask(std::move(request));
@@ -438,16 +440,11 @@
 }
 
 void BufferBase::UnmapInternal(WGPUBufferMapAsyncStatus callbackStatus) {
-    if (mState == BufferState::Mapped) {
-        // A map request can only be called once, so this will fire only if the request wasn't
-        // completed before the Unmap.
-        // Callbacks are not fired if there is no callback registered, so this is correct for
-        // mappedAtCreation = true.
-        CallMapCallback(mLastMapID, callbackStatus);
+    if (mState == BufferState::PendingMap) {
+        CallMapCallback(callbackStatus);
         UnmapImpl();
-
-        mMapCallback = nullptr;
-        mMapUserdata = 0;
+    } else if (mState == BufferState::Mapped) {
+        UnmapImpl();
     } else if (mState == BufferState::MappedAtCreation) {
         if (mStagingBuffer != nullptr) {
             GetDevice()->ConsumedError(CopyFromStagingBuffer());
@@ -483,6 +480,8 @@
         case BufferState::Mapped:
         case BufferState::MappedAtCreation:
             return DAWN_VALIDATION_ERROR("%s is already mapped.", this);
+        case BufferState::PendingMap:
+            return DAWN_VALIDATION_ERROR("%s is pending map.", this);
         case BufferState::Destroyed:
             return DAWN_VALIDATION_ERROR("%s is destroyed.", this);
         case BufferState::Unmapped:
@@ -542,6 +541,7 @@
             ASSERT(bool{mMapMode & wgpu::MapMode::Read} ^ bool{mMapMode & wgpu::MapMode::Write});
             return !writable || (mMapMode & wgpu::MapMode::Write);
 
+        case BufferState::PendingMap:
         case BufferState::Unmapped:
         case BufferState::Destroyed:
             return false;
@@ -554,8 +554,11 @@
     return {};
 }
 
-void BufferBase::OnMapRequestCompleted(MapRequestID mapID, WGPUBufferMapAsyncStatus status) {
-    CallMapCallback(mapID, status);
+void BufferBase::OnMapRequestCompleted(WGPUBufferMapAsyncStatus status) {
+    if (status == WGPUBufferMapAsyncStatus_Success && mState == BufferState::PendingMap) {
+        mState = BufferState::Mapped;
+    }
+    CallMapCallback(status);
 }
 
 bool BufferBase::NeedsInitialization() const {
diff --git a/src/dawn/native/Buffer.h b/src/dawn/native/Buffer.h
index 6bb1503..76d7f4c 100644
--- a/src/dawn/native/Buffer.h
+++ b/src/dawn/native/Buffer.h
@@ -44,6 +44,7 @@
   public:
     enum class BufferState {
         Unmapped,
+        PendingMap,
         Mapped,
         MappedAtCreation,
         Destroyed,
@@ -62,7 +63,7 @@
     wgpu::BufferUsage GetUsageExternalOnly() const;
 
     MaybeError MapAtCreation();
-    void OnMapRequestCompleted(MapRequestID mapID, WGPUBufferMapAsyncStatus status);
+    void OnMapRequestCompleted(WGPUBufferMapAsyncStatus status);
 
     MaybeError ValidateCanUseOnQueueNow() const;
 
@@ -109,7 +110,7 @@
 
     virtual bool IsCPUWritableAtCreation() const = 0;
     MaybeError CopyFromStagingBuffer();
-    void CallMapCallback(MapRequestID mapID, WGPUBufferMapAsyncStatus status);
+    void CallMapCallback(WGPUBufferMapAsyncStatus status);
 
     MaybeError ValidateMapAsync(wgpu::MapMode mode,
                                 size_t offset,
@@ -128,7 +129,6 @@
 
     WGPUBufferMapCallback mMapCallback = nullptr;
     void* mMapUserdata = 0;
-    MapRequestID mLastMapID = MapRequestID(0);
     wgpu::MapMode mMapMode = wgpu::MapMode::None;
     size_t mMapOffset = 0;
     size_t mMapSize = 0;
diff --git a/src/dawn/tests/end2end/DeviceLostTests.cpp b/src/dawn/tests/end2end/DeviceLostTests.cpp
index 4c2b01ed85..a225c4d 100644
--- a/src/dawn/tests/end2end/DeviceLostTests.cpp
+++ b/src/dawn/tests/end2end/DeviceLostTests.cpp
@@ -55,6 +55,24 @@
         EXPECT_EQ(WGPUBufferMapAsyncStatus_DeviceLost, status);
         EXPECT_EQ(&fakeUserData, userdata);
     }
+
+    void MapAsyncAndWait(const wgpu::Buffer& buffer,
+                         wgpu::MapMode mode,
+                         size_t offset,
+                         size_t size) {
+        bool done = false;
+        buffer.MapAsync(
+            mode, offset, size,
+            [](WGPUBufferMapAsyncStatus status, void* userdata) {
+                ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
+                *static_cast<bool*>(userdata) = true;
+            },
+            &done);
+
+        while (!done) {
+            WaitABit();
+        }
+    }
 };
 
 // Test that DeviceLostCallback is invoked when LostForTestimg is called
@@ -338,7 +356,7 @@
     desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
     wgpu::Buffer buffer = device.CreateBuffer(&desc);
 
-    buffer.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, nullptr);
+    MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, 4);
     queue.Submit(0, nullptr);
 
     const void* rangeBeforeLoss = buffer.GetConstMappedRange();
@@ -355,7 +373,7 @@
     desc.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc;
     wgpu::Buffer buffer = device.CreateBuffer(&desc);
 
-    buffer.MapAsync(wgpu::MapMode::Write, 0, 4, nullptr, nullptr);
+    MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, 4);
     queue.Submit(0, nullptr);
 
     const void* rangeBeforeLoss = buffer.GetConstMappedRange();
diff --git a/src/dawn/tests/unittests/validation/BufferValidationTests.cpp b/src/dawn/tests/unittests/validation/BufferValidationTests.cpp
index 3a0b4e5..57b1dcd 100644
--- a/src/dawn/tests/unittests/validation/BufferValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/BufferValidationTests.cpp
@@ -294,6 +294,7 @@
     {
         wgpu::Buffer buffer = CreateMapReadBuffer(4);
         buffer.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, nullptr);
+        WaitForAllOperations(device);
         AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4);
     }
     {
@@ -303,6 +304,7 @@
     {
         wgpu::Buffer buffer = CreateMapWriteBuffer(4);
         buffer.MapAsync(wgpu::MapMode::Write, 0, 4, nullptr, nullptr);
+        WaitForAllOperations(device);
         AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4);
     }
     {
@@ -311,6 +313,53 @@
     }
 }
 
+// Test map async with a buffer that's pending map
+TEST_F(BufferValidationTest, MapAsync_PendingMap) {
+    // Read + overlapping range
+    {
+        wgpu::Buffer buffer = CreateMapReadBuffer(4);
+        // The first map async call should succeed while the second one should fail
+        buffer.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
+        AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4);
+        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, nullptr))
+            .Times(1);
+        WaitForAllOperations(device);
+    }
+
+    // Read + non-overlapping range
+    {
+        wgpu::Buffer buffer = CreateMapReadBuffer(16);
+        // The first map async call should succeed while the second one should fail
+        buffer.MapAsync(wgpu::MapMode::Read, 0, 8, ToMockBufferMapAsyncCallback, nullptr);
+        AssertMapAsyncError(buffer, wgpu::MapMode::Read, 8, 8);
+        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, nullptr))
+            .Times(1);
+        WaitForAllOperations(device);
+    }
+
+    // Write + overlapping range
+    {
+        wgpu::Buffer buffer = CreateMapWriteBuffer(4);
+        // The first map async call should succeed while the second one should fail
+        buffer.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
+        AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4);
+        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, nullptr))
+            .Times(1);
+        WaitForAllOperations(device);
+    }
+
+    // Write + non-overlapping range
+    {
+        wgpu::Buffer buffer = CreateMapWriteBuffer(16);
+        // The first map async call should succeed while the second one should fail
+        buffer.MapAsync(wgpu::MapMode::Write, 0, 8, ToMockBufferMapAsyncCallback, nullptr);
+        AssertMapAsyncError(buffer, wgpu::MapMode::Write, 8, 8);
+        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, nullptr))
+            .Times(1);
+        WaitForAllOperations(device);
+    }
+}
+
 // Test map async with a buffer that's destroyed
 TEST_F(BufferValidationTest, MapAsync_Destroy) {
     {