mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-06 22:53:35 +00:00
dawn_wire: Correctly handle buffer state changes on Destroy()
The server didn't take intercept the destroy() call which meant the buffer could be unmapped by dawn_native without the status updated in ServerBuffer. This caused crash when a subsequent UpdateMappedData command was handled and tried to write into the mapped buffer. The client needs to also track destroy() otherwise it could sent an UpdateMappedData to a destroyed buffer which is a fatal error. Tests are added that cover the client-server interaction for this, but the pattern that the following is unfortunately not tested directly against the wire server: - CreateBufferMapped - Destroy - UpdateMappedData Bug: chromium:1068466 Change-Id: If5185d4a8a81cd5f6bb41c9888a18c44c14b2de4 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/18961 Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Kai Ninomiya <kainino@chromium.org>
This commit is contained in:
parent
9d2de1d6d4
commit
36cd17488a
@ -106,6 +106,7 @@
|
|||||||
"FenceOnCompletion"
|
"FenceOnCompletion"
|
||||||
],
|
],
|
||||||
"client_handwritten_commands": [
|
"client_handwritten_commands": [
|
||||||
|
"BufferDestroy",
|
||||||
"BufferUnmap",
|
"BufferUnmap",
|
||||||
"DeviceCreateBuffer",
|
"DeviceCreateBuffer",
|
||||||
"DeviceCreateBufferMapped",
|
"DeviceCreateBufferMapped",
|
||||||
@ -119,6 +120,7 @@
|
|||||||
"Fence"
|
"Fence"
|
||||||
],
|
],
|
||||||
"server_custom_pre_handler_commands": [
|
"server_custom_pre_handler_commands": [
|
||||||
|
"BufferDestroy",
|
||||||
"BufferUnmap"
|
"BufferUnmap"
|
||||||
],
|
],
|
||||||
"server_handwritten_commands": [
|
"server_handwritten_commands": [
|
||||||
|
@ -368,6 +368,22 @@ namespace dawn_wire { namespace client {
|
|||||||
cmd.Serialize(allocatedBuffer, *buffer->device->GetClient());
|
cmd.Serialize(allocatedBuffer, *buffer->device->GetClient());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClientBufferDestroy(WGPUBuffer cBuffer) {
|
||||||
|
Buffer* buffer = reinterpret_cast<Buffer*>(cBuffer);
|
||||||
|
|
||||||
|
// Cancel or remove all mappings
|
||||||
|
buffer->writeHandle = nullptr;
|
||||||
|
buffer->readHandle = nullptr;
|
||||||
|
buffer->ClearMapRequests(WGPUBufferMapAsyncStatus_Unknown);
|
||||||
|
|
||||||
|
BufferDestroyCmd cmd;
|
||||||
|
cmd.self = cBuffer;
|
||||||
|
size_t requiredSize = cmd.GetRequiredSize();
|
||||||
|
char* allocatedBuffer =
|
||||||
|
static_cast<char*>(buffer->device->GetClient()->GetCmdSpace(requiredSize));
|
||||||
|
cmd.Serialize(allocatedBuffer, *buffer->device->GetClient());
|
||||||
|
}
|
||||||
|
|
||||||
WGPUFence ClientQueueCreateFence(WGPUQueue cSelf, WGPUFenceDescriptor const* descriptor) {
|
WGPUFence ClientQueueCreateFence(WGPUQueue cSelf, WGPUFenceDescriptor const* descriptor) {
|
||||||
Queue* queue = reinterpret_cast<Queue*>(cSelf);
|
Queue* queue = reinterpret_cast<Queue*>(cSelf);
|
||||||
Device* device = queue->device;
|
Device* device = queue->device;
|
||||||
|
@ -31,6 +31,19 @@ namespace dawn_wire { namespace server {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Server::PreHandleBufferDestroy(const BufferDestroyCmd& cmd) {
|
||||||
|
// Destroying a buffer does an implicit unmapping.
|
||||||
|
auto* buffer = BufferObjects().Get(cmd.selfId);
|
||||||
|
DAWN_ASSERT(buffer != nullptr);
|
||||||
|
|
||||||
|
// The buffer was destroyed. Clear the Read/WriteHandle.
|
||||||
|
buffer->readHandle = nullptr;
|
||||||
|
buffer->writeHandle = nullptr;
|
||||||
|
buffer->mapWriteState = BufferMapWriteState::Unmapped;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Server::DoBufferMapAsync(ObjectId bufferId,
|
bool Server::DoBufferMapAsync(ObjectId bufferId,
|
||||||
uint32_t requestSerial,
|
uint32_t requestSerial,
|
||||||
bool isWrite,
|
bool isWrite,
|
||||||
|
@ -588,6 +588,61 @@ TEST_F(WireMemoryTransferServiceTests, BufferMapReadDeserializeInitialDataFailur
|
|||||||
EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
|
EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test MapRead destroying the buffer before unmapping on the client side.
|
||||||
|
TEST_F(WireMemoryTransferServiceTests, BufferMapReadDestroyBeforeUnmap) {
|
||||||
|
WGPUBuffer buffer;
|
||||||
|
WGPUBuffer apiBuffer;
|
||||||
|
std::tie(apiBuffer, buffer) = CreateBuffer();
|
||||||
|
FlushClient();
|
||||||
|
|
||||||
|
// The client should create and serialize a ReadHandle on mapReadAsync.
|
||||||
|
ClientReadHandle* clientHandle = ExpectReadHandleCreation();
|
||||||
|
ExpectReadHandleSerialization(clientHandle);
|
||||||
|
|
||||||
|
wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr);
|
||||||
|
|
||||||
|
// The server should deserialize the MapRead handle from the client and then serialize
|
||||||
|
// an initialization message.
|
||||||
|
ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
|
||||||
|
ExpectServerReadHandleInitialize(serverHandle);
|
||||||
|
|
||||||
|
// Mock a successful callback
|
||||||
|
EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, _, _))
|
||||||
|
.WillOnce(InvokeWithoutArgs([&]() {
|
||||||
|
api.CallMapReadCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success, &mBufferContent,
|
||||||
|
sizeof(mBufferContent));
|
||||||
|
}));
|
||||||
|
|
||||||
|
FlushClient();
|
||||||
|
|
||||||
|
// The client receives a successful callback.
|
||||||
|
EXPECT_CALL(*mockBufferMapReadCallback,
|
||||||
|
Call(WGPUBufferMapAsyncStatus_Success, &mBufferContent, sizeof(mBufferContent), _))
|
||||||
|
.Times(1);
|
||||||
|
|
||||||
|
// The client should receive the handle initialization message from the server.
|
||||||
|
ExpectClientReadHandleDeserializeInitialize(clientHandle, &mBufferContent);
|
||||||
|
|
||||||
|
FlushServer();
|
||||||
|
|
||||||
|
// THIS IS THE TEST: destroy the buffer before unmapping and check it destroyed the mapping
|
||||||
|
// immediately, both in the client and server side.
|
||||||
|
{
|
||||||
|
EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
|
||||||
|
wgpuBufferDestroy(buffer);
|
||||||
|
|
||||||
|
EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
|
||||||
|
EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1);
|
||||||
|
FlushClient();
|
||||||
|
|
||||||
|
// The handle is already destroyed so unmap only results in a server unmap call.
|
||||||
|
wgpuBufferUnmap(buffer);
|
||||||
|
|
||||||
|
EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
|
||||||
|
FlushClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Test successful MapWrite.
|
// Test successful MapWrite.
|
||||||
TEST_F(WireMemoryTransferServiceTests, BufferMapWriteSuccess) {
|
TEST_F(WireMemoryTransferServiceTests, BufferMapWriteSuccess) {
|
||||||
WGPUBuffer buffer;
|
WGPUBuffer buffer;
|
||||||
@ -823,6 +878,62 @@ TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDeserializeFlushFailure) {
|
|||||||
EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
|
EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test MapWrite destroying the buffer before unmapping on the client side.
|
||||||
|
TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDestroyBeforeUnmap) {
|
||||||
|
WGPUBuffer buffer;
|
||||||
|
WGPUBuffer apiBuffer;
|
||||||
|
std::tie(apiBuffer, buffer) = CreateBuffer();
|
||||||
|
FlushClient();
|
||||||
|
|
||||||
|
ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
|
||||||
|
ExpectWriteHandleSerialization(clientHandle);
|
||||||
|
|
||||||
|
wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr);
|
||||||
|
|
||||||
|
// The server should then deserialize the WriteHandle from the client.
|
||||||
|
ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
|
||||||
|
|
||||||
|
// Mock a successful callback.
|
||||||
|
EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _))
|
||||||
|
.WillOnce(InvokeWithoutArgs([&]() {
|
||||||
|
api.CallMapWriteCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success,
|
||||||
|
&mMappedBufferContent, sizeof(mMappedBufferContent));
|
||||||
|
}));
|
||||||
|
|
||||||
|
FlushClient();
|
||||||
|
|
||||||
|
// The client receives a successful callback.
|
||||||
|
EXPECT_CALL(*mockBufferMapWriteCallback,
|
||||||
|
Call(WGPUBufferMapAsyncStatus_Success, &mMappedBufferContent,
|
||||||
|
sizeof(mMappedBufferContent), _))
|
||||||
|
.Times(1);
|
||||||
|
|
||||||
|
// Since the mapping succeeds, the client opens the WriteHandle.
|
||||||
|
ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
|
||||||
|
|
||||||
|
FlushServer();
|
||||||
|
|
||||||
|
// The client writes to the handle contents.
|
||||||
|
mMappedBufferContent = mUpdatedBufferContent;
|
||||||
|
|
||||||
|
// THIS IS THE TEST: destroy the buffer before unmapping and check it destroyed the mapping
|
||||||
|
// immediately, both in the client and server side.
|
||||||
|
{
|
||||||
|
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
|
||||||
|
wgpuBufferDestroy(buffer);
|
||||||
|
|
||||||
|
EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
|
||||||
|
EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1);
|
||||||
|
FlushClient();
|
||||||
|
|
||||||
|
// The handle is already destroyed so unmap only results in a server unmap call.
|
||||||
|
wgpuBufferUnmap(buffer);
|
||||||
|
|
||||||
|
EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
|
||||||
|
FlushClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Test successful CreateBufferMappedAsync.
|
// Test successful CreateBufferMappedAsync.
|
||||||
TEST_F(WireMemoryTransferServiceTests, CreateBufferMappedAsyncSuccess) {
|
TEST_F(WireMemoryTransferServiceTests, CreateBufferMappedAsyncSuccess) {
|
||||||
// The client should create and serialize a WriteHandle on createBufferMappedAsync
|
// The client should create and serialize a WriteHandle on createBufferMappedAsync
|
||||||
@ -1115,3 +1226,42 @@ TEST_F(WireMemoryTransferServiceTests, CreateBufferMappedDeserializeFlushFailure
|
|||||||
|
|
||||||
EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
|
EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test CreateBufferMapped destroying the buffer before unmapping on the client side.
|
||||||
|
TEST_F(WireMemoryTransferServiceTests, CreateBufferMappedDestroyBeforeUnmap) {
|
||||||
|
// The client should create and serialize a WriteHandle on createBufferMapped.
|
||||||
|
ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
|
||||||
|
|
||||||
|
// Staging data is immediately available so the handle is Opened.
|
||||||
|
ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
|
||||||
|
|
||||||
|
ExpectWriteHandleSerialization(clientHandle);
|
||||||
|
|
||||||
|
// The server should then deserialize the WriteHandle from the client.
|
||||||
|
ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
|
||||||
|
|
||||||
|
WGPUCreateBufferMappedResult result;
|
||||||
|
WGPUCreateBufferMappedResult apiResult;
|
||||||
|
std::tie(apiResult, result) = CreateBufferMapped();
|
||||||
|
FlushClient();
|
||||||
|
|
||||||
|
// Update the mapped contents.
|
||||||
|
mMappedBufferContent = mUpdatedBufferContent;
|
||||||
|
|
||||||
|
// THIS IS THE TEST: destroy the buffer before unmapping and check it destroyed the mapping
|
||||||
|
// immediately, both in the client and server side.
|
||||||
|
{
|
||||||
|
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
|
||||||
|
wgpuBufferDestroy(result.buffer);
|
||||||
|
|
||||||
|
EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
|
||||||
|
EXPECT_CALL(api, BufferDestroy(apiResult.buffer)).Times(1);
|
||||||
|
FlushClient();
|
||||||
|
|
||||||
|
// The handle is already destroyed so unmap only results in a server unmap call.
|
||||||
|
wgpuBufferUnmap(result.buffer);
|
||||||
|
|
||||||
|
EXPECT_CALL(api, BufferUnmap(apiResult.buffer)).Times(1);
|
||||||
|
FlushClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user