MapAsync update: keep read/write handle through lifetime of mappable buffers
Change dawn read/write handle for buffer mapping to be created at buffer
creation time instead of at mapAsync time. Update related buffer mapping
tests and wire tests.
Bug: dawn:773
Change-Id: I7dd423c94e1bc15cfe561ea33ec9e348ddf2bfe0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/51164
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Shrek Shao <shrekshao@google.com>
diff --git a/dawn_wire.json b/dawn_wire.json
index 5a71c22..406ef4e 100644
--- a/dawn_wire.json
+++ b/dawn_wire.json
@@ -20,24 +20,26 @@
"commands": {
"buffer map async": [
{ "name": "buffer id", "type": "ObjectId" },
- { "name": "request serial", "type": "uint32_t" },
+ { "name": "request serial", "type": "uint64_t" },
{ "name": "mode", "type": "map mode" },
{ "name": "offset", "type": "uint64_t"},
- { "name": "size", "type": "uint64_t"},
- { "name": "handle create info length", "type": "uint64_t" },
- { "name": "handle create info", "type": "uint8_t", "annotation": "const*", "length": "handle create info length", "skip_serialize": true}
+ { "name": "size", "type": "uint64_t"}
],
"buffer update mapped data": [
{ "name": "buffer id", "type": "ObjectId" },
- { "name": "write flush info length", "type": "uint64_t" },
- { "name": "write flush info", "type": "uint8_t", "annotation": "const*", "length": "write flush info length", "skip_serialize": true}
+ { "name": "write data update info length", "type": "uint64_t" },
+ { "name": "write data update info", "type": "uint8_t", "annotation": "const*", "length": "write data update info length", "skip_serialize": true},
+ { "name": "offset", "type": "uint64_t"},
+ { "name": "size", "type": "uint64_t"}
],
"device create buffer": [
{ "name": "device id", "type": "ObjectId" },
{ "name": "descriptor", "type": "buffer descriptor", "annotation": "const*" },
{ "name": "result", "type": "ObjectHandle", "handle_type": "buffer" },
- { "name": "handle create info length", "type": "uint64_t" },
- { "name": "handle create info", "type": "uint8_t", "annotation": "const*", "length": "handle create info length", "skip_serialize": true}
+ { "name": "read handle create info length", "type": "uint64_t" },
+ { "name": "read handle create info", "type": "uint8_t", "annotation": "const*", "length": "read handle create info length", "skip_serialize": true},
+ { "name": "write handle create info length", "type": "uint64_t" },
+ { "name": "write handle create info", "type": "uint8_t", "annotation": "const*", "length": "write handle create info length", "skip_serialize": true}
],
"device create compute pipeline async": [
{ "name": "device id", "type": "ObjectId" },
@@ -87,10 +89,10 @@
"return commands": {
"buffer map async callback": [
{ "name": "buffer", "type": "ObjectHandle", "handle_type": "buffer" },
- { "name": "request serial", "type": "uint32_t" },
+ { "name": "request serial", "type": "uint64_t" },
{ "name": "status", "type": "uint32_t" },
- { "name": "read initial data info length", "type": "uint64_t" },
- { "name": "read initial data info", "type": "uint8_t", "annotation": "const*", "length": "read initial data info length", "skip_serialize": true }
+ { "name": "read data update info length", "type": "uint64_t" },
+ { "name": "read data update info", "type": "uint8_t", "annotation": "const*", "length": "read data update info length", "skip_serialize": true }
],
"device create compute pipeline async callback": [
{ "name": "device", "type": "ObjectHandle", "handle_type": "device" },
diff --git a/src/dawn_wire/WireServer.cpp b/src/dawn_wire/WireServer.cpp
index bb3d7ba..bad5957 100644
--- a/src/dawn_wire/WireServer.cpp
+++ b/src/dawn_wire/WireServer.cpp
@@ -68,8 +68,10 @@
MemoryTransferService::WriteHandle::~WriteHandle() = default;
- void MemoryTransferService::WriteHandle::SetTarget(void* data, size_t dataLength) {
+ void MemoryTransferService::WriteHandle::SetTarget(void* data) {
mTargetData = data;
+ }
+ void MemoryTransferService::WriteHandle::SetDataLength(size_t dataLength) {
mDataLength = dataLength;
}
} // namespace server
diff --git a/src/dawn_wire/client/Buffer.cpp b/src/dawn_wire/client/Buffer.cpp
index 6c9c70e..b735280 100644
--- a/src/dawn_wire/client/Buffer.cpp
+++ b/src/dawn_wire/client/Buffer.cpp
@@ -33,64 +33,93 @@
return device->CreateErrorBuffer();
}
+ std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = nullptr;
std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
- void* writeData = nullptr;
- size_t writeHandleCreateInfoLength = 0;
- // If the buffer is mapped at creation, create a write handle that will represent the
- // mapping of the whole buffer.
- if (descriptor->mappedAtCreation) {
- // Create the handle.
- writeHandle.reset(
- wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size));
- if (writeHandle == nullptr) {
- device->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed");
- return device->CreateErrorBuffer();
+ DeviceCreateBufferCmd cmd;
+ cmd.deviceId = device->id;
+ cmd.descriptor = descriptor;
+ cmd.readHandleCreateInfoLength = 0;
+ cmd.readHandleCreateInfo = nullptr;
+ cmd.writeHandleCreateInfoLength = 0;
+ cmd.writeHandleCreateInfo = nullptr;
+
+ if (mappable) {
+ if ((descriptor->usage & WGPUBufferUsage_MapRead) != 0) {
+ // Create the read handle on buffer creation.
+ readHandle.reset(
+ wireClient->GetMemoryTransferService()->CreateReadHandle(descriptor->size));
+ if (readHandle == nullptr) {
+ device->InjectError(WGPUErrorType_OutOfMemory,
+ "Failed to create buffer mapping");
+ return device->CreateErrorBuffer();
+ }
+ cmd.readHandleCreateInfoLength = readHandle->SerializeCreateSize();
}
- // Open the handle, it may fail by returning a nullptr in writeData.
- size_t writeDataLength = 0;
- std::tie(writeData, writeDataLength) = writeHandle->Open();
- if (writeData == nullptr) {
- device->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed");
- return device->CreateErrorBuffer();
+ if ((descriptor->usage & WGPUBufferUsage_MapWrite) != 0 ||
+ descriptor->mappedAtCreation) {
+ // Create the write handle on buffer creation.
+ writeHandle.reset(
+ wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size));
+ if (writeHandle == nullptr) {
+ device->InjectError(WGPUErrorType_OutOfMemory,
+ "Failed to create buffer mapping");
+ return device->CreateErrorBuffer();
+ }
+ cmd.writeHandleCreateInfoLength = writeHandle->SerializeCreateSize();
}
- ASSERT(writeDataLength == descriptor->size);
-
- // Get the serialization size of the write handle.
- writeHandleCreateInfoLength = writeHandle->SerializeCreateSize();
}
// Create the buffer and send the creation command.
+ // This must happen after any potential device->CreateErrorBuffer()
+ // as server expects allocating ids to be monotonically increasing
auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(wireClient);
Buffer* buffer = bufferObjectAndSerial->object.get();
buffer->mDevice = device;
buffer->mDeviceIsAlive = device->GetAliveWeakPtr();
buffer->mSize = descriptor->size;
+ buffer->mDestructWriteHandleOnUnmap = false;
- DeviceCreateBufferCmd cmd;
- cmd.deviceId = device->id;
- cmd.descriptor = descriptor;
+ if (descriptor->mappedAtCreation) {
+ // If the buffer is mapped at creation, a write handle is created and will be
+ // destructed on unmap if the buffer doesn't have MapWrite usage
+ // The buffer is mapped right now.
+ buffer->mMapState = MapState::MappedAtCreation;
+
+ // This flag is for write handle created by mappedAtCreation
+ // instead of MapWrite usage. We don't have such a case for read handle
+ buffer->mDestructWriteHandleOnUnmap =
+ (descriptor->usage & WGPUBufferUsage_MapWrite) == 0;
+
+ buffer->mMapOffset = 0;
+ buffer->mMapSize = buffer->mSize;
+ ASSERT(writeHandle != nullptr);
+ buffer->mMappedData = writeHandle->GetData();
+ }
+
cmd.result = ObjectHandle{buffer->id, bufferObjectAndSerial->generation};
- cmd.handleCreateInfoLength = writeHandleCreateInfoLength;
- cmd.handleCreateInfo = nullptr;
wireClient->SerializeCommand(
- cmd, writeHandleCreateInfoLength, [&](SerializeBuffer* serializeBuffer) {
- if (descriptor->mappedAtCreation) {
- char* writeHandleBuffer;
+ cmd, cmd.readHandleCreateInfoLength + cmd.writeHandleCreateInfoLength,
+ [&](SerializeBuffer* serializeBuffer) {
+ if (readHandle != nullptr) {
+ char* readHandleBuffer;
WIRE_TRY(
- serializeBuffer->NextN(writeHandleCreateInfoLength, &writeHandleBuffer));
+ serializeBuffer->NextN(cmd.readHandleCreateInfoLength, &readHandleBuffer));
+ // Serialize the ReadHandle into the space after the command.
+ readHandle->SerializeCreate(readHandleBuffer);
+ buffer->mReadHandle = std::move(readHandle);
+ }
+ if (writeHandle != nullptr) {
+ char* writeHandleBuffer;
+ WIRE_TRY(serializeBuffer->NextN(cmd.writeHandleCreateInfoLength,
+ &writeHandleBuffer));
// Serialize the WriteHandle into the space after the command.
writeHandle->SerializeCreate(writeHandleBuffer);
-
- // Set the buffer state for the mapping at creation. The buffer now owns the
- // write handle..
buffer->mWriteHandle = std::move(writeHandle);
- buffer->mMappedData = writeData;
- buffer->mMapOffset = 0;
- buffer->mMapSize = buffer->mSize;
}
+
return WireResult::Success;
});
return ToAPI(buffer);
@@ -120,7 +149,7 @@
}
mRequests.clear();
- FreeMappedData(true);
+ FreeMappedData();
}
void Buffer::CancelCallbacksForDisconnect() {
@@ -146,97 +175,41 @@
size = mSize - offset;
}
- bool isReadMode = mode & WGPUMapMode_Read;
- bool isWriteMode = mode & WGPUMapMode_Write;
-
- // Step 1. Do early validation of READ ^ WRITE because the server rejects mode = 0.
- if (!(isReadMode ^ isWriteMode)) {
- if (!mDeviceIsAlive.expired()) {
- mDevice->InjectError(WGPUErrorType_Validation,
- "MapAsync mode must be exactly one of Read or Write");
- }
- if (callback != nullptr) {
- callback(WGPUBufferMapAsyncStatus_Error, userdata);
- }
- return;
- }
-
- // Step 2. Create the request structure that will hold information while this mapping is
+ // Create the request structure that will hold information while this mapping is
// in flight.
- uint32_t serial = mRequestSerial++;
+ uint64_t serial = mRequestSerial++;
ASSERT(mRequests.find(serial) == mRequests.end());
Buffer::MapRequestData request = {};
request.callback = callback;
request.userdata = userdata;
- request.size = size;
request.offset = offset;
-
- // Step 2a: Create the read / write handles for this request.
- if (isReadMode) {
- request.readHandle.reset(client->GetMemoryTransferService()->CreateReadHandle(size));
- if (request.readHandle == nullptr) {
- if (!mDeviceIsAlive.expired()) {
- mDevice->InjectError(WGPUErrorType_OutOfMemory,
- "Failed to create buffer mapping");
- }
- callback(WGPUBufferMapAsyncStatus_Error, userdata);
- return;
- }
- } else {
- ASSERT(isWriteMode);
- request.writeHandle.reset(client->GetMemoryTransferService()->CreateWriteHandle(size));
- if (request.writeHandle == nullptr) {
- if (!mDeviceIsAlive.expired()) {
- mDevice->InjectError(WGPUErrorType_OutOfMemory,
- "Failed to create buffer mapping");
- }
- callback(WGPUBufferMapAsyncStatus_Error, userdata);
- return;
- }
+ request.size = size;
+ if (mode & WGPUMapMode_Read) {
+ request.type = MapRequestType::Read;
+ } else if (mode & WGPUMapMode_Write) {
+ request.type = MapRequestType::Write;
}
- // Step 3. Serialize the command to send to the server.
+ // Serialize the command to send to the server.
BufferMapAsyncCmd cmd;
cmd.bufferId = this->id;
cmd.requestSerial = serial;
cmd.mode = mode;
cmd.offset = offset;
cmd.size = size;
- cmd.handleCreateInfo = nullptr;
- // Step 3a. Fill the handle create info in the command.
- if (isReadMode) {
- cmd.handleCreateInfoLength = request.readHandle->SerializeCreateSize();
- client->SerializeCommand(
- cmd, cmd.handleCreateInfoLength, [&](SerializeBuffer* serializeBuffer) {
- char* readHandleBuffer;
- WIRE_TRY(serializeBuffer->NextN(cmd.handleCreateInfoLength, &readHandleBuffer));
- request.readHandle->SerializeCreate(readHandleBuffer);
- return WireResult::Success;
- });
- } else {
- ASSERT(isWriteMode);
- cmd.handleCreateInfoLength = request.writeHandle->SerializeCreateSize();
- client->SerializeCommand(
- cmd, cmd.handleCreateInfoLength, [&](SerializeBuffer* serializeBuffer) {
- char* writeHandleBuffer;
- WIRE_TRY(
- serializeBuffer->NextN(cmd.handleCreateInfoLength, &writeHandleBuffer));
- request.writeHandle->SerializeCreate(writeHandleBuffer);
- return WireResult::Success;
- });
- }
+ client->SerializeCommand(cmd);
- // Step 4. Register this request so that we can retrieve it from its serial when the server
+ // Register this request so that we can retrieve it from its serial when the server
// sends the callback.
mRequests[serial] = std::move(request);
}
- bool Buffer::OnMapAsyncCallback(uint32_t requestSerial,
+ bool Buffer::OnMapAsyncCallback(uint64_t requestSerial,
uint32_t status,
- uint64_t readInitialDataInfoLength,
- const uint8_t* readInitialDataInfo) {
+ uint64_t readDataUpdateInfoLength,
+ const uint8_t* readDataUpdateInfo) {
auto requestIt = mRequests.find(requestSerial);
if (requestIt == mRequests.end()) {
return false;
@@ -254,60 +227,50 @@
return false;
};
- bool isRead = request.readHandle != nullptr;
- bool isWrite = request.writeHandle != nullptr;
- ASSERT(isRead != isWrite);
-
// Take into account the client-side status of the request if the server says it is a success.
if (status == WGPUBufferMapAsyncStatus_Success) {
status = request.clientStatus;
}
- size_t mappedDataLength = 0;
- const void* mappedData = nullptr;
if (status == WGPUBufferMapAsyncStatus_Success) {
- if (mReadHandle || mWriteHandle) {
- // Buffer is already mapped.
- return FailRequest();
+ switch (request.type) {
+ case MapRequestType::Read: {
+ if (readDataUpdateInfoLength > std::numeric_limits<size_t>::max()) {
+ // This is the size of data deserialized from the command stream, which must
+ // be CPU-addressable.
+ return FailRequest();
+ }
+
+ // Validate to prevent bad map request; buffer destroyed during map request
+ if (mReadHandle == nullptr) {
+ return FailRequest();
+ }
+ // Update user map data with server returned data
+ if (!mReadHandle->DeserializeDataUpdate(
+ readDataUpdateInfo, static_cast<size_t>(readDataUpdateInfoLength),
+ request.offset, request.size)) {
+ return FailRequest();
+ }
+ mMapState = MapState::MappedForRead;
+ mMappedData = const_cast<void*>(mReadHandle->GetData());
+ break;
+ }
+ case MapRequestType::Write: {
+ if (mWriteHandle == nullptr) {
+ return FailRequest();
+ }
+ mMapState = MapState::MappedForWrite;
+ mMappedData = mWriteHandle->GetData();
+ break;
+ }
+ default:
+ UNREACHABLE();
}
- if (isRead) {
- if (readInitialDataInfoLength > std::numeric_limits<size_t>::max()) {
- // This is the size of data deserialized from the command stream, which must be
- // CPU-addressable.
- return FailRequest();
- }
-
- // The server serializes metadata to initialize the contents of the ReadHandle.
- // Deserialize the message and return a pointer and size of the mapped data for
- // reading.
- if (!request.readHandle->DeserializeInitialData(
- readInitialDataInfo, static_cast<size_t>(readInitialDataInfoLength),
- &mappedData, &mappedDataLength)) {
- // Deserialization shouldn't fail. This is a fatal error.
- return FailRequest();
- }
- ASSERT(mappedData != nullptr);
-
- } else {
- // Open the WriteHandle. This returns a pointer and size of mapped memory.
- // On failure, |mappedData| may be null.
- std::tie(mappedData, mappedDataLength) = request.writeHandle->Open();
-
- if (mappedData == nullptr) {
- return FailRequest();
- }
- }
-
- // The MapAsync request was successful. The buffer now owns the Read/Write handles
- // until Unmap().
- mReadHandle = std::move(request.readHandle);
- mWriteHandle = std::move(request.writeHandle);
+ mMapOffset = request.offset;
+ mMapSize = request.size;
}
- mMapOffset = request.offset;
- mMapSize = request.size;
- mMappedData = const_cast<void*>(mappedData);
if (request.callback) {
request.callback(static_cast<WGPUBufferMapAsyncStatus>(status), request.userdata);
}
@@ -319,7 +282,7 @@
if (!IsMappedForWriting() || !CheckGetMappedRangeOffsetSize(offset, size)) {
return nullptr;
}
- return static_cast<uint8_t*>(mMappedData) + (offset - mMapOffset);
+ return static_cast<uint8_t*>(mMappedData) + offset;
}
const void* Buffer::GetConstMappedRange(size_t offset, size_t size) {
@@ -327,7 +290,7 @@
!CheckGetMappedRangeOffsetSize(offset, size)) {
return nullptr;
}
- return static_cast<uint8_t*>(mMappedData) + (offset - mMapOffset);
+ return static_cast<uint8_t*>(mMappedData) + offset;
}
void Buffer::Unmap() {
@@ -339,32 +302,57 @@
// - Server -> Client: Result of MapRequest1
// - Unmap locally on the client
// - Server -> Client: Result of MapRequest2
- if (mWriteHandle) {
+
+ // TODO(dawn:608): mDevice->InjectError(WGPUErrorType_Validation) for map oom failure
+ // and separate from buffer destroyed before unmap case
+
+ // mWriteHandle can still be nullptr if buffer has been destroyed before unmap
+ if ((mMapState == MapState::MappedForWrite || mMapState == MapState::MappedAtCreation) &&
+ mWriteHandle != nullptr) {
// Writes need to be flushed before Unmap is sent. Unmap calls all associated
// in-flight callbacks which may read the updated data.
- ASSERT(mReadHandle == nullptr);
- // Get the serialization size of metadata to flush writes.
- size_t writeFlushInfoLength = mWriteHandle->SerializeFlushSize();
+ // Get the serialization size of data update writes.
+ size_t writeDataUpdateInfoLength =
+ mWriteHandle->SizeOfSerializeDataUpdate(mMapOffset, mMapSize);
BufferUpdateMappedDataCmd cmd;
cmd.bufferId = id;
- cmd.writeFlushInfoLength = writeFlushInfoLength;
- cmd.writeFlushInfo = nullptr;
+ cmd.writeDataUpdateInfoLength = writeDataUpdateInfoLength;
+ cmd.writeDataUpdateInfo = nullptr;
+ cmd.offset = mMapOffset;
+ cmd.size = mMapSize;
client->SerializeCommand(
- cmd, writeFlushInfoLength, [&](SerializeBuffer* serializeBuffer) {
+ cmd, writeDataUpdateInfoLength, [&](SerializeBuffer* serializeBuffer) {
char* writeHandleBuffer;
- WIRE_TRY(serializeBuffer->NextN(writeFlushInfoLength, &writeHandleBuffer));
+ WIRE_TRY(serializeBuffer->NextN(writeDataUpdateInfoLength, &writeHandleBuffer));
// Serialize flush metadata into the space after the command.
// This closes the handle for writing.
- mWriteHandle->SerializeFlush(writeHandleBuffer);
+ mWriteHandle->SerializeDataUpdate(writeHandleBuffer, cmd.offset, cmd.size);
+
return WireResult::Success;
});
+
+ // If mDestructWriteHandleOnUnmap is true, that means the write handle is merely
+ // for mappedAtCreation usage. It is destroyed on unmap after flush to server
+ // instead of at buffer destruction.
+ if (mMapState == MapState::MappedAtCreation && mDestructWriteHandleOnUnmap) {
+ mWriteHandle = nullptr;
+ if (mReadHandle) {
+ // If it's both mappedAtCreation and MapRead we need to reset
+ // mMappedData to readHandle's GetData(). This could be changed to
+ // merging read/write handle in future
+ mMappedData = const_cast<void*>(mReadHandle->GetData());
+ }
+ }
}
- FreeMappedData(false);
+ // Free map access tokens
+ mMapState = MapState::Unmapped;
+ mMapOffset = 0;
+ mMapSize = 0;
// Tag all mapping requests still in flight as unmapped before callback.
for (auto& it : mRequests) {
@@ -376,11 +364,13 @@
BufferUnmapCmd cmd;
cmd.self = ToAPI(this);
client->SerializeCommand(cmd);
+ // TODO(dawn:608): add tracking mapped status and change to return bool for returning unmap
+ // success/failure
}
void Buffer::Destroy() {
- // Remove the current mapping.
- FreeMappedData(true);
+ // Remove the current mapping and destroy Read/WriteHandles.
+ FreeMappedData();
// Tag all mapping requests still in flight as destroyed before callback.
for (auto& it : mRequests) {
@@ -395,11 +385,11 @@
}
bool Buffer::IsMappedForReading() const {
- return mReadHandle != nullptr;
+ return mMapState == MapState::MappedForRead;
}
bool Buffer::IsMappedForWriting() const {
- return mWriteHandle != nullptr;
+ return mMapState == MapState::MappedForWrite || mMapState == MapState::MappedAtCreation;
}
bool Buffer::CheckGetMappedRangeOffsetSize(size_t offset, size_t size) const {
@@ -415,20 +405,20 @@
return offsetInMappedRange <= mMapSize - size;
}
- void Buffer::FreeMappedData(bool destruction) {
+ void Buffer::FreeMappedData() {
#if defined(DAWN_ENABLE_ASSERTS)
// When in "debug" mode, 0xCA-out the mapped data when we free it so that in we can detect
// use-after-free of the mapped data. This is particularly useful for WebGPU test about the
// interaction of mapping and GC.
- if (mMappedData && destruction) {
- memset(mMappedData, 0xCA, mMapSize);
+ if (mMappedData) {
+ memset(static_cast<uint8_t*>(mMappedData) + mMapOffset, 0xCA, mMapSize);
}
#endif // defined(DAWN_ENABLE_ASSERTS)
mMapOffset = 0;
mMapSize = 0;
- mWriteHandle = nullptr;
mReadHandle = nullptr;
+ mWriteHandle = nullptr;
mMappedData = nullptr;
}
diff --git a/src/dawn_wire/client/Buffer.h b/src/dawn_wire/client/Buffer.h
index 50b9639..a7d3fab 100644
--- a/src/dawn_wire/client/Buffer.h
+++ b/src/dawn_wire/client/Buffer.h
@@ -35,10 +35,10 @@
~Buffer();
- bool OnMapAsyncCallback(uint32_t requestSerial,
+ bool OnMapAsyncCallback(uint64_t requestSerial,
uint32_t status,
- uint64_t readInitialDataInfoLength,
- const uint8_t* readInitialDataInfo);
+ uint64_t readDataUpdateInfoLength,
+ const uint8_t* readDataUpdateInfo);
void MapAsync(WGPUMapModeFlags mode,
size_t offset,
size_t size,
@@ -57,10 +57,19 @@
bool IsMappedForWriting() const;
bool CheckGetMappedRangeOffsetSize(size_t offset, size_t size) const;
- void FreeMappedData(bool destruction);
+ void FreeMappedData();
Device* mDevice;
+ enum class MapRequestType { None, Read, Write };
+
+ enum class MapState {
+ Unmapped,
+ MappedForRead,
+ MappedForWrite,
+ MappedAtCreation,
+ };
+
// We want to defer all the validation to the server, which means we could have multiple
// map request in flight at a single time and need to track them separately.
// On well-behaved applications, only one request should exist at a single time.
@@ -75,12 +84,10 @@
// from the server take precedence over the client-side status.
WGPUBufferMapAsyncStatus clientStatus = WGPUBufferMapAsyncStatus_Success;
- // TODO(enga): Use a tagged pointer to save space.
- std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = nullptr;
- std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
+ MapRequestType type = MapRequestType::None;
};
- std::map<uint32_t, MapRequestData> mRequests;
- uint32_t mRequestSerial = 0;
+ std::map<uint64_t, MapRequestData> mRequests;
+ uint64_t mRequestSerial = 0;
uint64_t mSize = 0;
// Only one mapped pointer can be active at a time because Unmap clears all the in-flight
@@ -88,6 +95,9 @@
// TODO(enga): Use a tagged pointer to save space.
std::unique_ptr<MemoryTransferService::ReadHandle> mReadHandle = nullptr;
std::unique_ptr<MemoryTransferService::WriteHandle> mWriteHandle = nullptr;
+ MapState mMapState = MapState::Unmapped;
+ bool mDestructWriteHandleOnUnmap = false;
+
void* mMappedData = nullptr;
size_t mMapOffset = 0;
size_t mMapSize = 0;
diff --git a/src/dawn_wire/client/ClientDoers.cpp b/src/dawn_wire/client/ClientDoers.cpp
index 2e864a4..e3e34bd 100644
--- a/src/dawn_wire/client/ClientDoers.cpp
+++ b/src/dawn_wire/client/ClientDoers.cpp
@@ -73,16 +73,16 @@
}
bool Client::DoBufferMapAsyncCallback(Buffer* buffer,
- uint32_t requestSerial,
+ uint64_t requestSerial,
uint32_t status,
- uint64_t readInitialDataInfoLength,
- const uint8_t* readInitialDataInfo) {
+ uint64_t readDataUpdateInfoLength,
+ const uint8_t* readDataUpdateInfo) {
// The buffer might have been deleted or recreated so this isn't an error.
if (buffer == nullptr) {
return true;
}
- return buffer->OnMapAsyncCallback(requestSerial, status, readInitialDataInfoLength,
- readInitialDataInfo);
+ return buffer->OnMapAsyncCallback(requestSerial, status, readDataUpdateInfoLength,
+ readDataUpdateInfo);
}
bool Client::DoQueueWorkDoneCallback(Queue* queue,
diff --git a/src/dawn_wire/client/ClientInlineMemoryTransferService.cpp b/src/dawn_wire/client/ClientInlineMemoryTransferService.cpp
index 80867da..9cb0eed 100644
--- a/src/dawn_wire/client/ClientInlineMemoryTransferService.cpp
+++ b/src/dawn_wire/client/ClientInlineMemoryTransferService.cpp
@@ -24,7 +24,8 @@
class InlineMemoryTransferService : public MemoryTransferService {
class ReadHandleImpl : public ReadHandle {
public:
- explicit ReadHandleImpl(size_t size) : mSize(size) {
+ explicit ReadHandleImpl(std::unique_ptr<uint8_t[]> stagingData, size_t size)
+ : mStagingData(std::move(stagingData)), mSize(size) {
}
~ReadHandleImpl() override = default;
@@ -36,36 +37,36 @@
void SerializeCreate(void*) override {
}
- bool DeserializeInitialData(const void* deserializePointer,
- size_t deserializeSize,
- const void** data,
- size_t* dataLength) override {
- if (deserializeSize != mSize || deserializePointer == nullptr) {
+ const void* GetData() override {
+ return mStagingData.get();
+ }
+
+ bool DeserializeDataUpdate(const void* deserializePointer,
+ size_t deserializeSize,
+ size_t offset,
+ size_t size) override {
+ if (deserializeSize != size || deserializePointer == nullptr) {
return false;
}
- mStagingData = std::unique_ptr<uint8_t[]>(AllocNoThrow<uint8_t>(mSize));
- if (!mStagingData) {
+ if (offset > mSize || size > mSize - offset) {
return false;
}
- memcpy(mStagingData.get(), deserializePointer, mSize);
- ASSERT(data != nullptr);
- ASSERT(dataLength != nullptr);
- *data = mStagingData.get();
- *dataLength = mSize;
-
+ void* start = static_cast<uint8_t*>(mStagingData.get()) + offset;
+ memcpy(start, deserializePointer, size);
return true;
}
private:
- size_t mSize;
std::unique_ptr<uint8_t[]> mStagingData;
+ size_t mSize;
};
class WriteHandleImpl : public WriteHandle {
public:
- explicit WriteHandleImpl(size_t size) : mSize(size) {
+ explicit WriteHandleImpl(std::unique_ptr<uint8_t[]> stagingData, size_t size)
+ : mStagingData(std::move(stagingData)), mSize(size) {
}
~WriteHandleImpl() override = default;
@@ -77,28 +78,27 @@
void SerializeCreate(void*) override {
}
- std::pair<void*, size_t> Open() override {
- mStagingData = std::unique_ptr<uint8_t[]>(AllocNoThrow<uint8_t>(mSize));
- if (!mStagingData) {
- return std::make_pair(nullptr, 0);
- }
- memset(mStagingData.get(), 0, mSize);
- return std::make_pair(mStagingData.get(), mSize);
+ void* GetData() override {
+ return mStagingData.get();
}
- size_t SerializeFlushSize() override {
- return mSize;
+ size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override {
+ ASSERT(offset <= mSize);
+ ASSERT(size <= mSize - offset);
+ return size;
}
- void SerializeFlush(void* serializePointer) override {
+ void SerializeDataUpdate(void* serializePointer, size_t offset, size_t size) override {
ASSERT(mStagingData != nullptr);
ASSERT(serializePointer != nullptr);
- memcpy(serializePointer, mStagingData.get(), mSize);
+ ASSERT(offset <= mSize);
+ ASSERT(size <= mSize - offset);
+ memcpy(serializePointer, static_cast<uint8_t*>(mStagingData.get()) + offset, size);
}
private:
- size_t mSize;
std::unique_ptr<uint8_t[]> mStagingData;
+ size_t mSize;
};
public:
@@ -107,11 +107,20 @@
~InlineMemoryTransferService() override = default;
ReadHandle* CreateReadHandle(size_t size) override {
- return new ReadHandleImpl(size);
+ auto stagingData = std::unique_ptr<uint8_t[]>(AllocNoThrow<uint8_t>(size));
+ if (stagingData) {
+ return new ReadHandleImpl(std::move(stagingData), size);
+ }
+ return nullptr;
}
WriteHandle* CreateWriteHandle(size_t size) override {
- return new WriteHandleImpl(size);
+ auto stagingData = std::unique_ptr<uint8_t[]>(AllocNoThrow<uint8_t>(size));
+ if (stagingData) {
+ memset(stagingData.get(), 0, size);
+ return new WriteHandleImpl(std::move(stagingData), size);
+ }
+ return nullptr;
}
};
diff --git a/src/dawn_wire/client/ClientMemoryTransferService_mock.cpp b/src/dawn_wire/client/ClientMemoryTransferService_mock.cpp
index dd8d62f..6a523f2 100644
--- a/src/dawn_wire/client/ClientMemoryTransferService_mock.cpp
+++ b/src/dawn_wire/client/ClientMemoryTransferService_mock.cpp
@@ -35,15 +35,19 @@
mService->OnReadHandleSerializeCreate(this, serializePointer);
}
- bool MockMemoryTransferService::MockReadHandle::DeserializeInitialData(
+ const void* MockMemoryTransferService::MockReadHandle::GetData() {
+ return mService->OnReadHandleGetData(this);
+ }
+
+ bool MockMemoryTransferService::MockReadHandle::DeserializeDataUpdate(
const void* deserializePointer,
size_t deserializeSize,
- const void** data,
- size_t* dataLength) {
+ size_t offset,
+ size_t size) {
ASSERT(deserializeSize % sizeof(uint32_t) == 0);
- return mService->OnReadHandleDeserializeInitialData(
- this, reinterpret_cast<const uint32_t*>(deserializePointer), deserializeSize, data,
- dataLength);
+ return mService->OnReadHandleDeserializeDataUpdate(
+ this, reinterpret_cast<const uint32_t*>(deserializePointer), deserializeSize, offset,
+ size);
}
MockMemoryTransferService::MockWriteHandle::MockWriteHandle(MockMemoryTransferService* service)
@@ -62,16 +66,19 @@
mService->OnWriteHandleSerializeCreate(this, serializePointer);
}
- std::pair<void*, size_t> MockMemoryTransferService::MockWriteHandle::Open() {
- return mService->OnWriteHandleOpen(this);
+ void* MockMemoryTransferService::MockWriteHandle::GetData() {
+ return mService->OnWriteHandleGetData(this);
}
- size_t MockMemoryTransferService::MockWriteHandle::SerializeFlushSize() {
- return mService->OnWriteHandleSerializeFlushSize(this);
+ size_t MockMemoryTransferService::MockWriteHandle::SizeOfSerializeDataUpdate(size_t offset,
+ size_t size) {
+ return mService->OnWriteHandleSizeOfSerializeDataUpdate(this, offset, size);
}
- void MockMemoryTransferService::MockWriteHandle::SerializeFlush(void* serializePointer) {
- mService->OnWriteHandleSerializeFlush(this, serializePointer);
+ void MockMemoryTransferService::MockWriteHandle::SerializeDataUpdate(void* serializePointer,
+ size_t offset,
+ size_t size) {
+ mService->OnWriteHandleSerializeDataUpdate(this, serializePointer, offset, size);
}
MockMemoryTransferService::MockMemoryTransferService() = default;
diff --git a/src/dawn_wire/client/ClientMemoryTransferService_mock.h b/src/dawn_wire/client/ClientMemoryTransferService_mock.h
index 2d5eba4..5f9eeb0 100644
--- a/src/dawn_wire/client/ClientMemoryTransferService_mock.h
+++ b/src/dawn_wire/client/ClientMemoryTransferService_mock.h
@@ -31,10 +31,11 @@
size_t SerializeCreateSize() override;
void SerializeCreate(void* serializePointer) override;
- bool DeserializeInitialData(const void* deserializePointer,
- size_t deserializeSize,
- const void** data,
- size_t* dataLength) override;
+ const void* GetData() override;
+ bool DeserializeDataUpdate(const void* deserializePointer,
+ size_t deserializeSize,
+ size_t offset,
+ size_t size) override;
private:
MockMemoryTransferService* mService;
@@ -47,9 +48,9 @@
size_t SerializeCreateSize() override;
void SerializeCreate(void* serializePointer) override;
- std::pair<void*, size_t> Open() override;
- size_t SerializeFlushSize() override;
- void SerializeFlush(void* serializePointer) override;
+ void* GetData() override;
+ size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override;
+ void SerializeDataUpdate(void* serializePointer, size_t offset, size_t size) override;
private:
MockMemoryTransferService* mService;
@@ -69,24 +70,27 @@
MOCK_METHOD(size_t, OnReadHandleSerializeCreateSize, (const ReadHandle*));
MOCK_METHOD(void, OnReadHandleSerializeCreate, (const ReadHandle*, void* serializePointer));
+ MOCK_METHOD((const void*), OnReadHandleGetData, (const ReadHandle*));
MOCK_METHOD(bool,
- OnReadHandleDeserializeInitialData,
+ OnReadHandleDeserializeDataUpdate,
(const ReadHandle*,
const uint32_t* deserializePointer,
size_t deserializeSize,
- const void** data,
- size_t* dataLength));
+ size_t offset,
+ size_t size));
MOCK_METHOD(void, OnReadHandleDestroy, (const ReadHandle*));
MOCK_METHOD(size_t, OnWriteHandleSerializeCreateSize, (const void* WriteHandle));
MOCK_METHOD(void,
OnWriteHandleSerializeCreate,
(const void* WriteHandle, void* serializePointer));
- MOCK_METHOD((std::pair<void*, size_t>), OnWriteHandleOpen, (const void* WriteHandle));
- MOCK_METHOD(size_t, OnWriteHandleSerializeFlushSize, (const void* WriteHandle));
- MOCK_METHOD(void,
- OnWriteHandleSerializeFlush,
- (const void* WriteHandle, void* serializePointer));
+ MOCK_METHOD((void*), OnWriteHandleGetData, (const void* WriteHandle));
+ MOCK_METHOD(size_t,
+ OnWriteHandleSizeOfSerializeDataUpdate,
+ (const void* WriteHandle, size_t offset, size_t size));
+ MOCK_METHOD(size_t,
+ OnWriteHandleSerializeDataUpdate,
+ (const void* WriteHandle, void* serializePointer, size_t offset, size_t size));
MOCK_METHOD(void, OnWriteHandleDestroy, (const void* WriteHandle));
};
diff --git a/src/dawn_wire/server/ObjectStorage.h b/src/dawn_wire/server/ObjectStorage.h
index d4aa83c..bc45b26 100644
--- a/src/dawn_wire/server/ObjectStorage.h
+++ b/src/dawn_wire/server/ObjectStorage.h
@@ -62,6 +62,9 @@
std::unique_ptr<MemoryTransferService::ReadHandle> readHandle;
std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle;
BufferMapWriteState mapWriteState = BufferMapWriteState::Unmapped;
+ WGPUBufferUsageFlags usage = WGPUBufferUsage_None;
+ // Indicate if writeHandle needs to be destroyed on unmap
+ bool mappedAtCreation = false;
};
// Pack the ObjectType and ObjectId as a single value for storage in
diff --git a/src/dawn_wire/server/Server.h b/src/dawn_wire/server/Server.h
index af16b88..7118135 100644
--- a/src/dawn_wire/server/Server.h
+++ b/src/dawn_wire/server/Server.h
@@ -111,13 +111,10 @@
ObjectHandle buffer;
WGPUBuffer bufferObj;
- uint32_t requestSerial;
+ uint64_t requestSerial;
uint64_t offset;
uint64_t size;
WGPUMapModeFlags mode;
- // TODO(enga): Use a tagged pointer to save space.
- std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = nullptr;
- std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
};
struct ErrorScopeUserdata : CallbackUserdata {
diff --git a/src/dawn_wire/server/ServerBuffer.cpp b/src/dawn_wire/server/ServerBuffer.cpp
index b9fda44..7005f38 100644
--- a/src/dawn_wire/server/ServerBuffer.cpp
+++ b/src/dawn_wire/server/ServerBuffer.cpp
@@ -25,9 +25,13 @@
auto* buffer = BufferObjects().Get(cmd.selfId);
DAWN_ASSERT(buffer != nullptr);
- // The buffer was unmapped. Clear the Read/WriteHandle.
- buffer->readHandle = nullptr;
- buffer->writeHandle = nullptr;
+ if (buffer->mappedAtCreation && !(buffer->usage & WGPUMapMode_Write)) {
+ // This indicates the writeHandle is for mappedAtCreation only. Destroy on unmap
+ // writeHandle could have possibly been deleted if buffer is already destroyed so we
+ // don't assert it's non-null
+ buffer->writeHandle = nullptr;
+ }
+
buffer->mapWriteState = BufferMapWriteState::Unmapped;
return true;
@@ -47,12 +51,10 @@
}
bool Server::DoBufferMapAsync(ObjectId bufferId,
- uint32_t requestSerial,
+ uint64_t requestSerial,
WGPUMapModeFlags mode,
uint64_t offset64,
- uint64_t size64,
- uint64_t handleCreateInfoLength,
- const uint8_t* handleCreateInfo) {
+ uint64_t size64) {
// These requests are just forwarded to the buffer, with userdata containing what the
// client will require in the return command.
@@ -66,13 +68,6 @@
return false;
}
- // The server only knows how to deal with write XOR read. Validate that.
- bool isReadMode = mode & WGPUMapMode_Read;
- bool isWriteMode = mode & WGPUMapMode_Write;
- if (!(isReadMode ^ isWriteMode)) {
- return false;
- }
-
std::unique_ptr<MapUserdata> userdata = MakeUserdata<MapUserdata>();
userdata->buffer = ObjectHandle{bufferId, buffer->generation};
userdata->bufferObj = buffer->handle;
@@ -80,8 +75,7 @@
userdata->mode = mode;
if (offset64 > std::numeric_limits<size_t>::max() ||
- size64 > std::numeric_limits<size_t>::max() ||
- handleCreateInfoLength > std::numeric_limits<size_t>::max()) {
+ size64 > std::numeric_limits<size_t>::max()) {
OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus_Error, userdata.get());
return true;
}
@@ -92,32 +86,6 @@
userdata->offset = offset;
userdata->size = size;
- // The handle will point to the mapped memory or staging memory for the mapping.
- // Store it on the map request.
- if (isWriteMode) {
- // Deserialize metadata produced from the client to create a companion server handle.
- MemoryTransferService::WriteHandle* writeHandle = nullptr;
- if (!mMemoryTransferService->DeserializeWriteHandle(
- handleCreateInfo, static_cast<size_t>(handleCreateInfoLength), &writeHandle)) {
- return false;
- }
- ASSERT(writeHandle != nullptr);
-
- userdata->writeHandle =
- std::unique_ptr<MemoryTransferService::WriteHandle>(writeHandle);
- } else {
- ASSERT(isReadMode);
- // Deserialize metadata produced from the client to create a companion server handle.
- MemoryTransferService::ReadHandle* readHandle = nullptr;
- if (!mMemoryTransferService->DeserializeReadHandle(
- handleCreateInfo, static_cast<size_t>(handleCreateInfoLength), &readHandle)) {
- return false;
- }
- ASSERT(readHandle != nullptr);
-
- userdata->readHandle = std::unique_ptr<MemoryTransferService::ReadHandle>(readHandle);
- }
-
mProcs.bufferMapAsync(
buffer->handle, mode, offset, size,
ForwardToServer<decltype(
@@ -130,8 +98,10 @@
bool Server::DoDeviceCreateBuffer(ObjectId deviceId,
const WGPUBufferDescriptor* descriptor,
ObjectHandle bufferResult,
- uint64_t handleCreateInfoLength,
- const uint8_t* handleCreateInfo) {
+ uint64_t readHandleCreateInfoLength,
+ const uint8_t* readHandleCreateInfo,
+ uint64_t writeHandleCreateInfoLength,
+ const uint8_t* writeHandleCreateInfo) {
auto* device = DeviceObjects().Get(deviceId);
if (device == nullptr) {
return false;
@@ -145,56 +115,84 @@
resultData->generation = bufferResult.generation;
resultData->handle = mProcs.deviceCreateBuffer(device->handle, descriptor);
resultData->deviceInfo = device->info.get();
+ resultData->usage = descriptor->usage;
+ resultData->mappedAtCreation = descriptor->mappedAtCreation;
if (!TrackDeviceChild(resultData->deviceInfo, ObjectType::Buffer, bufferResult.id)) {
return false;
}
- // If the buffer isn't mapped at creation, we are done.
- if (!descriptor->mappedAtCreation) {
- return handleCreateInfoLength == 0;
- }
+ // isReadMode and isWriteMode could be true at the same time if usage contains
+ // WGPUMapMode_Read and buffer is mappedAtCreation
+ bool isReadMode = descriptor->usage & WGPUMapMode_Read;
+ bool isWriteMode = descriptor->usage & WGPUMapMode_Write || descriptor->mappedAtCreation;
- // This is the size of data deserialized from the command stream to create the write handle,
- // which must be CPU-addressable.
- if (handleCreateInfoLength > std::numeric_limits<size_t>::max()) {
+ // This is the size of data deserialized from the command stream to create the read/write
+ // handle, which must be CPU-addressable.
+ if (readHandleCreateInfoLength > std::numeric_limits<size_t>::max() ||
+ writeHandleCreateInfoLength > std::numeric_limits<size_t>::max() ||
+ readHandleCreateInfoLength >
+ std::numeric_limits<size_t>::max() - writeHandleCreateInfoLength) {
return false;
}
- void* mapping = mProcs.bufferGetMappedRange(resultData->handle, 0, descriptor->size);
- if (mapping == nullptr) {
- // A zero mapping is used to indicate an allocation error of an error buffer. This is a
- // valid case and isn't fatal. Remember the buffer is an error so as to skip subsequent
- // mapping operations.
- resultData->mapWriteState = BufferMapWriteState::MapError;
- return true;
+ if (isWriteMode) {
+ MemoryTransferService::WriteHandle* writeHandle = nullptr;
+ // Deserialize metadata produced from the client to create a companion server handle.
+ if (!mMemoryTransferService->DeserializeWriteHandle(
+ writeHandleCreateInfo, static_cast<size_t>(writeHandleCreateInfoLength),
+ &writeHandle)) {
+ return false;
+ }
+ ASSERT(writeHandle != nullptr);
+ resultData->writeHandle.reset(writeHandle);
+ writeHandle->SetDataLength(descriptor->size);
+
+ if (descriptor->mappedAtCreation) {
+ void* mapping =
+ mProcs.bufferGetMappedRange(resultData->handle, 0, descriptor->size);
+ if (mapping == nullptr) {
+ // A zero mapping is used to indicate an allocation error of an error buffer.
+ // This is a valid case and isn't fatal. Remember the buffer is an error so as
+ // to skip subsequent mapping operations.
+ resultData->mapWriteState = BufferMapWriteState::MapError;
+ return true;
+ }
+ ASSERT(mapping != nullptr);
+ writeHandle->SetTarget(mapping);
+
+ resultData->mapWriteState = BufferMapWriteState::Mapped;
+ }
}
- // Deserialize metadata produced from the client to create a companion server handle.
- MemoryTransferService::WriteHandle* writeHandle = nullptr;
- if (!mMemoryTransferService->DeserializeWriteHandle(
- handleCreateInfo, static_cast<size_t>(handleCreateInfoLength), &writeHandle)) {
- return false;
+ if (isReadMode) {
+ MemoryTransferService::ReadHandle* readHandle = nullptr;
+ // Deserialize metadata produced from the client to create a companion server handle.
+ if (!mMemoryTransferService->DeserializeReadHandle(
+ readHandleCreateInfo, static_cast<size_t>(readHandleCreateInfoLength),
+ &readHandle)) {
+ return false;
+ }
+ ASSERT(readHandle != nullptr);
+
+ resultData->readHandle.reset(readHandle);
}
- // Set the target of the WriteHandle to the mapped GPU memory.
- ASSERT(writeHandle != nullptr);
- writeHandle->SetTarget(mapping, descriptor->size);
-
- resultData->mapWriteState = BufferMapWriteState::Mapped;
- resultData->writeHandle.reset(writeHandle);
-
return true;
}
bool Server::DoBufferUpdateMappedData(ObjectId bufferId,
- uint64_t writeFlushInfoLength,
- const uint8_t* writeFlushInfo) {
+ uint64_t writeDataUpdateInfoLength,
+ const uint8_t* writeDataUpdateInfo,
+ uint64_t offset,
+ uint64_t size) {
// The null object isn't valid as `self`
if (bufferId == 0) {
return false;
}
- if (writeFlushInfoLength > std::numeric_limits<size_t>::max()) {
+ if (writeDataUpdateInfoLength > std::numeric_limits<size_t>::max() ||
+ offset > std::numeric_limits<size_t>::max() ||
+ size > std::numeric_limits<size_t>::max()) {
return false;
}
@@ -220,8 +218,9 @@
// Deserialize the flush info and flush updated data from the handle into the target
// of the handle. The target is set via WriteHandle::SetTarget.
- return buffer->writeHandle->DeserializeFlush(writeFlushInfo,
- static_cast<size_t>(writeFlushInfoLength));
+ return buffer->writeHandle->DeserializeDataUpdate(
+ writeDataUpdateInfo, static_cast<size_t>(writeDataUpdateInfoLength),
+ static_cast<size_t>(offset), static_cast<size_t>(size));
}
void Server::OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus status, MapUserdata* data) {
@@ -238,39 +237,41 @@
cmd.buffer = data->buffer;
cmd.requestSerial = data->requestSerial;
cmd.status = status;
- cmd.readInitialDataInfoLength = 0;
- cmd.readInitialDataInfo = nullptr;
+ cmd.readDataUpdateInfoLength = 0;
+ cmd.readDataUpdateInfo = nullptr;
const void* readData = nullptr;
- if (isSuccess && isRead) {
- // Get the serialization size of the message to initialize ReadHandle data.
- readData = mProcs.bufferGetConstMappedRange(data->bufferObj, data->offset, data->size);
- cmd.readInitialDataInfoLength =
- data->readHandle->SerializeInitialDataSize(readData, data->size);
+ if (isSuccess) {
+ if (isRead) {
+ // Get the serialization size of the message to initialize ReadHandle data.
+ readData =
+ mProcs.bufferGetConstMappedRange(data->bufferObj, data->offset, data->size);
+ cmd.readDataUpdateInfoLength =
+ bufferData->readHandle->SizeOfSerializeDataUpdate(data->offset, data->size);
+ } else {
+ ASSERT(data->mode & WGPUMapMode_Write);
+ // The in-flight map request returned successfully.
+ bufferData->mapWriteState = BufferMapWriteState::Mapped;
+ // Set the target of the WriteHandle to the mapped buffer data.
+ // writeHandle Target always refers to the buffer base address.
+ // but we call getMappedRange exactly with the range of data that is potentially
+ // modified (i.e. we don't want getMappedRange(0, wholeBufferSize) if only a
+ // subset of the buffer is actually mapped) in case the implementation does some
+ // range tracking.
+ bufferData->writeHandle->SetTarget(
+ static_cast<uint8_t*>(
+ mProcs.bufferGetMappedRange(data->bufferObj, data->offset, data->size)) -
+ data->offset);
+ }
}
- SerializeCommand(cmd, cmd.readInitialDataInfoLength, [&](SerializeBuffer* serializeBuffer) {
- if (isSuccess) {
- if (isRead) {
- char* readHandleBuffer;
- WIRE_TRY(
- serializeBuffer->NextN(cmd.readInitialDataInfoLength, &readHandleBuffer));
-
- // Serialize the initialization message into the space after the command.
- data->readHandle->SerializeInitialData(readData, data->size, readHandleBuffer);
- // The in-flight map request returned successfully.
- // Move the ReadHandle so it is owned by the buffer.
- bufferData->readHandle = std::move(data->readHandle);
- } else {
- // The in-flight map request returned successfully.
- // Move the WriteHandle so it is owned by the buffer.
- bufferData->writeHandle = std::move(data->writeHandle);
- bufferData->mapWriteState = BufferMapWriteState::Mapped;
- // Set the target of the WriteHandle to the mapped buffer data.
- bufferData->writeHandle->SetTarget(
- mProcs.bufferGetMappedRange(data->bufferObj, data->offset, data->size),
- data->size);
- }
+ SerializeCommand(cmd, cmd.readDataUpdateInfoLength, [&](SerializeBuffer* serializeBuffer) {
+ if (isSuccess && isRead) {
+ char* readHandleBuffer;
+ WIRE_TRY(serializeBuffer->NextN(cmd.readDataUpdateInfoLength, &readHandleBuffer));
+ // The in-flight map request returned successfully.
+ bufferData->readHandle->SerializeDataUpdate(readData, data->offset, data->size,
+ readHandleBuffer);
}
return WireResult::Success;
});
diff --git a/src/dawn_wire/server/ServerInlineMemoryTransferService.cpp b/src/dawn_wire/server/ServerInlineMemoryTransferService.cpp
index 105dee4..71347d2 100644
--- a/src/dawn_wire/server/ServerInlineMemoryTransferService.cpp
+++ b/src/dawn_wire/server/ServerInlineMemoryTransferService.cpp
@@ -28,17 +28,18 @@
}
~ReadHandleImpl() override = default;
- size_t SerializeInitialDataSize(const void* data, size_t dataLength) override {
- return dataLength;
+ size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override {
+ return size;
}
- void SerializeInitialData(const void* data,
- size_t dataLength,
- void* serializePointer) override {
- if (dataLength > 0) {
+ void SerializeDataUpdate(const void* data,
+ size_t offset,
+ size_t size,
+ void* serializePointer) override {
+ if (size > 0) {
ASSERT(data != nullptr);
ASSERT(serializePointer != nullptr);
- memcpy(serializePointer, data, dataLength);
+ memcpy(serializePointer, data, size);
}
}
};
@@ -49,12 +50,18 @@
}
~WriteHandleImpl() override = default;
- bool DeserializeFlush(const void* deserializePointer, size_t deserializeSize) override {
- if (deserializeSize != mDataLength || mTargetData == nullptr ||
+ bool DeserializeDataUpdate(const void* deserializePointer,
+ size_t deserializeSize,
+ size_t offset,
+ size_t size) override {
+ if (deserializeSize != size || mTargetData == nullptr ||
deserializePointer == nullptr) {
return false;
}
- memcpy(mTargetData, deserializePointer, mDataLength);
+ if ((offset >= mDataLength && offset > 0) || size > mDataLength - offset) {
+ return false;
+ }
+ memcpy(static_cast<uint8_t*>(mTargetData) + offset, deserializePointer, size);
return true;
}
};
diff --git a/src/dawn_wire/server/ServerMemoryTransferService_mock.cpp b/src/dawn_wire/server/ServerMemoryTransferService_mock.cpp
index b8b1696..165c6d3 100644
--- a/src/dawn_wire/server/ServerMemoryTransferService_mock.cpp
+++ b/src/dawn_wire/server/ServerMemoryTransferService_mock.cpp
@@ -26,15 +26,16 @@
mService->OnReadHandleDestroy(this);
}
- size_t MockMemoryTransferService::MockReadHandle::SerializeInitialDataSize(const void* data,
- size_t dataLength) {
- return mService->OnReadHandleSerializeInitialDataSize(this, data, dataLength);
+ size_t MockMemoryTransferService::MockReadHandle::SizeOfSerializeDataUpdate(size_t offset,
+ size_t size) {
+ return mService->OnReadHandleSizeOfSerializeDataUpdate(this, offset, size);
}
- void MockMemoryTransferService::MockReadHandle::SerializeInitialData(const void* data,
- size_t dataLength,
- void* serializePointer) {
- mService->OnReadHandleSerializeInitialData(this, data, dataLength, serializePointer);
+ void MockMemoryTransferService::MockReadHandle::SerializeDataUpdate(const void* data,
+ size_t offset,
+ size_t size,
+ void* serializePointer) {
+ mService->OnReadHandleSerializeDataUpdate(this, data, offset, size, serializePointer);
}
MockMemoryTransferService::MockWriteHandle::MockWriteHandle(MockMemoryTransferService* service)
@@ -45,18 +46,21 @@
mService->OnWriteHandleDestroy(this);
}
- bool MockMemoryTransferService::MockWriteHandle::DeserializeFlush(
- const void* deserializePointer,
- size_t deserializeSize) {
- ASSERT(deserializeSize % sizeof(uint32_t) == 0);
- return mService->OnWriteHandleDeserializeFlush(
- this, reinterpret_cast<const uint32_t*>(deserializePointer), deserializeSize);
- }
-
const uint32_t* MockMemoryTransferService::MockWriteHandle::GetData() const {
return reinterpret_cast<const uint32_t*>(mTargetData);
}
+ bool MockMemoryTransferService::MockWriteHandle::DeserializeDataUpdate(
+ const void* deserializePointer,
+ size_t deserializeSize,
+ size_t offset,
+ size_t size) {
+ ASSERT(deserializeSize % sizeof(uint32_t) == 0);
+ return mService->OnWriteHandleDeserializeDataUpdate(
+ this, reinterpret_cast<const uint32_t*>(deserializePointer), deserializeSize, offset,
+ size);
+ }
+
MockMemoryTransferService::MockMemoryTransferService() = default;
MockMemoryTransferService::~MockMemoryTransferService() = default;
diff --git a/src/dawn_wire/server/ServerMemoryTransferService_mock.h b/src/dawn_wire/server/ServerMemoryTransferService_mock.h
index f85aa64..23cf9ed 100644
--- a/src/dawn_wire/server/ServerMemoryTransferService_mock.h
+++ b/src/dawn_wire/server/ServerMemoryTransferService_mock.h
@@ -29,10 +29,11 @@
MockReadHandle(MockMemoryTransferService* service);
~MockReadHandle() override;
- size_t SerializeInitialDataSize(const void* data, size_t dataLength) override;
- void SerializeInitialData(const void* data,
- size_t dataLength,
- void* serializePointer) override;
+ size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override;
+ void SerializeDataUpdate(const void* data,
+ size_t offset,
+ size_t size,
+ void* serializePointer) override;
private:
MockMemoryTransferService* mService;
@@ -43,7 +44,10 @@
MockWriteHandle(MockMemoryTransferService* service);
~MockWriteHandle() override;
- bool DeserializeFlush(const void* deserializePointer, size_t deserializeSize) override;
+ bool DeserializeDataUpdate(const void* deserializePointer,
+ size_t deserializeSize,
+ size_t offset,
+ size_t size) override;
const uint32_t* GetData() const;
@@ -78,21 +82,24 @@
WriteHandle** writeHandle));
MOCK_METHOD(size_t,
- OnReadHandleSerializeInitialDataSize,
- (const ReadHandle* readHandle, const void* data, size_t dataLength));
+ OnReadHandleSizeOfSerializeDataUpdate,
+ (const ReadHandle* readHandle, size_t offset, size_t size));
MOCK_METHOD(void,
- OnReadHandleSerializeInitialData,
+ OnReadHandleSerializeDataUpdate,
(const ReadHandle* readHandle,
const void* data,
- size_t dataLength,
+ size_t offset,
+ size_t size,
void* serializePointer));
MOCK_METHOD(void, OnReadHandleDestroy, (const ReadHandle* readHandle));
MOCK_METHOD(bool,
- OnWriteHandleDeserializeFlush,
+ OnWriteHandleDeserializeDataUpdate,
(const WriteHandle* writeHandle,
const uint32_t* deserializePointer,
- size_t deserializeSize));
+ size_t deserializeSize,
+ size_t offset,
+ size_t size));
MOCK_METHOD(void, OnWriteHandleDestroy, (const WriteHandle* writeHandle));
};
diff --git a/src/include/dawn_wire/WireClient.h b/src/include/dawn_wire/WireClient.h
index e633dc5..961e045 100644
--- a/src/include/dawn_wire/WireClient.h
+++ b/src/include/dawn_wire/WireClient.h
@@ -121,6 +121,7 @@
// deserialize the data update and apply
// it to the range (offset, offset + size) of allocation
// There could be nothing to be deserialized (if using shared memory)
+ // Needs to check potential offset/size OOB and overflow
// TODO(dawn:773): change to pure virtual after update on chromium side.
virtual bool DeserializeDataUpdate(const void* deserializePointer,
size_t deserializeSize,
diff --git a/src/include/dawn_wire/WireServer.h b/src/include/dawn_wire/WireServer.h
index 1e03880..bb3f7c2 100644
--- a/src/include/dawn_wire/WireServer.h
+++ b/src/include/dawn_wire/WireServer.h
@@ -132,8 +132,9 @@
// Set the target for writes from the client. DeserializeFlush should copy data
// into the target.
- // TODO(dawn:773): only set backing buffer pointer data
- void SetTarget(void* data, size_t dataLength);
+ void SetTarget(void* data);
+ // Set Staging data length for OOB check
+ void SetDataLength(size_t dataLength);
// TODO(dawn:773): remove after update on chromium side.
virtual bool DeserializeFlush(const void* deserializePointer,
@@ -143,6 +144,7 @@
// This function takes in the serialized result of
// client::MemoryTransferService::WriteHandle::SerializeDataUpdate.
+ // Needs to check potential offset/size OOB and overflow
virtual bool DeserializeDataUpdate(const void* deserializePointer,
size_t deserializeSize,
size_t offset,
@@ -152,7 +154,6 @@
protected:
void* mTargetData = nullptr;
- // TODO(dawn:773): only set backing buffer pointer data
size_t mDataLength = 0;
private:
diff --git a/src/tests/end2end/BufferTests.cpp b/src/tests/end2end/BufferTests.cpp
index 25b0fde..12ac228 100644
--- a/src/tests/end2end/BufferTests.cpp
+++ b/src/tests/end2end/BufferTests.cpp
@@ -113,6 +113,21 @@
buffer.Unmap();
}
+// Map read and test multiple get mapped range data
+TEST_P(BufferMappingTests, MapRead_MultipleMappedRange) {
+ wgpu::Buffer buffer = CreateMapReadBuffer(12);
+
+ uint32_t myData[] = {0x00010203, 0x04050607, 0x08090a0b};
+ queue.WriteBuffer(buffer, 0, &myData, 12);
+
+ MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, 12);
+ ASSERT_EQ(myData[0], *static_cast<const uint32_t*>(buffer.GetConstMappedRange(0)));
+ ASSERT_EQ(myData[1], *(static_cast<const uint32_t*>(buffer.GetConstMappedRange(0)) + 1));
+ ASSERT_EQ(myData[2], *(static_cast<const uint32_t*>(buffer.GetConstMappedRange(0)) + 2));
+ ASSERT_EQ(myData[2], *static_cast<const uint32_t*>(buffer.GetConstMappedRange(8)));
+ buffer.Unmap();
+}
+
// Test map-reading a large buffer.
TEST_P(BufferMappingTests, MapRead_Large) {
constexpr uint32_t kDataSize = 1000 * 1000;
@@ -257,6 +272,70 @@
EXPECT_BUFFER_U32_EQ(myData, buffer, 0);
}
+// Map write and unmap twice with different ranges and make sure the first write is preserved
+TEST_P(BufferMappingTests, MapWrite_TwicePreserve) {
+ wgpu::Buffer buffer = CreateMapWriteBuffer(12);
+
+ uint32_t data1 = 0x08090a0b;
+ size_t offset1 = 8;
+ MapAsyncAndWait(buffer, wgpu::MapMode::Write, offset1, sizeof(data1));
+ memcpy(buffer.GetMappedRange(offset1), &data1, sizeof(data1));
+ buffer.Unmap();
+
+ uint32_t data2 = 0x00010203;
+ size_t offset2 = 0;
+ MapAsyncAndWait(buffer, wgpu::MapMode::Write, offset2, sizeof(data2));
+ memcpy(buffer.GetMappedRange(offset2), &data2, sizeof(data2));
+ buffer.Unmap();
+
+ EXPECT_BUFFER_U32_EQ(data1, buffer, offset1);
+ EXPECT_BUFFER_U32_EQ(data2, buffer, offset2);
+}
+
+// Map write and unmap twice with overlapping ranges and make sure data is updated correctly
+TEST_P(BufferMappingTests, MapWrite_TwiceRangeOverlap) {
+ wgpu::Buffer buffer = CreateMapWriteBuffer(16);
+
+ uint32_t data1[] = {0x01234567, 0x89abcdef};
+ size_t offset1 = 8;
+ MapAsyncAndWait(buffer, wgpu::MapMode::Write, offset1, 8);
+ memcpy(buffer.GetMappedRange(offset1), data1, 8);
+ buffer.Unmap();
+
+ EXPECT_BUFFER_U32_EQ(0x00000000, buffer, 0);
+ EXPECT_BUFFER_U32_EQ(0x00000000, buffer, 4);
+ EXPECT_BUFFER_U32_EQ(0x01234567, buffer, 8);
+ EXPECT_BUFFER_U32_EQ(0x89abcdef, buffer, 12);
+
+ uint32_t data2[] = {0x01234567, 0x89abcdef, 0x55555555};
+ size_t offset2 = 0;
+ MapAsyncAndWait(buffer, wgpu::MapMode::Write, offset2, 12);
+ memcpy(buffer.GetMappedRange(offset2), data2, 12);
+ buffer.Unmap();
+
+ EXPECT_BUFFER_U32_EQ(0x01234567, buffer, 0);
+ EXPECT_BUFFER_U32_EQ(0x89abcdef, buffer, 4);
+ EXPECT_BUFFER_U32_EQ(0x55555555, buffer, 8);
+ EXPECT_BUFFER_U32_EQ(0x89abcdef, buffer, 12);
+}
+
+// Map write and test multiple mapped range data get updated correctly
+TEST_P(BufferMappingTests, MapWrite_MultipleMappedRange) {
+ wgpu::Buffer buffer = CreateMapWriteBuffer(12);
+
+ uint32_t data1 = 0x08090a0b;
+ size_t offset1 = 8;
+ uint32_t data2 = 0x00010203;
+ size_t offset2 = 0;
+ MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, 12);
+ memcpy(buffer.GetMappedRange(offset1), &data1, sizeof(data1));
+ memcpy(buffer.GetMappedRange(offset2), &data2, sizeof(data2));
+ buffer.Unmap();
+
+ EXPECT_BUFFER_U32_EQ(data1, buffer, offset1);
+ EXPECT_BUFFER_U32_EQ(data2, buffer, offset2);
+}
+
// Test mapping a large buffer.
TEST_P(BufferMappingTests, MapWrite_Large) {
constexpr uint32_t kDataSize = 1000 * 1000;
@@ -330,20 +409,26 @@
queue.WriteBuffer(buffer, 0, data, sizeof(data));
// Map the buffer but do not wait on the result yet.
- bool done = false;
+ bool done1 = false;
+ bool done2 = false;
buffer.MapAsync(
wgpu::MapMode::Read, 8, 4,
[](WGPUBufferMapAsyncStatus status, void* userdata) {
ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
*static_cast<bool*>(userdata) = true;
},
- &done);
+ &done1);
// Call MapAsync another time, it is an error because the buffer is already being mapped so
// mMapOffset is not updated.
- ASSERT_DEVICE_ERROR(buffer.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, nullptr));
+ ASSERT_DEVICE_ERROR(buffer.MapAsync(
+ wgpu::MapMode::Read, 0, 4,
+ [](WGPUBufferMapAsyncStatus status, void* userdata) {
+ *static_cast<bool*>(userdata) = true;
+ },
+ &done2));
- while (!done) {
+ while (!done1 || !done2) {
WaitABit();
}
diff --git a/src/tests/unittests/wire/WireBufferMappingTests.cpp b/src/tests/unittests/wire/WireBufferMappingTests.cpp
index eab0b5b..22d8109 100644
--- a/src/tests/unittests/wire/WireBufferMappingTests.cpp
+++ b/src/tests/unittests/wire/WireBufferMappingTests.cpp
@@ -47,17 +47,7 @@
WireTest::SetUp();
mockBufferMapCallback = std::make_unique<StrictMock<MockBufferMapCallback>>();
-
- WGPUBufferDescriptor descriptor = {};
- descriptor.size = kBufferSize;
-
apiBuffer = api.GetNewBuffer();
- buffer = wgpuDeviceCreateBuffer(device, &descriptor);
-
- EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _))
- .WillOnce(Return(apiBuffer))
- .RetiresOnSaturation();
- FlushClient();
}
void TearDown() override {
@@ -77,6 +67,19 @@
Mock::VerifyAndClearExpectations(&mockBufferMapCallback);
}
+ void SetupBuffer(WGPUBufferUsageFlags usage) {
+ WGPUBufferDescriptor descriptor = {};
+ descriptor.size = kBufferSize;
+ descriptor.usage = usage;
+
+ buffer = wgpuDeviceCreateBuffer(device, &descriptor);
+
+ EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _))
+ .WillOnce(Return(apiBuffer))
+ .RetiresOnSaturation();
+ FlushClient();
+ }
+
protected:
static constexpr uint64_t kBufferSize = sizeof(uint32_t);
// A successfully created buffer
@@ -85,9 +88,21 @@
};
// Tests specific to mapping for reading
+class WireBufferMappingReadTests : public WireBufferMappingTests {
+ public:
+ WireBufferMappingReadTests() {
+ }
+ ~WireBufferMappingReadTests() override = default;
+
+ void SetUp() override {
+ WireBufferMappingTests::SetUp();
+
+ SetupBuffer(WGPUBufferUsage_MapRead);
+ }
+};
// Check mapping for reading a succesfully created buffer
-TEST_F(WireBufferMappingTests, MappingForReadSuccessBuffer) {
+TEST_F(WireBufferMappingReadTests, MappingForReadSuccessBuffer) {
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
uint32_t bufferContent = 31337;
@@ -115,7 +130,7 @@
// Check that things work correctly when a validation error happens when mapping the buffer for
// reading
-TEST_F(WireBufferMappingTests, ErrorWhileMappingForRead) {
+TEST_F(WireBufferMappingReadTests, ErrorWhileMappingForRead) {
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
@@ -133,7 +148,7 @@
// Check that the map read callback is called with UNKNOWN when the buffer is destroyed before the
// request is finished
-TEST_F(WireBufferMappingTests, DestroyBeforeReadRequestEnd) {
+TEST_F(WireBufferMappingReadTests, DestroyBeforeReadRequestEnd) {
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
// Return success
@@ -158,7 +173,7 @@
// Check the map read callback is called with "UnmappedBeforeCallback" when the map request would
// have worked, but Unmap was called
-TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForRead) {
+TEST_F(WireBufferMappingReadTests, UnmapCalledTooEarlyForRead) {
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
uint32_t bufferContent = 31337;
@@ -186,7 +201,7 @@
// Check that even if Unmap() was called early client-side, we correctly surface server-side
// validation errors.
-TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForReadButServerSideError) {
+TEST_F(WireBufferMappingReadTests, UnmapCalledTooEarlyForReadButServerSideError) {
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
@@ -209,7 +224,7 @@
// Check the map read callback is called with "DestroyedBeforeCallback" when the map request would
// have worked, but Destroy was called
-TEST_F(WireBufferMappingTests, DestroyCalledTooEarlyForRead) {
+TEST_F(WireBufferMappingReadTests, DestroyCalledTooEarlyForRead) {
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
uint32_t bufferContent = 31337;
@@ -237,7 +252,7 @@
// Check that even if Destroy() was called early client-side, we correctly surface server-side
// validation errors.
-TEST_F(WireBufferMappingTests, DestroyCalledTooEarlyForReadButServerSideError) {
+TEST_F(WireBufferMappingReadTests, DestroyCalledTooEarlyForReadButServerSideError) {
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
@@ -258,8 +273,9 @@
FlushServer();
}
-// Check that an error map read callback gets nullptr while a buffer is already mapped
-TEST_F(WireBufferMappingTests, MappingForReadingErrorWhileAlreadyMappedGetsNullptr) {
+// Check that an error map read while a buffer is already mapped won't changed the result of get
+// mapped range
+TEST_F(WireBufferMappingReadTests, MappingForReadingErrorWhileAlreadyMappedUnchangeMapData) {
// Successful map
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
@@ -289,11 +305,12 @@
FlushServer();
- EXPECT_EQ(nullptr, wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize));
+ EXPECT_EQ(bufferContent,
+ *static_cast<const uint32_t*>(wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize)));
}
// Test that the MapReadCallback isn't fired twice when unmap() is called inside the callback
-TEST_F(WireBufferMappingTests, UnmapInsideMapReadCallback) {
+TEST_F(WireBufferMappingReadTests, UnmapInsideMapReadCallback) {
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
uint32_t bufferContent = 31337;
@@ -318,7 +335,7 @@
// Test that the MapReadCallback isn't fired twice the buffer external refcount reaches 0 in the
// callback
-TEST_F(WireBufferMappingTests, DestroyInsideMapReadCallback) {
+TEST_F(WireBufferMappingReadTests, DestroyInsideMapReadCallback) {
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
uint32_t bufferContent = 31337;
@@ -342,9 +359,21 @@
}
// Tests specific to mapping for writing
+class WireBufferMappingWriteTests : public WireBufferMappingTests {
+ public:
+ WireBufferMappingWriteTests() {
+ }
+ ~WireBufferMappingWriteTests() override = default;
+
+ void SetUp() override {
+ WireBufferMappingTests::SetUp();
+
+ SetupBuffer(WGPUBufferUsage_MapWrite);
+ }
+};
// Check mapping for writing a succesfully created buffer
-TEST_F(WireBufferMappingTests, MappingForWriteSuccessBuffer) {
+TEST_F(WireBufferMappingWriteTests, MappingForWriteSuccessBuffer) {
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
uint32_t serverBufferContent = 31337;
@@ -382,7 +411,7 @@
// Check that things work correctly when a validation error happens when mapping the buffer for
// writing
-TEST_F(WireBufferMappingTests, ErrorWhileMappingForWrite) {
+TEST_F(WireBufferMappingWriteTests, ErrorWhileMappingForWrite) {
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
@@ -400,7 +429,7 @@
// Check that the map write callback is called with "DestroyedBeforeCallback" when the buffer is
// destroyed before the request is finished
-TEST_F(WireBufferMappingTests, DestroyBeforeWriteRequestEnd) {
+TEST_F(WireBufferMappingWriteTests, DestroyBeforeWriteRequestEnd) {
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
// Return success
@@ -423,9 +452,9 @@
FlushServer();
}
-// Check the map read callback is called with "UnmappedBeforeCallback" when the map request would
+// Check the map write callback is called with "UnmappedBeforeCallback" when the map request would
// have worked, but Unmap was called
-TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForWrite) {
+TEST_F(WireBufferMappingWriteTests, UnmapCalledTooEarlyForWrite) {
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
uint32_t bufferContent = 31337;
@@ -447,8 +476,8 @@
FlushServer();
}
-// Check that an error map read callback gets nullptr while a buffer is already mapped
-TEST_F(WireBufferMappingTests, MappingForWritingErrorWhileAlreadyMappedGetsNullptr) {
+// Check that an error map write while a buffer is already mapped
+TEST_F(WireBufferMappingWriteTests, MappingForWritingErrorWhileAlreadyMapped) {
// Successful map
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
@@ -478,11 +507,12 @@
FlushServer();
- EXPECT_EQ(nullptr, wgpuBufferGetMappedRange(buffer, 0, kBufferSize));
+ EXPECT_NE(nullptr,
+ static_cast<const uint32_t*>(wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize)));
}
// Test that the MapWriteCallback isn't fired twice when unmap() is called inside the callback
-TEST_F(WireBufferMappingTests, UnmapInsideMapWriteCallback) {
+TEST_F(WireBufferMappingWriteTests, UnmapInsideMapWriteCallback) {
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
uint32_t bufferContent = 31337;
@@ -507,7 +537,7 @@
// Test that the MapWriteCallback isn't fired twice the buffer external refcount reaches 0 in the
// callback
-TEST_F(WireBufferMappingTests, DestroyInsideMapWriteCallback) {
+TEST_F(WireBufferMappingWriteTests, DestroyInsideMapWriteCallback) {
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
uint32_t bufferContent = 31337;
@@ -578,6 +608,7 @@
TEST_F(WireBufferMappingTests, MappedAtCreationThenMapSuccess) {
WGPUBufferDescriptor descriptor = {};
descriptor.size = 4;
+ descriptor.usage = WGPUMapMode_Write;
descriptor.mappedAtCreation = true;
WGPUBuffer apiBuffer = api.GetNewBuffer();
@@ -639,7 +670,8 @@
FlushServer();
- EXPECT_EQ(nullptr, wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize));
+ EXPECT_NE(nullptr,
+ static_cast<const uint32_t*>(wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize)));
wgpuBufferUnmap(buffer);
EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
@@ -694,6 +726,7 @@
// Test that registering a callback then wire disconnect calls the callback with
// DeviceLost.
TEST_F(WireBufferMappingTests, MapThenDisconnect) {
+ SetupBuffer(WGPUMapMode_Write);
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, this);
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
@@ -711,6 +744,8 @@
// Test that registering a callback after wire disconnect calls the callback with
// DeviceLost.
TEST_F(WireBufferMappingTests, MapAfterDisconnect) {
+ SetupBuffer(WGPUMapMode_Read);
+
GetWireClient()->Disconnect();
EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, this)).Times(1);
diff --git a/src/tests/unittests/wire/WireDestroyObjectTests.cpp b/src/tests/unittests/wire/WireDestroyObjectTests.cpp
index f613051..beb2e03 100644
--- a/src/tests/unittests/wire/WireDestroyObjectTests.cpp
+++ b/src/tests/unittests/wire/WireDestroyObjectTests.cpp
@@ -56,58 +56,3 @@
wgpuCommandEncoderFinish(encoder, nullptr);
FlushClient(false);
}
-
-// Test that calling a function that would generate an InjectError doesn't crash after
-// the device is destroyed.
-TEST_F(WireDestroyObjectTests, ImplicitInjectErrorAfterDestroyDevice) {
- WGPUBufferDescriptor bufferDesc = {};
- bufferDesc.size = 4;
- WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &bufferDesc);
-
- WGPUBuffer apiBuffer = api.GetNewBuffer();
- EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer));
-
- FlushClient();
-
- {
- // Control case: MapAsync errors on invalid WGPUMapMode.
- MockCallback<WGPUBufferMapCallback> mockBufferMapCallback;
-
- EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, _));
- EXPECT_CALL(mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, this));
- wgpuBufferMapAsync(buffer, WGPUMapMode(0), 0, 4, mockBufferMapCallback.Callback(),
- mockBufferMapCallback.MakeUserdata(this));
-
- FlushClient();
- }
-
- {
- // Now, release the device. InjectError shouldn't happen.
- wgpuDeviceRelease(device);
- MockCallback<WGPUBufferMapCallback> mockBufferMapCallback;
-
- EXPECT_CALL(mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, this + 1));
- wgpuBufferMapAsync(buffer, WGPUMapMode(0), 0, 4, mockBufferMapCallback.Callback(),
- mockBufferMapCallback.MakeUserdata(this + 1));
-
- Sequence s1, s2;
- // The device and child objects alre also released.
- EXPECT_CALL(api, BufferRelease(apiBuffer)).InSequence(s1);
- EXPECT_CALL(api, QueueRelease(apiQueue)).InSequence(s2);
- EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr))
- .Times(1)
- .InSequence(s1, s2);
- EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr))
- .Times(1)
- .InSequence(s1, s2);
- EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, nullptr, nullptr))
- .Times(1)
- .InSequence(s1, s2);
- EXPECT_CALL(api, DeviceRelease(apiDevice)).InSequence(s1, s2);
-
- FlushClient();
-
- // Signal that we already released and cleared callbacks for |apiDevice|
- DefaultApiDeviceWasReleased();
- }
-}
diff --git a/src/tests/unittests/wire/WireMemoryTransferServiceTests.cpp b/src/tests/unittests/wire/WireMemoryTransferServiceTests.cpp
index d372f79..756dcc0 100644
--- a/src/tests/unittests/wire/WireMemoryTransferServiceTests.cpp
+++ b/src/tests/unittests/wire/WireMemoryTransferServiceTests.cpp
@@ -74,8 +74,8 @@
mMappedBufferContent = 0;
mUpdatedBufferContent++;
mSerializeCreateInfo++;
- mSerializeInitialDataInfo++;
- mSerializeFlushInfo++;
+ mReadHandleSerializeDataInfo++;
+ mWriteHandleSerializeDataInfo++;
}
void TearDown() override {
@@ -103,9 +103,10 @@
using ClientWriteHandle = client::MockMemoryTransferService::MockWriteHandle;
using ServerWriteHandle = server::MockMemoryTransferService::MockWriteHandle;
- std::pair<WGPUBuffer, WGPUBuffer> CreateBuffer() {
+ std::pair<WGPUBuffer, WGPUBuffer> CreateBuffer(WGPUBufferUsage usage = WGPUBufferUsage_None) {
WGPUBufferDescriptor descriptor = {};
descriptor.size = kBufferSize;
+ descriptor.usage = usage;
WGPUBuffer apiBuffer = api.GetNewBuffer();
WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &descriptor);
@@ -117,10 +118,12 @@
return std::make_pair(apiBuffer, buffer);
}
- std::pair<WGPUBuffer, WGPUBuffer> CreateBufferMapped() {
+ std::pair<WGPUBuffer, WGPUBuffer> CreateBufferMapped(
+ WGPUBufferUsage usage = WGPUBufferUsage_None) {
WGPUBufferDescriptor descriptor = {};
descriptor.size = sizeof(mBufferContent);
descriptor.mappedAtCreation = true;
+ descriptor.usage = usage;
WGPUBuffer apiBuffer = api.GetNewBuffer();
@@ -180,42 +183,46 @@
.WillOnce(InvokeWithoutArgs([&]() { return false; }));
}
- void ExpectServerReadHandleInitialize(ServerReadHandle* handle) {
- EXPECT_CALL(serverMemoryTransferService, OnReadHandleSerializeInitialDataSize(handle, _, _))
- .WillOnce(InvokeWithoutArgs([&]() { return sizeof(mSerializeInitialDataInfo); }));
- EXPECT_CALL(serverMemoryTransferService, OnReadHandleSerializeInitialData(handle, _, _, _))
- .WillOnce(WithArg<3>([&](void* serializePointer) {
- memcpy(serializePointer, &mSerializeInitialDataInfo,
- sizeof(mSerializeInitialDataInfo));
- return sizeof(mSerializeInitialDataInfo);
+ void ExpectServerReadHandleSerializeDataUpdate(ServerReadHandle* handle) {
+ EXPECT_CALL(serverMemoryTransferService,
+ OnReadHandleSizeOfSerializeDataUpdate(handle, _, _))
+ .WillOnce(InvokeWithoutArgs([&]() { return sizeof(mReadHandleSerializeDataInfo); }));
+ EXPECT_CALL(serverMemoryTransferService,
+ OnReadHandleSerializeDataUpdate(handle, _, _, _, _))
+ .WillOnce(WithArg<4>([&](void* serializePointer) {
+ memcpy(serializePointer, &mReadHandleSerializeDataInfo,
+ sizeof(mReadHandleSerializeDataInfo));
+ return sizeof(mReadHandleSerializeDataInfo);
}));
}
- void ExpectClientReadHandleDeserializeInitialize(ClientReadHandle* handle,
+ void ExpectClientReadHandleDeserializeDataUpdate(ClientReadHandle* handle,
uint32_t* mappedData) {
- EXPECT_CALL(clientMemoryTransferService, OnReadHandleDeserializeInitialData(
- handle, Pointee(Eq(mSerializeInitialDataInfo)),
- sizeof(mSerializeInitialDataInfo), _, _))
- .WillOnce(WithArgs<3, 4>([=](const void** data, size_t* dataLength) {
- *data = mappedData;
- *dataLength = sizeof(*mappedData);
- return true;
- }));
+ EXPECT_CALL(
+ clientMemoryTransferService,
+ OnReadHandleDeserializeDataUpdate(handle, Pointee(Eq(mReadHandleSerializeDataInfo)),
+ sizeof(mReadHandleSerializeDataInfo), _, _))
+ .WillOnce(Return(true));
}
- void MockClientReadHandleDeserializeInitializeFailure(ClientReadHandle* handle) {
- EXPECT_CALL(clientMemoryTransferService, OnReadHandleDeserializeInitialData(
- handle, Pointee(Eq(mSerializeInitialDataInfo)),
- sizeof(mSerializeInitialDataInfo), _, _))
- .WillOnce(InvokeWithoutArgs([&]() { return false; }));
+ void MockClientReadHandleDeserializeDataUpdateFailure(ClientReadHandle* handle) {
+ EXPECT_CALL(
+ clientMemoryTransferService,
+ OnReadHandleDeserializeDataUpdate(handle, Pointee(Eq(mReadHandleSerializeDataInfo)),
+ sizeof(mReadHandleSerializeDataInfo), _, _))
+ .WillOnce(Return(false));
}
- ClientWriteHandle* ExpectWriteHandleCreation() {
+ ClientWriteHandle* ExpectWriteHandleCreation(bool mappedAtCreation) {
// Create the handle first so we can use it in later expectations.
ClientWriteHandle* handle = clientMemoryTransferService.NewWriteHandle();
EXPECT_CALL(clientMemoryTransferService, OnCreateWriteHandle(sizeof(mBufferContent)))
.WillOnce(InvokeWithoutArgs([=]() { return handle; }));
+ if (mappedAtCreation) {
+ EXPECT_CALL(clientMemoryTransferService, OnWriteHandleGetData(handle))
+ .WillOnce(Return(&mBufferContent));
+ }
return handle;
}
@@ -254,46 +261,36 @@
EXPECT_CALL(serverMemoryTransferService,
OnDeserializeWriteHandle(Pointee(Eq(mSerializeCreateInfo)),
sizeof(mSerializeCreateInfo), _))
- .WillOnce(InvokeWithoutArgs([&]() { return false; }));
+ .WillOnce(Return(false));
}
- void ExpectClientWriteHandleOpen(ClientWriteHandle* handle, uint32_t* mappedData) {
- EXPECT_CALL(clientMemoryTransferService, OnWriteHandleOpen(handle))
- .WillOnce(InvokeWithoutArgs(
- [=]() { return std::make_pair(mappedData, sizeof(*mappedData)); }));
- }
-
- void MockClientWriteHandleOpenFailure(ClientWriteHandle* handle) {
- EXPECT_CALL(clientMemoryTransferService, OnWriteHandleOpen(handle))
- .WillOnce(InvokeWithoutArgs([&]() { return std::make_pair(nullptr, 0); }));
- }
-
- void ExpectClientWriteHandleSerializeFlush(ClientWriteHandle* handle) {
- EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeFlushSize(handle))
- .WillOnce(InvokeWithoutArgs([&]() { return sizeof(mSerializeFlushInfo); }));
- EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeFlush(handle, _))
+ void ExpectClientWriteHandleSerializeDataUpdate(ClientWriteHandle* handle) {
+ EXPECT_CALL(clientMemoryTransferService,
+ OnWriteHandleSizeOfSerializeDataUpdate(handle, _, _))
+ .WillOnce(InvokeWithoutArgs([&]() { return sizeof(mWriteHandleSerializeDataInfo); }));
+ EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeDataUpdate(handle, _, _, _))
.WillOnce(WithArg<1>([&](void* serializePointer) {
- memcpy(serializePointer, &mSerializeFlushInfo, sizeof(mSerializeFlushInfo));
- return sizeof(mSerializeFlushInfo);
+ memcpy(serializePointer, &mWriteHandleSerializeDataInfo,
+ sizeof(mWriteHandleSerializeDataInfo));
+ return sizeof(mWriteHandleSerializeDataInfo);
}));
}
- void ExpectServerWriteHandleDeserializeFlush(ServerWriteHandle* handle, uint32_t expectedData) {
- EXPECT_CALL(serverMemoryTransferService,
- OnWriteHandleDeserializeFlush(handle, Pointee(Eq(mSerializeFlushInfo)),
- sizeof(mSerializeFlushInfo)))
- .WillOnce(InvokeWithoutArgs([=]() {
- // The handle data should be updated.
- EXPECT_EQ(*handle->GetData(), expectedData);
- return true;
- }));
+ void ExpectServerWriteHandleDeserializeDataUpdate(ServerWriteHandle* handle,
+ uint32_t expectedData) {
+ EXPECT_CALL(
+ serverMemoryTransferService,
+ OnWriteHandleDeserializeDataUpdate(handle, Pointee(Eq(mWriteHandleSerializeDataInfo)),
+ sizeof(mWriteHandleSerializeDataInfo), _, _))
+ .WillOnce(Return(true));
}
- void MockServerWriteHandleDeserializeFlushFailure(ServerWriteHandle* handle) {
- EXPECT_CALL(serverMemoryTransferService,
- OnWriteHandleDeserializeFlush(handle, Pointee(Eq(mSerializeFlushInfo)),
- sizeof(mSerializeFlushInfo)))
- .WillOnce(InvokeWithoutArgs([&]() { return false; }));
+ void MockServerWriteHandleDeserializeDataUpdateFailure(ServerWriteHandle* handle) {
+ EXPECT_CALL(
+ serverMemoryTransferService,
+ OnWriteHandleDeserializeDataUpdate(handle, Pointee(Eq(mWriteHandleSerializeDataInfo)),
+ sizeof(mWriteHandleSerializeDataInfo), _, _))
+ .WillOnce(Return(false));
}
// Arbitrary values used within tests to check if serialized data is correctly passed
@@ -301,8 +298,8 @@
// test expectations will check that serialized values are passed to the respective
// deserialization function.
static uint32_t mSerializeCreateInfo;
- static uint32_t mSerializeInitialDataInfo;
- static uint32_t mSerializeFlushInfo;
+ static uint32_t mReadHandleSerializeDataInfo;
+ static uint32_t mWriteHandleSerializeDataInfo;
// Represents the buffer contents for the test.
static uint32_t mBufferContent;
@@ -324,32 +321,38 @@
uint32_t WireMemoryTransferServiceTests::mBufferContent = 1337;
uint32_t WireMemoryTransferServiceTests::mUpdatedBufferContent = 2349;
uint32_t WireMemoryTransferServiceTests::mSerializeCreateInfo = 4242;
-uint32_t WireMemoryTransferServiceTests::mSerializeInitialDataInfo = 1394;
-uint32_t WireMemoryTransferServiceTests::mSerializeFlushInfo = 1235;
+uint32_t WireMemoryTransferServiceTests::mReadHandleSerializeDataInfo = 1394;
+uint32_t WireMemoryTransferServiceTests::mWriteHandleSerializeDataInfo = 1235;
// Test successful mapping for reading.
TEST_F(WireMemoryTransferServiceTests, BufferMapReadSuccess) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
- std::tie(apiBuffer, buffer) = CreateBuffer();
- FlushClient();
- // The client should create and serialize a ReadHandle on mapAsync for reading.
+ // The client should create and serialize a ReadHandle on creation.
ClientReadHandle* clientHandle = ExpectReadHandleCreation();
ExpectReadHandleSerialization(clientHandle);
- wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+ std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
// The server should deserialize the read handle from the client and then serialize
// an initialization message.
ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
- ExpectServerReadHandleInitialize(serverHandle);
+
+ FlushClient();
+
+ wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+
+ // The handle serialize data update on mapAsync cmd
+ ExpectServerReadHandleSerializeDataUpdate(serverHandle);
// Mock a successful callback
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
.WillOnce(InvokeWithoutArgs([&]() {
api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success);
}));
+ EXPECT_CALL(clientMemoryTransferService, OnReadHandleGetData(clientHandle))
+ .WillOnce(Return(&mBufferContent));
EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize))
.WillOnce(Return(&mBufferContent));
@@ -358,53 +361,74 @@
// The client receives a successful callback.
EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
- // The client should receive the handle initialization message from the server.
- ExpectClientReadHandleDeserializeInitialize(clientHandle, &mBufferContent);
+ // The client should receive the handle data update message from the server.
+ ExpectClientReadHandleDeserializeDataUpdate(clientHandle, &mBufferContent);
FlushServer();
- // The handle is destroyed once the buffer is unmapped.
- EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
wgpuBufferUnmap(buffer);
-
- EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
FlushClient();
+
+ // The handle is destroyed once the buffer is destroyed.
+ EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
+ EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
+}
+
+// Test ReadHandle destroy behavior
+TEST_F(WireMemoryTransferServiceTests, BufferMapReadDestroy) {
+ WGPUBuffer buffer;
+ WGPUBuffer apiBuffer;
+
+ // The client should create and serialize a ReadHandle on creation.
+ ClientReadHandle* clientHandle = ExpectReadHandleCreation();
+ ExpectReadHandleSerialization(clientHandle);
+
+ std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
+
+ // The server should deserialize the read handle from the client and then serialize
+ // an initialization message.
+ ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
+
+ FlushClient();
+
+ // The handle is destroyed once the buffer is destroyed.
+ EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
+ wgpuBufferDestroy(buffer);
+ EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
+ EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1);
+
+ FlushClient();
}
// Test unsuccessful mapping for reading.
TEST_F(WireMemoryTransferServiceTests, BufferMapReadError) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
- std::tie(apiBuffer, buffer) = CreateBuffer();
- FlushClient();
- // The client should create and serialize a ReadHandle on mapAsync.
+ // The client should create and serialize a ReadHandle on creation.
ClientReadHandle* clientHandle = ExpectReadHandleCreation();
ExpectReadHandleSerialization(clientHandle);
- wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
-
// The server should deserialize the ReadHandle from the client.
ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
+ std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
+ FlushClient();
+
+ wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+
// Mock a failed callback.
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
.WillOnce(InvokeWithoutArgs(
[&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); }));
- // Since the mapping failed, the handle is immediately destroyed.
- EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
-
FlushClient();
// The client receives an error callback.
EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1);
- // The client receives the map failure and destroys the handle.
- EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
-
FlushServer();
wgpuBufferUnmap(buffer);
@@ -412,68 +436,64 @@
EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
FlushClient();
+
+ // The handle is destroyed once the buffer is destroyed.
+ EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
+ EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
}
// Test ReadHandle creation failure.
TEST_F(WireMemoryTransferServiceTests, BufferMapReadHandleCreationFailure) {
- WGPUBuffer buffer;
- WGPUBuffer apiBuffer;
- std::tie(apiBuffer, buffer) = CreateBuffer();
- FlushClient();
-
// Mock a ReadHandle creation failure
MockReadHandleCreationFailure();
- // Failed creation of a ReadHandle is a mapping failure and the client synchronously receives
- // an error callback.
- EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1);
+ WGPUBufferDescriptor descriptor = {};
+ descriptor.size = kBufferSize;
+ descriptor.usage = WGPUBufferUsage_MapRead;
- wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+ wgpuDeviceCreateBuffer(device, &descriptor);
}
// Test MapRead DeserializeReadHandle failure.
TEST_F(WireMemoryTransferServiceTests, BufferMapReadDeserializeReadHandleFailure) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
- std::tie(apiBuffer, buffer) = CreateBuffer();
- FlushClient();
// The client should create and serialize a ReadHandle on mapping for reading..
ClientReadHandle* clientHandle = ExpectReadHandleCreation();
ExpectReadHandleSerialization(clientHandle);
- wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+ std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
// Mock a Deserialization failure.
MockServerReadHandleDeserializeFailure();
FlushClient(false);
- // The server received a fatal failure and the client callback was never returned.
- // It is called when the wire is destructed.
- EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _))
- .Times(1);
-
+ // The handle is destroyed once the buffer is destroyed.
EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
}
-// Test read handle DeserializeInitialData failure.
-TEST_F(WireMemoryTransferServiceTests, BufferMapReadDeserializeInitialDataFailure) {
+// Test read handle DeserializeDataUpdate failure.
+TEST_F(WireMemoryTransferServiceTests, BufferMapReadDeserializeDataUpdateFailure) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
- std::tie(apiBuffer, buffer) = CreateBuffer();
- FlushClient();
// The client should create and serialize a ReadHandle on mapping for reading.
ClientReadHandle* clientHandle = ExpectReadHandleCreation();
ExpectReadHandleSerialization(clientHandle);
- wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
-
// The server should deserialize the read handle from the client and then serialize
// an initialization message.
ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
- ExpectServerReadHandleInitialize(serverHandle);
+
+ std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
+ FlushClient();
+
+ wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+
+ // The handle serialize data update on mapAsync cmd
+ ExpectServerReadHandleSerializeDataUpdate(serverHandle);
// Mock a successful callback
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
@@ -485,19 +505,18 @@
FlushClient();
- // The client should receive the handle initialization message from the server.
+ // The client should receive the handle data update message from the server.
// Mock a deserialization failure.
- MockClientReadHandleDeserializeInitializeFailure(clientHandle);
+ MockClientReadHandleDeserializeDataUpdateFailure(clientHandle);
// Failed deserialization is a fatal failure and the client synchronously receives a
// DEVICE_LOST callback.
EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, _)).Times(1);
- // The handle will be destroyed since deserializing failed.
- EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
-
FlushServer(false);
+ // The handle is destroyed once the buffer is destroyed.
+ EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
}
@@ -505,25 +524,30 @@
TEST_F(WireMemoryTransferServiceTests, BufferMapReadDestroyBeforeUnmap) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
- std::tie(apiBuffer, buffer) = CreateBuffer();
- FlushClient();
- // The client should create and serialize a ReadHandle on mapping for reading.
+ // The client should create and serialize a ReadHandle on mapping for reading..
ClientReadHandle* clientHandle = ExpectReadHandleCreation();
ExpectReadHandleSerialization(clientHandle);
- wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
-
// The server should deserialize the read handle from the client and then serialize
// an initialization message.
ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
- ExpectServerReadHandleInitialize(serverHandle);
+
+ std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
+ FlushClient();
+
+ wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+
+ // The handle serialize data update on mapAsync cmd
+ ExpectServerReadHandleSerializeDataUpdate(serverHandle);
// Mock a successful callback
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
.WillOnce(InvokeWithoutArgs([&]() {
api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success);
}));
+ EXPECT_CALL(clientMemoryTransferService, OnReadHandleGetData(clientHandle))
+ .WillOnce(Return(&mBufferContent));
EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize))
.WillOnce(Return(&mBufferContent));
@@ -532,8 +556,8 @@
// The client receives a successful callback.
EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
- // The client should receive the handle initialization message from the server.
- ExpectClientReadHandleDeserializeInitialize(clientHandle, &mBufferContent);
+ // The client should receive the handle data update message from the server.
+ ExpectClientReadHandleDeserializeDataUpdate(clientHandle, &mBufferContent);
FlushServer();
@@ -559,22 +583,26 @@
TEST_F(WireMemoryTransferServiceTests, BufferMapWriteSuccess) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
- std::tie(apiBuffer, buffer) = CreateBuffer();
- FlushClient();
- ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
+ ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
ExpectWriteHandleSerialization(clientHandle);
- wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+ std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
// The server should then deserialize the WriteHandle from the client.
ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
+ FlushClient();
+
+ wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+
// Mock a successful callback.
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
.WillOnce(InvokeWithoutArgs([&]() {
api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success);
}));
+ EXPECT_CALL(clientMemoryTransferService, OnWriteHandleGetData(clientHandle))
+ .WillOnce(Return(&mBufferContent));
EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
.WillOnce(Return(&mMappedBufferContent));
@@ -583,62 +611,81 @@
// The client receives a successful callback.
EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
- // Since the mapping succeeds, the client opens the WriteHandle.
- ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
-
FlushServer();
// The client writes to the handle contents.
mMappedBufferContent = mUpdatedBufferContent;
- // The client will then flush and destroy the handle on Unmap()
- ExpectClientWriteHandleSerializeFlush(clientHandle);
- EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
+ // The client will then serialize data update and destroy the handle on Unmap()
+ ExpectClientWriteHandleSerializeDataUpdate(clientHandle);
wgpuBufferUnmap(buffer);
- // The server deserializes the Flush message.
- ExpectServerWriteHandleDeserializeFlush(serverHandle, mUpdatedBufferContent);
+ // The server deserializes the data update message.
+ ExpectServerWriteHandleDeserializeDataUpdate(serverHandle, mUpdatedBufferContent);
- // After the handle is updated it can be destroyed.
- EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
FlushClient();
+
+ // The handle is destroyed once the buffer is destroyed.
+ EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
+ EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
+}
+
+// Test WriteHandle destroy behavior
+TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDestroy) {
+ WGPUBuffer buffer;
+ WGPUBuffer apiBuffer;
+
+ ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
+ ExpectWriteHandleSerialization(clientHandle);
+
+ std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
+
+ // The server should then deserialize the WriteHandle from the client.
+ ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
+
+ FlushClient();
+
+ // The handle is destroyed once the buffer is destroyed.
+ EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
+ wgpuBufferDestroy(buffer);
+ EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
+ EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1);
+
+ FlushClient();
}
// Test unsuccessful MapWrite.
TEST_F(WireMemoryTransferServiceTests, BufferMapWriteError) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
- std::tie(apiBuffer, buffer) = CreateBuffer();
- FlushClient();
- // The client should create and serialize a WriteHandle on mapping for writing.
- ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
+ // The client should create and serialize a WriteHandle on buffer creation with MapWrite usage.
+ ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
ExpectWriteHandleSerialization(clientHandle);
- wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+ std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
// The server should then deserialize the WriteHandle from the client.
ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
+ FlushClient();
+
+ wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+
// Mock an error callback.
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
.WillOnce(InvokeWithoutArgs(
[&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); }));
- // Since the mapping fails, the handle is immediately destroyed because it won't be written.
- EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
FlushClient();
// The client receives an error callback.
EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1);
- // Client receives the map failure and destroys the handle.
- EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
-
FlushServer();
wgpuBufferUnmap(buffer);
@@ -646,111 +693,68 @@
EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
FlushClient();
+
+ // The handle is destroyed once the buffer is destroyed.
+ EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
+ EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
}
// Test WriteHandle creation failure.
TEST_F(WireMemoryTransferServiceTests, BufferMapWriteHandleCreationFailure) {
- WGPUBuffer buffer;
- WGPUBuffer apiBuffer;
- std::tie(apiBuffer, buffer) = CreateBuffer();
- FlushClient();
-
// Mock a WriteHandle creation failure
MockWriteHandleCreationFailure();
- // Failed creation of a WriteHandle is a mapping failure and the client synchronously receives
- // an error callback.
- EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1);
+ WGPUBufferDescriptor descriptor = {};
+ descriptor.size = kBufferSize;
+ descriptor.usage = WGPUBufferUsage_MapWrite;
- wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+ wgpuDeviceCreateBuffer(device, &descriptor);
}
// Test MapWrite DeserializeWriteHandle failure.
TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDeserializeWriteHandleFailure) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
- std::tie(apiBuffer, buffer) = CreateBuffer();
- FlushClient();
- // The client should create and serialize a WriteHandle on mapping for writing.
- ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
+ // The client should create and serialize a WriteHandle on buffer creation with MapWrite usage.
+ ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
ExpectWriteHandleSerialization(clientHandle);
- wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+ std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
// Mock a deserialization failure.
MockServerWriteHandleDeserializeFailure();
FlushClient(false);
- // The server hit a fatal failure and never returned the callback. The client callback is
- // called when the wire is destructed.
- EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _))
- .Times(1);
-
+ // The handle is destroyed once the buffer is destroyed.
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
}
-// Test MapWrite handle Open failure.
-TEST_F(WireMemoryTransferServiceTests, BufferMapWriteHandleOpenFailure) {
+// Test MapWrite DeserializeDataUpdate failure.
+TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDeserializeDataUpdateFailure) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
- std::tie(apiBuffer, buffer) = CreateBuffer();
- FlushClient();
- ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
+ ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
ExpectWriteHandleSerialization(clientHandle);
- wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+ std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
// The server should then deserialize the WriteHandle from the client.
ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
+ FlushClient();
+
+ wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+
// Mock a successful callback.
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
.WillOnce(InvokeWithoutArgs([&]() {
api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success);
}));
- EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
- .WillOnce(Return(&mMappedBufferContent));
-
- FlushClient();
-
- // Since the mapping succeeds, the client opens the WriteHandle.
- // Mock a failure.
- MockClientWriteHandleOpenFailure(clientHandle);
-
- // Failing to open a handle is a fatal failure and the client receives a DEVICE_LOST callback.
- EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, _)).Times(1);
-
- // Since opening the handle fails, it gets destroyed immediately.
- EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
-
- FlushServer(false);
-
- EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
-}
-
-// Test MapWrite DeserializeFlush failure.
-TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDeserializeFlushFailure) {
- WGPUBuffer buffer;
- WGPUBuffer apiBuffer;
- std::tie(apiBuffer, buffer) = CreateBuffer();
- FlushClient();
-
- ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
- ExpectWriteHandleSerialization(clientHandle);
-
- wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
-
- // The server should then deserialize the WriteHandle from the client.
- ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
-
- // Mock a successful callback.
- EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
- .WillOnce(InvokeWithoutArgs([&]() {
- api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success);
- }));
+ EXPECT_CALL(clientMemoryTransferService, OnWriteHandleGetData(clientHandle))
+ .WillOnce(Return(&mBufferContent));
EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
.WillOnce(Return(&mMappedBufferContent));
@@ -759,25 +763,23 @@
// The client receives a success callback.
EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
- // Since the mapping succeeds, the client opens the WriteHandle.
- ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
-
FlushServer();
// The client writes to the handle contents.
mMappedBufferContent = mUpdatedBufferContent;
- // The client will then flush and destroy the handle on Unmap()
- ExpectClientWriteHandleSerializeFlush(clientHandle);
- EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
+ // The client will then serialize data update
+ ExpectClientWriteHandleSerializeDataUpdate(clientHandle);
wgpuBufferUnmap(buffer);
- // The server deserializes the Flush message. Mock a deserialization failure.
- MockServerWriteHandleDeserializeFlushFailure(serverHandle);
+ // The server deserializes the data update message. Mock a deserialization failure.
+ MockServerWriteHandleDeserializeDataUpdateFailure(serverHandle);
FlushClient(false);
+ // The handle is destroyed once the buffer is destroyed.
+ EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
}
@@ -785,22 +787,26 @@
TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDestroyBeforeUnmap) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
- std::tie(apiBuffer, buffer) = CreateBuffer();
- FlushClient();
- ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
+ ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
ExpectWriteHandleSerialization(clientHandle);
- wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+ std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
// The server should then deserialize the WriteHandle from the client.
ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
+ FlushClient();
+
+ wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+
// Mock a successful callback.
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
.WillOnce(InvokeWithoutArgs([&]() {
api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success);
}));
+ EXPECT_CALL(clientMemoryTransferService, OnWriteHandleGetData(clientHandle))
+ .WillOnce(Return(&mBufferContent));
EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
.WillOnce(Return(&mMappedBufferContent));
@@ -809,9 +815,6 @@
// The client receives a successful callback.
EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
- // Since the mapping succeeds, the client opens the WriteHandle.
- ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
-
FlushServer();
// The client writes to the handle contents.
@@ -820,7 +823,9 @@
// THIS IS THE TEST: destroy the buffer before unmapping and check it destroyed the mapping
// immediately, both in the client and server side.
{
+ // The handle is destroyed once the buffer is destroyed.
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
+
wgpuBufferDestroy(buffer);
EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
@@ -838,11 +843,7 @@
// Test successful buffer creation with mappedAtCreation = true.
TEST_F(WireMemoryTransferServiceTests, MappedAtCreationSuccess) {
// 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);
-
+ ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true);
ExpectWriteHandleSerialization(clientHandle);
// The server should then deserialize the WriteHandle from the client.
@@ -856,14 +857,15 @@
// Update the mapped contents.
mMappedBufferContent = mUpdatedBufferContent;
- // When the client Unmaps the buffer, it will flush writes to the handle and destroy it.
- ExpectClientWriteHandleSerializeFlush(clientHandle);
+ // When the client Unmaps the buffer, it will serialize data update writes to the handle and
+ // destroy it.
+ ExpectClientWriteHandleSerializeDataUpdate(clientHandle);
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
wgpuBufferUnmap(buffer);
- // The server deserializes the Flush message.
- ExpectServerWriteHandleDeserializeFlush(serverHandle, mUpdatedBufferContent);
+ // The server deserializes the data update message.
+ ExpectServerWriteHandleDeserializeDataUpdate(serverHandle, mUpdatedBufferContent);
// After the handle is updated it can be destroyed.
EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
@@ -888,53 +890,32 @@
// Test buffer creation with mappedAtCreation DeserializeWriteHandle failure.
TEST_F(WireMemoryTransferServiceTests, MappedAtCreationDeserializeWriteHandleFailure) {
// 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);
-
+ ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true);
ExpectWriteHandleSerialization(clientHandle);
// The server should then deserialize the WriteHandle from the client.
MockServerWriteHandleDeserializeFailure();
- WGPUBuffer buffer;
- WGPUBuffer apiBuffer;
- std::tie(apiBuffer, buffer) = CreateBufferMapped();
+ WGPUBufferDescriptor descriptor = {};
+ descriptor.size = sizeof(mBufferContent);
+ descriptor.mappedAtCreation = true;
+
+ WGPUBuffer apiBuffer = api.GetNewBuffer();
+
+ wgpuDeviceCreateBuffer(device, &descriptor);
+
+ EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer));
+ // Now bufferGetMappedRange won't be called if deserialize writeHandle fails
+
FlushClient(false);
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
}
-// Test buffer creation with mappedAtCreation handle Open failure.
-TEST_F(WireMemoryTransferServiceTests, MappedAtCreationHandleOpenFailure) {
- // The client should create a WriteHandle on createBufferMapped.
- ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
-
- // Staging data is immediately available so the handle is Opened.
- // Mock a failure.
- MockClientWriteHandleOpenFailure(clientHandle);
-
- // Since synchronous opening of the handle failed, it is destroyed immediately.
- // Note: The handle is not serialized because sychronously opening it failed.
- EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
-
- WGPUBufferDescriptor descriptor = {};
- descriptor.size = sizeof(mBufferContent);
- descriptor.mappedAtCreation = true;
-
- WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &descriptor);
- EXPECT_EQ(nullptr, wgpuBufferGetMappedRange(buffer, 0, sizeof(mBufferContent)));
-}
-
-// Test buffer creation with mappedAtCreation = true DeserializeFlush failure.
-TEST_F(WireMemoryTransferServiceTests, MappedAtCreationDeserializeFlushFailure) {
+// Test buffer creation with mappedAtCreation = true DeserializeDataUpdate failure.
+TEST_F(WireMemoryTransferServiceTests, MappedAtCreationDeserializeDataUpdateFailure) {
// 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);
-
+ ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true);
ExpectWriteHandleSerialization(clientHandle);
// The server should then deserialize the WriteHandle from the client.
@@ -948,28 +929,27 @@
// Update the mapped contents.
mMappedBufferContent = mUpdatedBufferContent;
- // When the client Unmaps the buffer, it will flush writes to the handle and destroy it.
- ExpectClientWriteHandleSerializeFlush(clientHandle);
+ // When the client Unmaps the buffer, it will serialize data update writes to the handle and
+ // destroy it.
+ ExpectClientWriteHandleSerializeDataUpdate(clientHandle);
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
wgpuBufferUnmap(buffer);
- // The server deserializes the Flush message. Mock a deserialization failure.
- MockServerWriteHandleDeserializeFlushFailure(serverHandle);
+ // The server deserializes the data update message. Mock a deserialization failure.
+ MockServerWriteHandleDeserializeDataUpdateFailure(serverHandle);
FlushClient(false);
+ // Failed BufferUpdateMappedData cmd will early return so BufferUnmap is not processed.
+ // The server side writeHandle is destructed at buffer destruction.
EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
}
// Test mappedAtCreation=true destroying the buffer before unmapping on the client side.
TEST_F(WireMemoryTransferServiceTests, MappedAtCreationDestroyBeforeUnmap) {
// 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);
-
+ ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true);
ExpectWriteHandleSerialization(clientHandle);
// The server should then deserialize the WriteHandle from the client.
@@ -1000,3 +980,77 @@
FlushClient();
}
}
+
+// Test a buffer with mappedAtCreation and MapRead usage destroy WriteHandle on unmap and switch
+// data pointer to ReadHandle
+TEST_F(WireMemoryTransferServiceTests, MappedAtCreationAndMapReadSuccess) {
+ // The client should create and serialize a ReadHandle and a WriteHandle on createBufferMapped.
+ ClientReadHandle* clientReadHandle = ExpectReadHandleCreation();
+ ExpectReadHandleSerialization(clientReadHandle);
+ ClientWriteHandle* clientWriteHandle = ExpectWriteHandleCreation(true);
+ ExpectWriteHandleSerialization(clientWriteHandle);
+
+ // The server should then deserialize a ReadHandle and a WriteHandle from the client.
+ ServerReadHandle* serverReadHandle = ExpectServerReadHandleDeserialize();
+ ServerWriteHandle* serverWriteHandle = ExpectServerWriteHandleDeserialization();
+
+ WGPUBuffer buffer;
+ WGPUBuffer apiBuffer;
+ std::tie(apiBuffer, buffer) = CreateBufferMapped(WGPUBufferUsage_MapRead);
+ FlushClient();
+
+ // Update the mapped contents.
+ mMappedBufferContent = mUpdatedBufferContent;
+
+ // When the client Unmaps the buffer, it will serialize data update writes to the handle and
+ // destroy it.
+ ExpectClientWriteHandleSerializeDataUpdate(clientWriteHandle);
+ EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientWriteHandle)).Times(1);
+ EXPECT_CALL(clientMemoryTransferService, OnReadHandleGetData(clientReadHandle))
+ .WillOnce(Return(&mBufferContent));
+ wgpuBufferUnmap(buffer);
+
+ // The server deserializes the data update message.
+ ExpectServerWriteHandleDeserializeDataUpdate(serverWriteHandle, mUpdatedBufferContent);
+ EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
+ EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverWriteHandle)).Times(1);
+ FlushClient();
+
+ // The ReadHandle will be destoryed on buffer destroy.
+ EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientReadHandle)).Times(1);
+ EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverReadHandle)).Times(1);
+}
+
+// Test WriteHandle preserves after unmap for a buffer with mappedAtCreation and MapWrite usage
+TEST_F(WireMemoryTransferServiceTests, MappedAtCreationAndMapWriteSuccess) {
+ // The client should create and serialize a WriteHandle on createBufferMapped.
+ ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true);
+
+ ExpectWriteHandleSerialization(clientHandle);
+
+ // The server should then deserialize the WriteHandle from the client.
+ ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
+
+ WGPUBuffer buffer;
+ WGPUBuffer apiBuffer;
+ std::tie(apiBuffer, buffer) = CreateBufferMapped(WGPUBufferUsage_MapWrite);
+ FlushClient();
+
+ // Update the mapped contents.
+ mMappedBufferContent = mUpdatedBufferContent;
+
+ // When the client Unmaps the buffer, it will serialize data update writes to the handle.
+ ExpectClientWriteHandleSerializeDataUpdate(clientHandle);
+
+ wgpuBufferUnmap(buffer);
+
+ // The server deserializes the data update message.
+ ExpectServerWriteHandleDeserializeDataUpdate(serverHandle, mUpdatedBufferContent);
+ EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
+
+ FlushClient();
+
+ // The writeHandle is preserved after unmap and is destroyed once the buffer is destroyed.
+ EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
+ EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
+}
\ No newline at end of file