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>
This commit is contained in:
parent
b231c7fb71
commit
bae16b4df9
|
@ -18,214 +18,49 @@
|
||||||
|
|
||||||
namespace dawn_wire { namespace client {
|
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,
|
void ClientHandwrittenBufferMapReadAsync(WGPUBuffer cBuffer,
|
||||||
WGPUBufferMapReadCallback callback,
|
WGPUBufferMapReadCallback callback,
|
||||||
void* userdata) {
|
void* userdata) {
|
||||||
Buffer* buffer = reinterpret_cast<Buffer*>(cBuffer);
|
Buffer* buffer = reinterpret_cast<Buffer*>(cBuffer);
|
||||||
|
buffer->MapReadAsync(callback, userdata);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientHandwrittenBufferMapWriteAsync(WGPUBuffer cBuffer,
|
void ClientHandwrittenBufferMapWriteAsync(WGPUBuffer cBuffer,
|
||||||
WGPUBufferMapWriteCallback callback,
|
WGPUBufferMapWriteCallback callback,
|
||||||
void* userdata) {
|
void* userdata) {
|
||||||
Buffer* buffer = reinterpret_cast<Buffer*>(cBuffer);
|
Buffer* buffer = reinterpret_cast<Buffer*>(cBuffer);
|
||||||
|
buffer->MapWriteAsync(callback, userdata);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t serial = buffer->requestSerial++;
|
void ClientHandwrittenBufferSetSubData(WGPUBuffer cBuffer,
|
||||||
ASSERT(buffer->requests.find(serial) == buffer->requests.end());
|
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()) {
|
void ClientHandwrittenBufferUnmap(WGPUBuffer cBuffer) {
|
||||||
// On buffer creation, we check that mappable buffers do not exceed this size.
|
Buffer* buffer = reinterpret_cast<Buffer*>(cBuffer);
|
||||||
// So this buffer must not have mappable usage. Inject a validation error.
|
buffer->Unmap();
|
||||||
ClientDeviceInjectError(reinterpret_cast<WGPUDevice>(buffer->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
|
void ClientHandwrittenBufferDestroy(WGPUBuffer cBuffer) {
|
||||||
// memory.
|
Buffer* buffer = reinterpret_cast<Buffer*>(cBuffer);
|
||||||
MemoryTransferService::WriteHandle* writeHandle =
|
buffer->Destroy();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WGPUBuffer ClientHandwrittenDeviceCreateBuffer(WGPUDevice cDevice,
|
WGPUBuffer ClientHandwrittenDeviceCreateBuffer(WGPUDevice cDevice,
|
||||||
const WGPUBufferDescriptor* descriptor) {
|
const WGPUBufferDescriptor* descriptor) {
|
||||||
Device* device = reinterpret_cast<Device*>(cDevice);
|
Device* device = reinterpret_cast<Device*>(cDevice);
|
||||||
Client* wireClient = device->GetClient();
|
return Buffer::Create(device, descriptor);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WGPUCreateBufferMappedResult ClientHandwrittenDeviceCreateBufferMapped(
|
WGPUCreateBufferMappedResult ClientHandwrittenDeviceCreateBufferMapped(
|
||||||
WGPUDevice cDevice,
|
WGPUDevice cDevice,
|
||||||
const WGPUBufferDescriptor* descriptor) {
|
const WGPUBufferDescriptor* descriptor) {
|
||||||
Device* device = reinterpret_cast<Device*>(cDevice);
|
Device* device = reinterpret_cast<Device*>(cDevice);
|
||||||
Client* wireClient = device->GetClient();
|
return Buffer::CreateMapped(device, descriptor);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientHandwrittenDevicePushErrorScope(WGPUDevice cDevice, WGPUErrorFilter filter) {
|
void ClientHandwrittenDevicePushErrorScope(WGPUDevice cDevice, WGPUErrorFilter filter) {
|
||||||
|
@ -269,76 +104,6 @@ namespace dawn_wire { namespace client {
|
||||||
fence->requests.Enqueue(std::move(request), value);
|
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,
|
WGPUFence ClientHandwrittenQueueCreateFence(WGPUQueue cSelf,
|
||||||
WGPUFenceDescriptor const* descriptor) {
|
WGPUFenceDescriptor const* descriptor) {
|
||||||
Queue* queue = reinterpret_cast<Queue*>(cSelf);
|
Queue* queue = reinterpret_cast<Queue*>(cSelf);
|
||||||
|
|
|
@ -14,8 +14,136 @@
|
||||||
|
|
||||||
#include "dawn_wire/client/Buffer.h"
|
#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 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() {
|
Buffer::~Buffer() {
|
||||||
// Callbacks need to be fired in all cases, as they can handle freeing resources
|
// Callbacks need to be fired in all cases, as they can handle freeing resources
|
||||||
// so we call them with "Unknown" status.
|
// so we call them with "Unknown" status.
|
||||||
|
@ -23,14 +151,275 @@ namespace dawn_wire { namespace client {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::ClearMapRequests(WGPUBufferMapAsyncStatus status) {
|
void Buffer::ClearMapRequests(WGPUBufferMapAsyncStatus status) {
|
||||||
for (auto& it : requests) {
|
for (auto& it : mRequests) {
|
||||||
if (it.second.writeHandle) {
|
if (it.second.writeHandle) {
|
||||||
it.second.writeCallback(status, nullptr, 0, it.second.userdata);
|
it.second.writeCallback(status, nullptr, 0, it.second.userdata);
|
||||||
} else {
|
} else {
|
||||||
it.second.readCallback(status, nullptr, 0, it.second.userdata);
|
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
|
}} // namespace dawn_wire::client
|
||||||
|
|
|
@ -24,12 +24,31 @@
|
||||||
|
|
||||||
namespace dawn_wire { namespace client {
|
namespace dawn_wire { namespace client {
|
||||||
|
|
||||||
struct Buffer : ObjectBase {
|
class Buffer : public ObjectBase {
|
||||||
|
public:
|
||||||
using ObjectBase::ObjectBase;
|
using ObjectBase::ObjectBase;
|
||||||
|
|
||||||
|
static WGPUBuffer Create(Device* device, const WGPUBufferDescriptor* descriptor);
|
||||||
|
static WGPUCreateBufferMappedResult CreateMapped(Device* device,
|
||||||
|
const WGPUBufferDescriptor* descriptor);
|
||||||
|
|
||||||
~Buffer();
|
~Buffer();
|
||||||
void ClearMapRequests(WGPUBufferMapAsyncStatus status);
|
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
|
// 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.
|
// 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.
|
// On well-behaved applications, only one request should exist at a single time.
|
||||||
|
@ -42,15 +61,15 @@ namespace dawn_wire { namespace client {
|
||||||
std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = nullptr;
|
std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = nullptr;
|
||||||
std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
|
std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
|
||||||
};
|
};
|
||||||
std::map<uint32_t, MapRequestData> requests;
|
std::map<uint32_t, MapRequestData> mRequests;
|
||||||
uint32_t requestSerial = 0;
|
uint32_t mRequestSerial = 0;
|
||||||
uint64_t size = 0;
|
uint64_t mSize = 0;
|
||||||
|
|
||||||
// Only one mapped pointer can be active at a time because Unmap clears all the in-flight
|
// Only one mapped pointer can be active at a time because Unmap clears all the in-flight
|
||||||
// requests.
|
// requests.
|
||||||
// TODO(enga): Use a tagged pointer to save space.
|
// TODO(enga): Use a tagged pointer to save space.
|
||||||
std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = nullptr;
|
std::unique_ptr<MemoryTransferService::ReadHandle> mReadHandle = nullptr;
|
||||||
std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
|
std::unique_ptr<MemoryTransferService::WriteHandle> mWriteHandle = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
}} // namespace dawn_wire::client
|
}} // namespace dawn_wire::client
|
||||||
|
|
|
@ -56,67 +56,8 @@ namespace dawn_wire { namespace client {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The requests can have been deleted via an Unmap so this isn't an error.
|
return buffer->OnMapReadAsyncCallback(requestSerial, status, initialDataInfoLength,
|
||||||
auto requestIt = buffer->requests.find(requestSerial);
|
initialDataInfo);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::DoBufferMapWriteAsyncCallback(Buffer* buffer,
|
bool Client::DoBufferMapWriteAsyncCallback(Buffer* buffer,
|
||||||
|
@ -127,60 +68,7 @@ namespace dawn_wire { namespace client {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The requests can have been deleted via an Unmap so this isn't an error.
|
return buffer->OnMapWriteAsyncCallback(requestSerial, status);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::DoFenceUpdateCompletedValue(Fence* fence, uint64_t value) {
|
bool Client::DoFenceUpdateCompletedValue(Fence* fence, uint64_t value) {
|
||||||
|
|
Loading…
Reference in New Issue