dawn_wire/client: Encapsulate all buffer-related logic in Buffer.cpp
This CL only moves code, renames client::Buffer members and does
additional casts where needed. No functional changes.
Bug: dawn:445
Change-Id: I2bf83ecc1c9b36d5965d0365360dd981fcd41aac
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/23860
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn_wire/client/ApiProcs.cpp b/src/dawn_wire/client/ApiProcs.cpp
index 358e717..da12171 100644
--- a/src/dawn_wire/client/ApiProcs.cpp
+++ b/src/dawn_wire/client/ApiProcs.cpp
@@ -18,214 +18,49 @@
namespace dawn_wire { namespace client {
- namespace {
- template <typename Handle>
- void SerializeBufferMapAsync(const Buffer* buffer, uint32_t serial, Handle* handle) {
- // TODO(enga): Remove the template when Read/Write handles are combined in a tagged
- // pointer.
- constexpr bool isWrite =
- std::is_same<Handle, MemoryTransferService::WriteHandle>::value;
-
- // Get the serialization size of the handle.
- size_t handleCreateInfoLength = handle->SerializeCreateSize();
-
- BufferMapAsyncCmd cmd;
- cmd.bufferId = buffer->id;
- cmd.requestSerial = serial;
- cmd.isWrite = isWrite;
- cmd.handleCreateInfoLength = handleCreateInfoLength;
- cmd.handleCreateInfo = nullptr;
-
- char* writeHandleSpace =
- buffer->device->GetClient()->SerializeCommand(cmd, handleCreateInfoLength);
-
- // Serialize the handle into the space after the command.
- handle->SerializeCreate(writeHandleSpace);
- }
- } // namespace
-
void ClientHandwrittenBufferMapReadAsync(WGPUBuffer cBuffer,
WGPUBufferMapReadCallback callback,
void* userdata) {
Buffer* buffer = reinterpret_cast<Buffer*>(cBuffer);
-
- uint32_t serial = buffer->requestSerial++;
- ASSERT(buffer->requests.find(serial) == buffer->requests.end());
-
- if (buffer->size > std::numeric_limits<size_t>::max()) {
- // On buffer creation, we check that mappable buffers do not exceed this size.
- // So this buffer must not have mappable usage. Inject a validation error.
- ClientDeviceInjectError(reinterpret_cast<WGPUDevice>(buffer->device),
- WGPUErrorType_Validation,
- "Buffer needs the correct map usage bit");
- callback(WGPUBufferMapAsyncStatus_Error, nullptr, 0, userdata);
- return;
- }
-
- // Create a ReadHandle for the map request. This is the client's intent to read GPU
- // memory.
- MemoryTransferService::ReadHandle* readHandle =
- buffer->device->GetClient()->GetMemoryTransferService()->CreateReadHandle(
- static_cast<size_t>(buffer->size));
- if (readHandle == nullptr) {
- ClientDeviceInjectError(reinterpret_cast<WGPUDevice>(buffer->device),
- WGPUErrorType_OutOfMemory, "Failed to create buffer mapping");
- callback(WGPUBufferMapAsyncStatus_Error, nullptr, 0, userdata);
- return;
- }
-
- Buffer::MapRequestData request = {};
- request.readCallback = callback;
- request.userdata = userdata;
- // The handle is owned by the MapRequest until the callback returns.
- request.readHandle = std::unique_ptr<MemoryTransferService::ReadHandle>(readHandle);
-
- // Store a mapping from serial -> MapRequest. The client can map/unmap before the map
- // operations are returned by the server so multiple requests may be in flight.
- buffer->requests[serial] = std::move(request);
-
- SerializeBufferMapAsync(buffer, serial, readHandle);
+ buffer->MapReadAsync(callback, userdata);
}
void ClientHandwrittenBufferMapWriteAsync(WGPUBuffer cBuffer,
WGPUBufferMapWriteCallback callback,
void* userdata) {
Buffer* buffer = reinterpret_cast<Buffer*>(cBuffer);
+ buffer->MapWriteAsync(callback, userdata);
+ }
- uint32_t serial = buffer->requestSerial++;
- ASSERT(buffer->requests.find(serial) == buffer->requests.end());
+ void ClientHandwrittenBufferSetSubData(WGPUBuffer cBuffer,
+ uint64_t start,
+ uint64_t count,
+ const void* data) {
+ Buffer* buffer = reinterpret_cast<Buffer*>(cBuffer);
+ buffer->SetSubData(start, count, data);
+ }
- if (buffer->size > std::numeric_limits<size_t>::max()) {
- // On buffer creation, we check that mappable buffers do not exceed this size.
- // So this buffer must not have mappable usage. Inject a validation error.
- ClientDeviceInjectError(reinterpret_cast<WGPUDevice>(buffer->device),
- WGPUErrorType_Validation,
- "Buffer needs the correct map usage bit");
- callback(WGPUBufferMapAsyncStatus_Error, nullptr, 0, userdata);
- return;
- }
+ void ClientHandwrittenBufferUnmap(WGPUBuffer cBuffer) {
+ Buffer* buffer = reinterpret_cast<Buffer*>(cBuffer);
+ buffer->Unmap();
+ }
- // Create a WriteHandle for the map request. This is the client's intent to write GPU
- // memory.
- MemoryTransferService::WriteHandle* writeHandle =
- buffer->device->GetClient()->GetMemoryTransferService()->CreateWriteHandle(
- static_cast<size_t>(buffer->size));
- if (writeHandle == nullptr) {
- ClientDeviceInjectError(reinterpret_cast<WGPUDevice>(buffer->device),
- WGPUErrorType_OutOfMemory, "Failed to create buffer mapping");
- callback(WGPUBufferMapAsyncStatus_Error, nullptr, 0, userdata);
- return;
- }
-
- Buffer::MapRequestData request = {};
- request.writeCallback = callback;
- request.userdata = userdata;
- // The handle is owned by the MapRequest until the callback returns.
- request.writeHandle = std::unique_ptr<MemoryTransferService::WriteHandle>(writeHandle);
-
- // Store a mapping from serial -> MapRequest. The client can map/unmap before the map
- // operations are returned by the server so multiple requests may be in flight.
- buffer->requests[serial] = std::move(request);
-
- SerializeBufferMapAsync(buffer, serial, writeHandle);
+ void ClientHandwrittenBufferDestroy(WGPUBuffer cBuffer) {
+ Buffer* buffer = reinterpret_cast<Buffer*>(cBuffer);
+ buffer->Destroy();
}
WGPUBuffer ClientHandwrittenDeviceCreateBuffer(WGPUDevice cDevice,
const WGPUBufferDescriptor* descriptor) {
Device* device = reinterpret_cast<Device*>(cDevice);
- Client* wireClient = device->GetClient();
-
- if ((descriptor->usage & (WGPUBufferUsage_MapRead | WGPUBufferUsage_MapWrite)) != 0 &&
- descriptor->size > std::numeric_limits<size_t>::max()) {
- ClientDeviceInjectError(cDevice, WGPUErrorType_OutOfMemory,
- "Buffer is too large for map usage");
- return ClientDeviceCreateErrorBuffer(cDevice);
- }
-
- auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(device);
- Buffer* buffer = bufferObjectAndSerial->object.get();
- // Store the size of the buffer so that mapping operations can allocate a
- // MemoryTransfer handle of the proper size.
- buffer->size = descriptor->size;
-
- DeviceCreateBufferCmd cmd;
- cmd.self = cDevice;
- cmd.descriptor = descriptor;
- cmd.result = ObjectHandle{buffer->id, bufferObjectAndSerial->generation};
-
- wireClient->SerializeCommand(cmd);
-
- return reinterpret_cast<WGPUBuffer>(buffer);
+ return Buffer::Create(device, descriptor);
}
WGPUCreateBufferMappedResult ClientHandwrittenDeviceCreateBufferMapped(
WGPUDevice cDevice,
const WGPUBufferDescriptor* descriptor) {
Device* device = reinterpret_cast<Device*>(cDevice);
- Client* wireClient = device->GetClient();
-
- WGPUCreateBufferMappedResult result;
- result.data = nullptr;
- result.dataLength = 0;
-
- // This buffer is too large to be mapped and to make a WriteHandle for.
- if (descriptor->size > std::numeric_limits<size_t>::max()) {
- ClientDeviceInjectError(cDevice, WGPUErrorType_OutOfMemory,
- "Buffer is too large for mapping");
- result.buffer = ClientDeviceCreateErrorBuffer(cDevice);
- return result;
- }
-
- // Create a WriteHandle for the map request. This is the client's intent to write GPU
- // memory.
- std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle =
- std::unique_ptr<MemoryTransferService::WriteHandle>(
- wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size));
-
- if (writeHandle == nullptr) {
- ClientDeviceInjectError(cDevice, WGPUErrorType_OutOfMemory,
- "Buffer mapping allocation failed");
- result.buffer = ClientDeviceCreateErrorBuffer(cDevice);
- return result;
- }
-
- // CreateBufferMapped is synchronous and the staging buffer for upload should be immediately
- // available.
- // Open the WriteHandle. This returns a pointer and size of mapped memory.
- // |result.data| may be null on error.
- std::tie(result.data, result.dataLength) = writeHandle->Open();
- if (result.data == nullptr) {
- ClientDeviceInjectError(cDevice, WGPUErrorType_OutOfMemory,
- "Buffer mapping allocation failed");
- result.buffer = ClientDeviceCreateErrorBuffer(cDevice);
- return result;
- }
-
- auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(device);
- Buffer* buffer = bufferObjectAndSerial->object.get();
- buffer->size = descriptor->size;
- // Successfully created staging memory. The buffer now owns the WriteHandle.
- buffer->writeHandle = std::move(writeHandle);
-
- result.buffer = reinterpret_cast<WGPUBuffer>(buffer);
-
- // Get the serialization size of the WriteHandle.
- size_t handleCreateInfoLength = buffer->writeHandle->SerializeCreateSize();
-
- DeviceCreateBufferMappedCmd cmd;
- cmd.device = cDevice;
- cmd.descriptor = descriptor;
- cmd.result = ObjectHandle{buffer->id, bufferObjectAndSerial->generation};
- cmd.handleCreateInfoLength = handleCreateInfoLength;
- cmd.handleCreateInfo = nullptr;
-
- char* writeHandleSpace =
- buffer->device->GetClient()->SerializeCommand(cmd, handleCreateInfoLength);
-
- // Serialize the WriteHandle into the space after the command.
- buffer->writeHandle->SerializeCreate(writeHandleSpace);
-
- return result;
+ return Buffer::CreateMapped(device, descriptor);
}
void ClientHandwrittenDevicePushErrorScope(WGPUDevice cDevice, WGPUErrorFilter filter) {
@@ -269,76 +104,6 @@
fence->requests.Enqueue(std::move(request), value);
}
- void ClientHandwrittenBufferSetSubData(WGPUBuffer cBuffer,
- uint64_t start,
- uint64_t count,
- const void* data) {
- Buffer* buffer = reinterpret_cast<Buffer*>(cBuffer);
-
- BufferSetSubDataInternalCmd cmd;
- cmd.bufferId = buffer->id;
- cmd.start = start;
- cmd.count = count;
- cmd.data = static_cast<const uint8_t*>(data);
-
- buffer->device->GetClient()->SerializeCommand(cmd);
- }
-
- void ClientHandwrittenBufferUnmap(WGPUBuffer cBuffer) {
- Buffer* buffer = reinterpret_cast<Buffer*>(cBuffer);
-
- // Invalidate the local pointer, and cancel all other in-flight requests that would
- // turn into errors anyway (you can't double map). This prevents race when the following
- // happens, where the application code would have unmapped a buffer but still receive a
- // callback:
- // - Client -> Server: MapRequest1, Unmap, MapRequest2
- // - Server -> Client: Result of MapRequest1
- // - Unmap locally on the client
- // - Server -> Client: Result of MapRequest2
- if (buffer->writeHandle) {
- // Writes need to be flushed before Unmap is sent. Unmap calls all associated
- // in-flight callbacks which may read the updated data.
- ASSERT(buffer->readHandle == nullptr);
-
- // Get the serialization size of metadata to flush writes.
- size_t writeFlushInfoLength = buffer->writeHandle->SerializeFlushSize();
-
- BufferUpdateMappedDataCmd cmd;
- cmd.bufferId = buffer->id;
- cmd.writeFlushInfoLength = writeFlushInfoLength;
- cmd.writeFlushInfo = nullptr;
-
- char* writeHandleSpace =
- buffer->device->GetClient()->SerializeCommand(cmd, writeFlushInfoLength);
-
- // Serialize flush metadata into the space after the command.
- // This closes the handle for writing.
- buffer->writeHandle->SerializeFlush(writeHandleSpace);
- buffer->writeHandle = nullptr;
-
- } else if (buffer->readHandle) {
- buffer->readHandle = nullptr;
- }
- buffer->ClearMapRequests(WGPUBufferMapAsyncStatus_Unknown);
-
- BufferUnmapCmd cmd;
- cmd.self = cBuffer;
- buffer->device->GetClient()->SerializeCommand(cmd);
- }
-
- void ClientHandwrittenBufferDestroy(WGPUBuffer cBuffer) {
- Buffer* buffer = reinterpret_cast<Buffer*>(cBuffer);
-
- // Cancel or remove all mappings
- buffer->writeHandle = nullptr;
- buffer->readHandle = nullptr;
- buffer->ClearMapRequests(WGPUBufferMapAsyncStatus_Unknown);
-
- BufferDestroyCmd cmd;
- cmd.self = cBuffer;
- buffer->device->GetClient()->SerializeCommand(cmd);
- }
-
WGPUFence ClientHandwrittenQueueCreateFence(WGPUQueue cSelf,
WGPUFenceDescriptor const* descriptor) {
Queue* queue = reinterpret_cast<Queue*>(cSelf);
diff --git a/src/dawn_wire/client/Buffer.cpp b/src/dawn_wire/client/Buffer.cpp
index 3548f51..a4f5fad 100644
--- a/src/dawn_wire/client/Buffer.cpp
+++ b/src/dawn_wire/client/Buffer.cpp
@@ -14,8 +14,136 @@
#include "dawn_wire/client/Buffer.h"
+#include "dawn_wire/client/ApiProcs_autogen.h"
+#include "dawn_wire/client/Client.h"
+#include "dawn_wire/client/Device.h"
+
namespace dawn_wire { namespace client {
+ namespace {
+ template <typename Handle>
+ void SerializeBufferMapAsync(const Buffer* buffer, uint32_t serial, Handle* handle) {
+ // TODO(enga): Remove the template when Read/Write handles are combined in a tagged
+ // pointer.
+ constexpr bool isWrite =
+ std::is_same<Handle, MemoryTransferService::WriteHandle>::value;
+
+ // Get the serialization size of the handle.
+ size_t handleCreateInfoLength = handle->SerializeCreateSize();
+
+ BufferMapAsyncCmd cmd;
+ cmd.bufferId = buffer->id;
+ cmd.requestSerial = serial;
+ cmd.isWrite = isWrite;
+ cmd.handleCreateInfoLength = handleCreateInfoLength;
+ cmd.handleCreateInfo = nullptr;
+
+ char* writeHandleSpace =
+ buffer->device->GetClient()->SerializeCommand(cmd, handleCreateInfoLength);
+
+ // Serialize the handle into the space after the command.
+ handle->SerializeCreate(writeHandleSpace);
+ }
+ } // namespace
+
+ // static
+ WGPUBuffer Buffer::Create(Device* device_, const WGPUBufferDescriptor* descriptor) {
+ WGPUDevice cDevice = reinterpret_cast<WGPUDevice>(device_);
+ Client* wireClient = device_->GetClient();
+
+ if ((descriptor->usage & (WGPUBufferUsage_MapRead | WGPUBufferUsage_MapWrite)) != 0 &&
+ descriptor->size > std::numeric_limits<size_t>::max()) {
+ ClientDeviceInjectError(cDevice, WGPUErrorType_OutOfMemory,
+ "Buffer is too large for map usage");
+ return ClientDeviceCreateErrorBuffer(cDevice);
+ }
+
+ auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(device_);
+ Buffer* buffer = bufferObjectAndSerial->object.get();
+ // Store the size of the buffer so that mapping operations can allocate a
+ // MemoryTransfer handle of the proper size.
+ buffer->mSize = descriptor->size;
+
+ DeviceCreateBufferCmd cmd;
+ cmd.self = cDevice;
+ cmd.descriptor = descriptor;
+ cmd.result = ObjectHandle{buffer->id, bufferObjectAndSerial->generation};
+
+ wireClient->SerializeCommand(cmd);
+
+ return reinterpret_cast<WGPUBuffer>(buffer);
+ }
+
+ // static
+ WGPUCreateBufferMappedResult Buffer::CreateMapped(Device* device_,
+ const WGPUBufferDescriptor* descriptor) {
+ WGPUDevice cDevice = reinterpret_cast<WGPUDevice>(device_);
+ Client* wireClient = device_->GetClient();
+
+ WGPUCreateBufferMappedResult result;
+ result.data = nullptr;
+ result.dataLength = 0;
+
+ // This buffer is too large to be mapped and to make a WriteHandle for.
+ if (descriptor->size > std::numeric_limits<size_t>::max()) {
+ ClientDeviceInjectError(cDevice, WGPUErrorType_OutOfMemory,
+ "Buffer is too large for mapping");
+ result.buffer = ClientDeviceCreateErrorBuffer(cDevice);
+ return result;
+ }
+
+ // Create a WriteHandle for the map request. This is the client's intent to write GPU
+ // memory.
+ std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle =
+ std::unique_ptr<MemoryTransferService::WriteHandle>(
+ wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size));
+
+ if (writeHandle == nullptr) {
+ ClientDeviceInjectError(cDevice, WGPUErrorType_OutOfMemory,
+ "Buffer mapping allocation failed");
+ result.buffer = ClientDeviceCreateErrorBuffer(cDevice);
+ return result;
+ }
+
+ // CreateBufferMapped is synchronous and the staging buffer for upload should be immediately
+ // available.
+ // Open the WriteHandle. This returns a pointer and size of mapped memory.
+ // |result.data| may be null on error.
+ std::tie(result.data, result.dataLength) = writeHandle->Open();
+ if (result.data == nullptr) {
+ ClientDeviceInjectError(cDevice, WGPUErrorType_OutOfMemory,
+ "Buffer mapping allocation failed");
+ result.buffer = ClientDeviceCreateErrorBuffer(cDevice);
+ return result;
+ }
+
+ auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(device_);
+ Buffer* buffer = bufferObjectAndSerial->object.get();
+ buffer->mSize = descriptor->size;
+ // Successfully created staging memory. The buffer now owns the WriteHandle.
+ buffer->mWriteHandle = std::move(writeHandle);
+
+ result.buffer = reinterpret_cast<WGPUBuffer>(buffer);
+
+ // Get the serialization size of the WriteHandle.
+ size_t handleCreateInfoLength = buffer->mWriteHandle->SerializeCreateSize();
+
+ DeviceCreateBufferMappedCmd cmd;
+ cmd.device = cDevice;
+ cmd.descriptor = descriptor;
+ cmd.result = ObjectHandle{buffer->id, bufferObjectAndSerial->generation};
+ cmd.handleCreateInfoLength = handleCreateInfoLength;
+ cmd.handleCreateInfo = nullptr;
+
+ char* writeHandleSpace =
+ buffer->device->GetClient()->SerializeCommand(cmd, handleCreateInfoLength);
+
+ // Serialize the WriteHandle into the space after the command.
+ buffer->mWriteHandle->SerializeCreate(writeHandleSpace);
+
+ return result;
+ }
+
Buffer::~Buffer() {
// Callbacks need to be fired in all cases, as they can handle freeing resources
// so we call them with "Unknown" status.
@@ -23,14 +151,275 @@
}
void Buffer::ClearMapRequests(WGPUBufferMapAsyncStatus status) {
- for (auto& it : requests) {
+ for (auto& it : mRequests) {
if (it.second.writeHandle) {
it.second.writeCallback(status, nullptr, 0, it.second.userdata);
} else {
it.second.readCallback(status, nullptr, 0, it.second.userdata);
}
}
- requests.clear();
+ mRequests.clear();
+ }
+
+ void Buffer::MapReadAsync(WGPUBufferMapReadCallback callback, void* userdata) {
+ uint32_t serial = mRequestSerial++;
+ ASSERT(mRequests.find(serial) == mRequests.end());
+
+ if (mSize > std::numeric_limits<size_t>::max()) {
+ // On buffer creation, we check that mappable buffers do not exceed this size.
+ // So this buffer must not have mappable usage. Inject a validation error.
+ ClientDeviceInjectError(reinterpret_cast<WGPUDevice>(device), WGPUErrorType_Validation,
+ "Buffer needs the correct map usage bit");
+ callback(WGPUBufferMapAsyncStatus_Error, nullptr, 0, userdata);
+ return;
+ }
+
+ // Create a ReadHandle for the map request. This is the client's intent to read GPU
+ // memory.
+ MemoryTransferService::ReadHandle* readHandle =
+ device->GetClient()->GetMemoryTransferService()->CreateReadHandle(
+ static_cast<size_t>(mSize));
+ if (readHandle == nullptr) {
+ ClientDeviceInjectError(reinterpret_cast<WGPUDevice>(device), WGPUErrorType_OutOfMemory,
+ "Failed to create buffer mapping");
+ callback(WGPUBufferMapAsyncStatus_Error, nullptr, 0, userdata);
+ return;
+ }
+
+ Buffer::MapRequestData request = {};
+ request.readCallback = callback;
+ request.userdata = userdata;
+ // The handle is owned by the MapRequest until the callback returns.
+ request.readHandle = std::unique_ptr<MemoryTransferService::ReadHandle>(readHandle);
+
+ // Store a mapping from serial -> MapRequest. The client can map/unmap before the map
+ // operations are returned by the server so multiple requests may be in flight.
+ mRequests[serial] = std::move(request);
+
+ SerializeBufferMapAsync(this, serial, readHandle);
+ }
+
+ void Buffer::MapWriteAsync(WGPUBufferMapWriteCallback callback, void* userdata) {
+ uint32_t serial = mRequestSerial++;
+ ASSERT(mRequests.find(serial) == mRequests.end());
+
+ if (mSize > std::numeric_limits<size_t>::max()) {
+ // On buffer creation, we check that mappable buffers do not exceed this size.
+ // So this buffer must not have mappable usage. Inject a validation error.
+ ClientDeviceInjectError(reinterpret_cast<WGPUDevice>(device), WGPUErrorType_Validation,
+ "Buffer needs the correct map usage bit");
+ callback(WGPUBufferMapAsyncStatus_Error, nullptr, 0, userdata);
+ return;
+ }
+
+ // Create a WriteHandle for the map request. This is the client's intent to write GPU
+ // memory.
+ MemoryTransferService::WriteHandle* writeHandle =
+ device->GetClient()->GetMemoryTransferService()->CreateWriteHandle(
+ static_cast<size_t>(mSize));
+ if (writeHandle == nullptr) {
+ ClientDeviceInjectError(reinterpret_cast<WGPUDevice>(device), WGPUErrorType_OutOfMemory,
+ "Failed to create buffer mapping");
+ callback(WGPUBufferMapAsyncStatus_Error, nullptr, 0, userdata);
+ return;
+ }
+
+ Buffer::MapRequestData request = {};
+ request.writeCallback = callback;
+ request.userdata = userdata;
+ // The handle is owned by the MapRequest until the callback returns.
+ request.writeHandle = std::unique_ptr<MemoryTransferService::WriteHandle>(writeHandle);
+
+ // Store a mapping from serial -> MapRequest. The client can map/unmap before the map
+ // operations are returned by the server so multiple requests may be in flight.
+ mRequests[serial] = std::move(request);
+
+ SerializeBufferMapAsync(this, serial, writeHandle);
+ }
+
+ bool Buffer::OnMapReadAsyncCallback(uint32_t requestSerial,
+ uint32_t status,
+ uint64_t initialDataInfoLength,
+ const uint8_t* initialDataInfo) {
+ // The requests can have been deleted via an Unmap so this isn't an error.
+ auto requestIt = mRequests.find(requestSerial);
+ if (requestIt == mRequests.end()) {
+ return true;
+ }
+
+ auto request = std::move(requestIt->second);
+ // Delete the request before calling the callback otherwise the callback could be fired a
+ // second time. If, for example, buffer.Unmap() is called inside the callback.
+ mRequests.erase(requestIt);
+
+ const void* mappedData = nullptr;
+ size_t mappedDataLength = 0;
+
+ auto GetMappedData = [&]() -> bool {
+ // It is an error for the server to call the read callback when we asked for a map write
+ if (request.writeHandle) {
+ return false;
+ }
+
+ if (status == WGPUBufferMapAsyncStatus_Success) {
+ if (mReadHandle || mWriteHandle) {
+ // Buffer is already mapped.
+ return false;
+ }
+ if (initialDataInfoLength > std::numeric_limits<size_t>::max()) {
+ // This is the size of data deserialized from the command stream, which must be
+ // CPU-addressable.
+ return false;
+ }
+ ASSERT(request.readHandle != nullptr);
+
+ // 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(
+ initialDataInfo, static_cast<size_t>(initialDataInfoLength), &mappedData,
+ &mappedDataLength)) {
+ // Deserialization shouldn't fail. This is a fatal error.
+ return false;
+ }
+ ASSERT(mappedData != nullptr);
+
+ // The MapRead request was successful. The buffer now owns the ReadHandle until
+ // Unmap().
+ mReadHandle = std::move(request.readHandle);
+ }
+
+ return true;
+ };
+
+ if (!GetMappedData()) {
+ // Dawn promises that all callbacks are called in finite time. Even if a fatal error
+ // occurs, the callback is called.
+ request.readCallback(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, request.userdata);
+ return false;
+ } else {
+ request.readCallback(static_cast<WGPUBufferMapAsyncStatus>(status), mappedData,
+ static_cast<uint64_t>(mappedDataLength), request.userdata);
+ return true;
+ }
+ }
+
+ bool Buffer::OnMapWriteAsyncCallback(uint32_t requestSerial, uint32_t status) {
+ // The requests can have been deleted via an Unmap so this isn't an error.
+ auto requestIt = mRequests.find(requestSerial);
+ if (requestIt == mRequests.end()) {
+ return true;
+ }
+
+ auto request = std::move(requestIt->second);
+ // Delete the request before calling the callback otherwise the callback could be fired a
+ // second time. If, for example, buffer.Unmap() is called inside the callback.
+ mRequests.erase(requestIt);
+
+ void* mappedData = nullptr;
+ size_t mappedDataLength = 0;
+
+ auto GetMappedData = [&]() -> bool {
+ // It is an error for the server to call the write callback when we asked for a map read
+ if (request.readHandle) {
+ return false;
+ }
+
+ if (status == WGPUBufferMapAsyncStatus_Success) {
+ if (mReadHandle || mWriteHandle) {
+ // Buffer is already mapped.
+ return false;
+ }
+ ASSERT(request.writeHandle != nullptr);
+
+ // 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 false;
+ }
+
+ // The MapWrite request was successful. The buffer now owns the WriteHandle until
+ // Unmap().
+ mWriteHandle = std::move(request.writeHandle);
+ }
+
+ return true;
+ };
+
+ if (!GetMappedData()) {
+ // Dawn promises that all callbacks are called in finite time. Even if a fatal error
+ // occurs, the callback is called.
+ request.writeCallback(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0,
+ request.userdata);
+ return false;
+ } else {
+ request.writeCallback(static_cast<WGPUBufferMapAsyncStatus>(status), mappedData,
+ static_cast<uint64_t>(mappedDataLength), request.userdata);
+ return true;
+ }
+ }
+
+ void Buffer::Unmap() {
+ // Invalidate the local pointer, and cancel all other in-flight requests that would
+ // turn into errors anyway (you can't double map). This prevents race when the following
+ // happens, where the application code would have unmapped a buffer but still receive a
+ // callback:
+ // - Client -> Server: MapRequest1, Unmap, MapRequest2
+ // - Server -> Client: Result of MapRequest1
+ // - Unmap locally on the client
+ // - Server -> Client: Result of MapRequest2
+ if (mWriteHandle) {
+ // 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();
+
+ BufferUpdateMappedDataCmd cmd;
+ cmd.bufferId = id;
+ cmd.writeFlushInfoLength = writeFlushInfoLength;
+ cmd.writeFlushInfo = nullptr;
+
+ char* writeHandleSpace =
+ device->GetClient()->SerializeCommand(cmd, writeFlushInfoLength);
+
+ // Serialize flush metadata into the space after the command.
+ // This closes the handle for writing.
+ mWriteHandle->SerializeFlush(writeHandleSpace);
+ mWriteHandle = nullptr;
+
+ } else if (mReadHandle) {
+ mReadHandle = nullptr;
+ }
+ ClearMapRequests(WGPUBufferMapAsyncStatus_Unknown);
+
+ BufferUnmapCmd cmd;
+ cmd.self = reinterpret_cast<WGPUBuffer>(this);
+ device->GetClient()->SerializeCommand(cmd);
+ }
+
+ void Buffer::Destroy() {
+ // Cancel or remove all mappings
+ mWriteHandle = nullptr;
+ mReadHandle = nullptr;
+ ClearMapRequests(WGPUBufferMapAsyncStatus_Unknown);
+
+ BufferDestroyCmd cmd;
+ cmd.self = reinterpret_cast<WGPUBuffer>(this);
+ device->GetClient()->SerializeCommand(cmd);
+ }
+
+ void Buffer::SetSubData(uint64_t start, uint64_t count, const void* data) {
+ BufferSetSubDataInternalCmd cmd;
+ cmd.bufferId = id;
+ cmd.start = start;
+ cmd.count = count;
+ cmd.data = static_cast<const uint8_t*>(data);
+
+ device->GetClient()->SerializeCommand(cmd);
}
}} // namespace dawn_wire::client
diff --git a/src/dawn_wire/client/Buffer.h b/src/dawn_wire/client/Buffer.h
index 09da8e3..96cabf6 100644
--- a/src/dawn_wire/client/Buffer.h
+++ b/src/dawn_wire/client/Buffer.h
@@ -24,12 +24,31 @@
namespace dawn_wire { namespace client {
- struct Buffer : ObjectBase {
+ class Buffer : public ObjectBase {
+ public:
using ObjectBase::ObjectBase;
+ static WGPUBuffer Create(Device* device, const WGPUBufferDescriptor* descriptor);
+ static WGPUCreateBufferMappedResult CreateMapped(Device* device,
+ const WGPUBufferDescriptor* descriptor);
+
~Buffer();
void ClearMapRequests(WGPUBufferMapAsyncStatus status);
+ void MapReadAsync(WGPUBufferMapReadCallback callback, void* userdata);
+ void MapWriteAsync(WGPUBufferMapWriteCallback callback, void* userdata);
+ bool OnMapReadAsyncCallback(uint32_t requestSerial,
+ uint32_t status,
+ uint64_t initialDataInfoLength,
+ const uint8_t* initialDataInfo);
+ bool OnMapWriteAsyncCallback(uint32_t requestSerial, uint32_t status);
+ void Unmap();
+
+ void Destroy();
+
+ void SetSubData(uint64_t start, uint64_t count, const void* data);
+
+ private:
// 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.
@@ -42,15 +61,15 @@
std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = nullptr;
std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
};
- std::map<uint32_t, MapRequestData> requests;
- uint32_t requestSerial = 0;
- uint64_t size = 0;
+ std::map<uint32_t, MapRequestData> mRequests;
+ uint32_t mRequestSerial = 0;
+ uint64_t mSize = 0;
// Only one mapped pointer can be active at a time because Unmap clears all the in-flight
// requests.
// TODO(enga): Use a tagged pointer to save space.
- std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = nullptr;
- std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
+ std::unique_ptr<MemoryTransferService::ReadHandle> mReadHandle = nullptr;
+ std::unique_ptr<MemoryTransferService::WriteHandle> mWriteHandle = nullptr;
};
}} // namespace dawn_wire::client
diff --git a/src/dawn_wire/client/ClientDoers.cpp b/src/dawn_wire/client/ClientDoers.cpp
index dd90406..ffc5198 100644
--- a/src/dawn_wire/client/ClientDoers.cpp
+++ b/src/dawn_wire/client/ClientDoers.cpp
@@ -56,67 +56,8 @@
return true;
}
- // The requests can have been deleted via an Unmap so this isn't an error.
- auto requestIt = buffer->requests.find(requestSerial);
- if (requestIt == buffer->requests.end()) {
- return true;
- }
-
- auto request = std::move(requestIt->second);
- // Delete the request before calling the callback otherwise the callback could be fired a
- // second time. If, for example, buffer.Unmap() is called inside the callback.
- buffer->requests.erase(requestIt);
-
- const void* mappedData = nullptr;
- size_t mappedDataLength = 0;
-
- auto GetMappedData = [&]() -> bool {
- // It is an error for the server to call the read callback when we asked for a map write
- if (request.writeHandle) {
- return false;
- }
-
- if (status == WGPUBufferMapAsyncStatus_Success) {
- if (buffer->readHandle || buffer->writeHandle) {
- // Buffer is already mapped.
- return false;
- }
- if (initialDataInfoLength > std::numeric_limits<size_t>::max()) {
- // This is the size of data deserialized from the command stream, which must be
- // CPU-addressable.
- return false;
- }
- ASSERT(request.readHandle != nullptr);
-
- // 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(
- initialDataInfo, static_cast<size_t>(initialDataInfoLength), &mappedData,
- &mappedDataLength)) {
- // Deserialization shouldn't fail. This is a fatal error.
- return false;
- }
- ASSERT(mappedData != nullptr);
-
- // The MapRead request was successful. The buffer now owns the ReadHandle until
- // Unmap().
- buffer->readHandle = std::move(request.readHandle);
- }
-
- return true;
- };
-
- if (!GetMappedData()) {
- // Dawn promises that all callbacks are called in finite time. Even if a fatal error
- // occurs, the callback is called.
- request.readCallback(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, request.userdata);
- return false;
- } else {
- request.readCallback(static_cast<WGPUBufferMapAsyncStatus>(status), mappedData,
- static_cast<uint64_t>(mappedDataLength), request.userdata);
- return true;
- }
+ return buffer->OnMapReadAsyncCallback(requestSerial, status, initialDataInfoLength,
+ initialDataInfo);
}
bool Client::DoBufferMapWriteAsyncCallback(Buffer* buffer,
@@ -127,60 +68,7 @@
return true;
}
- // The requests can have been deleted via an Unmap so this isn't an error.
- auto requestIt = buffer->requests.find(requestSerial);
- if (requestIt == buffer->requests.end()) {
- return true;
- }
-
- auto request = std::move(requestIt->second);
- // Delete the request before calling the callback otherwise the callback could be fired a
- // second time. If, for example, buffer.Unmap() is called inside the callback.
- buffer->requests.erase(requestIt);
-
- void* mappedData = nullptr;
- size_t mappedDataLength = 0;
-
- auto GetMappedData = [&]() -> bool {
- // It is an error for the server to call the write callback when we asked for a map read
- if (request.readHandle) {
- return false;
- }
-
- if (status == WGPUBufferMapAsyncStatus_Success) {
- if (buffer->readHandle || buffer->writeHandle) {
- // Buffer is already mapped.
- return false;
- }
- ASSERT(request.writeHandle != nullptr);
-
- // 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 false;
- }
-
- // The MapWrite request was successful. The buffer now owns the WriteHandle until
- // Unmap().
- buffer->writeHandle = std::move(request.writeHandle);
- }
-
- return true;
- };
-
- if (!GetMappedData()) {
- // Dawn promises that all callbacks are called in finite time. Even if a fatal error
- // occurs, the callback is called.
- request.writeCallback(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0,
- request.userdata);
- return false;
- } else {
- request.writeCallback(static_cast<WGPUBufferMapAsyncStatus>(status), mappedData,
- static_cast<uint64_t>(mappedDataLength), request.userdata);
- return true;
- }
+ return buffer->OnMapWriteAsyncCallback(requestSerial, status);
}
bool Client::DoFenceUpdateCompletedValue(Fence* fence, uint64_t value) {