// Copyright 2019 The Dawn Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dawn_wire/client/ApiObjects.h" #include "dawn_wire/client/ApiProcs_autogen.h" #include "dawn_wire/client/Client.h" namespace dawn_wire { namespace client { namespace { template 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::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; size_t commandSize = cmd.GetRequiredSize(); size_t requiredSize = commandSize + handleCreateInfoLength; char* allocatedBuffer = static_cast(buffer->device->GetClient()->GetCmdSpace(requiredSize)); cmd.Serialize(allocatedBuffer); // Serialize the handle into the space after the command. handle->SerializeCreate(allocatedBuffer + commandSize); } } // namespace void ClientBufferMapReadAsync(WGPUBuffer cBuffer, WGPUBufferMapReadCallback callback, void* userdata) { Buffer* buffer = reinterpret_cast(cBuffer); uint32_t serial = buffer->requestSerial++; ASSERT(buffer->requests.find(serial) == buffer->requests.end()); // 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(buffer->size); if (readHandle == nullptr) { callback(WGPUBufferMapAsyncStatus_DeviceLost, 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(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 ClientBufferMapWriteAsync(WGPUBuffer cBuffer, WGPUBufferMapWriteCallback callback, void* userdata) { Buffer* buffer = reinterpret_cast(cBuffer); uint32_t serial = buffer->requestSerial++; ASSERT(buffer->requests.find(serial) == buffer->requests.end()); // 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( buffer->size); if (writeHandle == nullptr) { callback(WGPUBufferMapAsyncStatus_DeviceLost, 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(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 ClientDeviceCreateBuffer(WGPUDevice cDevice, const WGPUBufferDescriptor* descriptor) { Device* device = reinterpret_cast(cDevice); Client* wireClient = device->GetClient(); 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->serial}; size_t requiredSize = cmd.GetRequiredSize(); char* allocatedBuffer = static_cast(wireClient->GetCmdSpace(requiredSize)); cmd.Serialize(allocatedBuffer, *wireClient); return reinterpret_cast(buffer); } WGPUCreateBufferMappedResult ClientDeviceCreateBufferMapped( WGPUDevice cDevice, const WGPUBufferDescriptor* descriptor) { Device* device = reinterpret_cast(cDevice); Client* wireClient = device->GetClient(); auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(device); Buffer* buffer = bufferObjectAndSerial->object.get(); buffer->size = descriptor->size; WGPUCreateBufferMappedResult result; result.buffer = reinterpret_cast(buffer); result.data = nullptr; result.dataLength = 0; // Create a WriteHandle for the map request. This is the client's intent to write GPU // memory. std::unique_ptr writeHandle = std::unique_ptr( wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size)); if (writeHandle == nullptr) { // TODO(enga): Support context lost generated by the client. 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) { // TODO(enga): Support context lost generated by the client. return result; } // Successfully created staging memory. The buffer now owns the WriteHandle. buffer->writeHandle = std::move(writeHandle); // 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->serial}; cmd.handleCreateInfoLength = handleCreateInfoLength; cmd.handleCreateInfo = nullptr; size_t commandSize = cmd.GetRequiredSize(); size_t requiredSize = commandSize + handleCreateInfoLength; char* allocatedBuffer = static_cast(wireClient->GetCmdSpace(requiredSize)); cmd.Serialize(allocatedBuffer, *wireClient); // Serialize the WriteHandle into the space after the command. buffer->writeHandle->SerializeCreate(allocatedBuffer + commandSize); return result; } void ClientDeviceCreateBufferMappedAsync(WGPUDevice cDevice, const WGPUBufferDescriptor* descriptor, WGPUBufferCreateMappedCallback callback, void* userdata) { Device* device = reinterpret_cast(cDevice); Client* wireClient = device->GetClient(); auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(device); Buffer* buffer = bufferObjectAndSerial->object.get(); buffer->size = descriptor->size; uint32_t serial = buffer->requestSerial++; struct CreateBufferMappedInfo { WGPUBuffer buffer; WGPUBufferCreateMappedCallback callback; void* userdata; }; CreateBufferMappedInfo* info = new CreateBufferMappedInfo; info->buffer = reinterpret_cast(buffer); info->callback = callback; info->userdata = userdata; // Create a WriteHandle for the map request. This is the client's intent to write GPU // memory. MemoryTransferService::WriteHandle* writeHandle = wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size); if (writeHandle == nullptr) { WGPUCreateBufferMappedResult result; result.buffer = reinterpret_cast(buffer); result.data = nullptr; result.dataLength = 0; callback(WGPUBufferMapAsyncStatus_DeviceLost, result, userdata); return; } Buffer::MapRequestData request; request.writeCallback = [](WGPUBufferMapAsyncStatus status, void* data, uint64_t dataLength, void* userdata) { auto info = std::unique_ptr( static_cast(userdata)); WGPUCreateBufferMappedResult result; result.buffer = info->buffer; result.data = data; result.dataLength = dataLength; info->callback(status, result, info->userdata); }; request.userdata = info; // The handle is owned by the MapRequest until the callback returns. request.writeHandle = std::unique_ptr(writeHandle); buffer->requests[serial] = std::move(request); // Get the serialization size of the WriteHandle. size_t handleCreateInfoLength = writeHandle->SerializeCreateSize(); DeviceCreateBufferMappedAsyncCmd cmd; cmd.device = cDevice; cmd.descriptor = descriptor; cmd.requestSerial = serial; cmd.result = ObjectHandle{buffer->id, bufferObjectAndSerial->serial}; cmd.handleCreateInfoLength = handleCreateInfoLength; cmd.handleCreateInfo = nullptr; size_t commandSize = cmd.GetRequiredSize(); size_t requiredSize = commandSize + handleCreateInfoLength; char* allocatedBuffer = static_cast(wireClient->GetCmdSpace(requiredSize)); cmd.Serialize(allocatedBuffer, *wireClient); // Serialize the WriteHandle into the space after the command. writeHandle->SerializeCreate(allocatedBuffer + commandSize); } void ClientDevicePushErrorScope(WGPUDevice cDevice, WGPUErrorFilter filter) { Device* device = reinterpret_cast(cDevice); device->PushErrorScope(filter); } bool ClientDevicePopErrorScope(WGPUDevice cDevice, WGPUErrorCallback callback, void* userdata) { Device* device = reinterpret_cast(cDevice); return device->RequestPopErrorScope(callback, userdata); } uint64_t ClientFenceGetCompletedValue(WGPUFence cSelf) { auto fence = reinterpret_cast(cSelf); return fence->completedValue; } void ClientFenceOnCompletion(WGPUFence cFence, uint64_t value, WGPUFenceOnCompletionCallback callback, void* userdata) { Fence* fence = reinterpret_cast(cFence); if (value > fence->signaledValue) { ClientDeviceInjectError(reinterpret_cast(fence->device), WGPUErrorType_Validation, "Value greater than fence signaled value"); callback(WGPUFenceCompletionStatus_Error, userdata); return; } if (value <= fence->completedValue) { callback(WGPUFenceCompletionStatus_Success, userdata); return; } Fence::OnCompletionData request; request.completionCallback = callback; request.userdata = userdata; fence->requests.Enqueue(std::move(request), value); } void ClientBufferSetSubData(WGPUBuffer cBuffer, uint64_t start, uint64_t count, const void* data) { Buffer* buffer = reinterpret_cast(cBuffer); BufferSetSubDataInternalCmd cmd; cmd.bufferId = buffer->id; cmd.start = start; cmd.count = count; cmd.data = static_cast(data); Client* wireClient = buffer->device->GetClient(); size_t requiredSize = cmd.GetRequiredSize(); char* allocatedBuffer = static_cast(wireClient->GetCmdSpace(requiredSize)); cmd.Serialize(allocatedBuffer); } void ClientBufferUnmap(WGPUBuffer cBuffer) { Buffer* buffer = reinterpret_cast(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; size_t commandSize = cmd.GetRequiredSize(); size_t requiredSize = commandSize + writeFlushInfoLength; char* allocatedBuffer = static_cast(buffer->device->GetClient()->GetCmdSpace(requiredSize)); cmd.Serialize(allocatedBuffer); // Serialize flush metadata into the space after the command. // This closes the handle for writing. buffer->writeHandle->SerializeFlush(allocatedBuffer + commandSize); buffer->writeHandle = nullptr; } else if (buffer->readHandle) { buffer->readHandle = nullptr; } buffer->ClearMapRequests(WGPUBufferMapAsyncStatus_Unknown); BufferUnmapCmd cmd; cmd.self = cBuffer; size_t requiredSize = cmd.GetRequiredSize(); char* allocatedBuffer = static_cast(buffer->device->GetClient()->GetCmdSpace(requiredSize)); cmd.Serialize(allocatedBuffer, *buffer->device->GetClient()); } WGPUFence ClientQueueCreateFence(WGPUQueue cSelf, WGPUFenceDescriptor const* descriptor) { Queue* queue = reinterpret_cast(cSelf); Device* device = queue->device; QueueCreateFenceCmd cmd; cmd.self = cSelf; auto* allocation = device->GetClient()->FenceAllocator().New(device); cmd.result = ObjectHandle{allocation->object->id, allocation->serial}; cmd.descriptor = descriptor; size_t requiredSize = cmd.GetRequiredSize(); char* allocatedBuffer = static_cast(device->GetClient()->GetCmdSpace(requiredSize)); cmd.Serialize(allocatedBuffer, *device->GetClient()); WGPUFence cFence = reinterpret_cast(allocation->object.get()); Fence* fence = reinterpret_cast(cFence); fence->queue = queue; fence->signaledValue = descriptor->initialValue; fence->completedValue = descriptor->initialValue; return cFence; } void ClientQueueSignal(WGPUQueue cQueue, WGPUFence cFence, uint64_t signalValue) { Fence* fence = reinterpret_cast(cFence); Queue* queue = reinterpret_cast(cQueue); if (fence->queue != queue) { ClientDeviceInjectError(reinterpret_cast(fence->device), WGPUErrorType_Validation, "Fence must be signaled on the queue on which it was created."); return; } if (signalValue <= fence->signaledValue) { ClientDeviceInjectError(reinterpret_cast(fence->device), WGPUErrorType_Validation, "Fence value less than or equal to signaled value"); return; } fence->signaledValue = signalValue; QueueSignalCmd cmd; cmd.self = cQueue; cmd.fence = cFence; cmd.signalValue = signalValue; size_t requiredSize = cmd.GetRequiredSize(); char* allocatedBuffer = static_cast(fence->device->GetClient()->GetCmdSpace(requiredSize)); cmd.Serialize(allocatedBuffer, *fence->device->GetClient()); } void ClientDeviceReference(WGPUDevice) { } void ClientDeviceRelease(WGPUDevice) { } void ClientDeviceSetUncapturedErrorCallback(WGPUDevice cSelf, WGPUErrorCallback callback, void* userdata) { Device* device = reinterpret_cast(cSelf); device->SetUncapturedErrorCallback(callback, userdata); } }} // namespace dawn_wire::client