diff --git a/dawn.json b/dawn.json index 9216b858fc..b38d1e16f4 100644 --- a/dawn.json +++ b/dawn.json @@ -445,6 +445,10 @@ "name": "get size", "returns": "uint64_t" }, + { + "name": "get map state", + "returns": "buffer map state" + }, { "name": "unmap" }, @@ -482,6 +486,14 @@ {"value": 5, "name": "unmapped before callback"} ] }, + "buffer map state": { + "category": "enum", + "values": [ + {"value": 0, "name": "unmapped"}, + {"value": 1, "name": "pending"}, + {"value": 2, "name": "mapped"} + ] + }, "buffer usage": { "category": "bitmask", "values": [ diff --git a/dawn_wire.json b/dawn_wire.json index c26d4cdcad..46cba66009 100644 --- a/dawn_wire.json +++ b/dawn_wire.json @@ -189,6 +189,7 @@ "BufferMapAsync", "BufferGetConstMappedRange", "BufferGetMappedRange", + "BufferGetMapState", "BufferGetSize", "BufferGetUsage", "DeviceCreateBuffer", diff --git a/src/dawn/native/Buffer.cpp b/src/dawn/native/Buffer.cpp index 1f8f5d9796..cf08c03b1a 100644 --- a/src/dawn/native/Buffer.cpp +++ b/src/dawn/native/Buffer.cpp @@ -239,6 +239,19 @@ wgpu::BufferUsage BufferBase::APIGetUsage() const { return mUsage & ~kAllInternalBufferUsages; } +wgpu::BufferMapState BufferBase::APIGetMapState() const { + switch (mState) { + case BufferState::Mapped: + case BufferState::MappedAtCreation: + return wgpu::BufferMapState::Mapped; + case BufferState::PendingMap: + return wgpu::BufferMapState::Pending; + case BufferState::Unmapped: + case BufferState::Destroyed: + return wgpu::BufferMapState::Unmapped; + } +} + MaybeError BufferBase::MapAtCreation() { DAWN_TRY(MapAtCreationInternal()); diff --git a/src/dawn/native/Buffer.h b/src/dawn/native/Buffer.h index 5fab9b7e45..0091087a27 100644 --- a/src/dawn/native/Buffer.h +++ b/src/dawn/native/Buffer.h @@ -86,6 +86,7 @@ class BufferBase : public ApiObjectBase { void APIUnmap(); void APIDestroy(); wgpu::BufferUsage APIGetUsage() const; + wgpu::BufferMapState APIGetMapState() const; uint64_t APIGetSize() const; protected: diff --git a/src/dawn/tests/unittests/validation/BufferValidationTests.cpp b/src/dawn/tests/unittests/validation/BufferValidationTests.cpp index 3e2ddf4431..e9ca7986b6 100644 --- a/src/dawn/tests/unittests/validation/BufferValidationTests.cpp +++ b/src/dawn/tests/unittests/validation/BufferValidationTests.cpp @@ -1101,3 +1101,44 @@ TEST_F(BufferValidationTest, CreationParameterReflectionNoInternalUsage) { EXPECT_EQ(wgpu::BufferUsage::QueryResolve, buf.GetUsage()); EXPECT_EQ(16u, buf.GetSize()); } + +// Test that GetMapState() shows expected buffer map state +TEST_F(BufferValidationTest, GetMapState) { + // MapRead + { + wgpu::Buffer buf = CreateMapReadBuffer(4); + EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState()); + buf.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, nullptr); + EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState()); + WaitForAllOperations(device); + EXPECT_EQ(wgpu::BufferMapState::Mapped, buf.GetMapState()); + buf.Unmap(); + EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState()); + buf.Destroy(); + EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState()); + } + + // MapWrite + { + wgpu::Buffer buf = CreateMapWriteBuffer(4); + EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState()); + buf.MapAsync(wgpu::MapMode::Write, 0, 4, nullptr, nullptr); + EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState()); + WaitForAllOperations(device); + EXPECT_EQ(wgpu::BufferMapState::Mapped, buf.GetMapState()); + buf.Unmap(); + EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState()); + buf.Destroy(); + EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState()); + } + + // MappedAtCreation + { + wgpu::Buffer buf = BufferMappedAtCreation(4, wgpu::BufferUsage::CopySrc); + EXPECT_EQ(wgpu::BufferMapState::Mapped, buf.GetMapState()); + buf.Unmap(); + EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState()); + buf.Destroy(); + EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState()); + } +} diff --git a/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp b/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp index 20a7120d87..7ae8d130cd 100644 --- a/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp +++ b/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp @@ -746,7 +746,7 @@ TEST_F(WireBufferMappingTests, MapAfterDisconnect) { wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, this); } -// Test that mappinga again while pending map immediately cause an error +// Test that mapping again while pending map immediately cause an error TEST_F(WireBufferMappingTests, PendingMapImmediateError) { SetupBuffer(WGPUMapMode_Read); @@ -756,6 +756,66 @@ TEST_F(WireBufferMappingTests, PendingMapImmediateError) { wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, this); } +// Test that GetMapState() returns map state as expected +TEST_F(WireBufferMappingTests, GetMapState) { + SetupBuffer(WGPUMapMode_Read); + + // Server-side success case + { + uint32_t bufferContent = 31337; + EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&bufferContent)); + EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1); + + ASSERT_EQ(wgpuBufferGetMapState(buffer), WGPUBufferMapState_Unmapped); + wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, + nullptr); + + // map state should become pending immediately after map async call + ASSERT_EQ(wgpuBufferGetMapState(buffer), WGPUBufferMapState_Pending); + FlushClient(); + + // map state should be pending until receiving a response from server + ASSERT_EQ(wgpuBufferGetMapState(buffer), WGPUBufferMapState_Pending); + FlushServer(); + + // mapping succeeded + ASSERT_EQ(wgpuBufferGetMapState(buffer), WGPUBufferMapState_Mapped); + } + + wgpuBufferUnmap(buffer); + EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); + FlushClient(); + + // Server-side error case + { + EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); + })); + EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1); + + ASSERT_EQ(wgpuBufferGetMapState(buffer), WGPUBufferMapState_Unmapped); + wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, + nullptr); + + // map state should become pending immediately after map async call + ASSERT_EQ(wgpuBufferGetMapState(buffer), WGPUBufferMapState_Pending); + FlushClient(); + + // map state should be pending until receiving a response from server + ASSERT_EQ(wgpuBufferGetMapState(buffer), WGPUBufferMapState_Pending); + FlushServer(); + + // mapping failed + ASSERT_EQ(wgpuBufferGetMapState(buffer), WGPUBufferMapState_Unmapped); + } +} + // Hack to pass in test context into user callback struct TestData { WireBufferMappingTests* pTest; diff --git a/src/dawn/wire/client/Buffer.cpp b/src/dawn/wire/client/Buffer.cpp index 9c7e969352..345a75bf5f 100644 --- a/src/dawn/wire/client/Buffer.cpp +++ b/src/dawn/wire/client/Buffer.cpp @@ -386,6 +386,21 @@ uint64_t Buffer::GetSize() const { return mSize; } +WGPUBufferMapState Buffer::GetMapState() const { + switch (mMapState) { + case MapState::MappedForRead: + case MapState::MappedForWrite: + case MapState::MappedAtCreation: + return WGPUBufferMapState_Mapped; + case MapState::Unmapped: + if (mPendingMap) { + return WGPUBufferMapState_Pending; + } else { + return WGPUBufferMapState_Unmapped; + } + } +} + bool Buffer::IsMappedForReading() const { return mMapState == MapState::MappedForRead; } diff --git a/src/dawn/wire/client/Buffer.h b/src/dawn/wire/client/Buffer.h index 1784c0cca0..72339b3e6c 100644 --- a/src/dawn/wire/client/Buffer.h +++ b/src/dawn/wire/client/Buffer.h @@ -52,6 +52,8 @@ class Buffer final : public ObjectBase { WGPUBufferUsage GetUsage() const; uint64_t GetSize() const; + WGPUBufferMapState GetMapState() const; + private: void CancelCallbacksForDisconnect() override; void InvokeAndClearCallback(WGPUBufferMapAsyncStatus status);