mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-08 15:43:41 +00:00
BUG=dawn:22 Change-Id: I4963aa27322086e74947a1a6265c921e2c7d3d85 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/12700 Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Corentin Wallez <cwallez@chromium.org>
436 lines
18 KiB
C++
436 lines
18 KiB
C++
// 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 <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;
|
|
|
|
size_t commandSize = cmd.GetRequiredSize();
|
|
size_t requiredSize = commandSize + handleCreateInfoLength;
|
|
char* allocatedBuffer =
|
|
static_cast<char*>(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<Buffer*>(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<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 ClientBufferMapWriteAsync(WGPUBuffer cBuffer,
|
|
WGPUBufferMapWriteCallback callback,
|
|
void* userdata) {
|
|
Buffer* buffer = reinterpret_cast<Buffer*>(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<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 ClientDeviceCreateBuffer(WGPUDevice cDevice,
|
|
const WGPUBufferDescriptor* descriptor) {
|
|
Device* device = reinterpret_cast<Device*>(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<char*>(wireClient->GetCmdSpace(requiredSize));
|
|
cmd.Serialize(allocatedBuffer, *wireClient);
|
|
|
|
return reinterpret_cast<WGPUBuffer>(buffer);
|
|
}
|
|
|
|
WGPUCreateBufferMappedResult ClientDeviceCreateBufferMapped(
|
|
WGPUDevice cDevice,
|
|
const WGPUBufferDescriptor* descriptor) {
|
|
Device* device = reinterpret_cast<Device*>(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<WGPUBuffer>(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<MemoryTransferService::WriteHandle> writeHandle =
|
|
std::unique_ptr<MemoryTransferService::WriteHandle>(
|
|
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<char*>(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<Device*>(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<WGPUBuffer>(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<WGPUBuffer>(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<CreateBufferMappedInfo>(
|
|
static_cast<CreateBufferMappedInfo*>(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<MemoryTransferService::WriteHandle>(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<char*>(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<Device*>(cDevice);
|
|
device->PushErrorScope(filter);
|
|
}
|
|
|
|
bool ClientDevicePopErrorScope(WGPUDevice cDevice, WGPUErrorCallback callback, void* userdata) {
|
|
Device* device = reinterpret_cast<Device*>(cDevice);
|
|
return device->RequestPopErrorScope(callback, userdata);
|
|
}
|
|
|
|
uint64_t ClientFenceGetCompletedValue(WGPUFence cSelf) {
|
|
auto fence = reinterpret_cast<Fence*>(cSelf);
|
|
return fence->completedValue;
|
|
}
|
|
|
|
void ClientFenceOnCompletion(WGPUFence cFence,
|
|
uint64_t value,
|
|
WGPUFenceOnCompletionCallback callback,
|
|
void* userdata) {
|
|
Fence* fence = reinterpret_cast<Fence*>(cFence);
|
|
if (value > fence->signaledValue) {
|
|
ClientDeviceInjectError(reinterpret_cast<WGPUDevice>(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<Buffer*>(cBuffer);
|
|
|
|
BufferSetSubDataInternalCmd cmd;
|
|
cmd.bufferId = buffer->id;
|
|
cmd.start = start;
|
|
cmd.count = count;
|
|
cmd.data = static_cast<const uint8_t*>(data);
|
|
|
|
Client* wireClient = buffer->device->GetClient();
|
|
size_t requiredSize = cmd.GetRequiredSize();
|
|
char* allocatedBuffer = static_cast<char*>(wireClient->GetCmdSpace(requiredSize));
|
|
cmd.Serialize(allocatedBuffer);
|
|
}
|
|
|
|
void ClientBufferUnmap(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;
|
|
|
|
size_t commandSize = cmd.GetRequiredSize();
|
|
size_t requiredSize = commandSize + writeFlushInfoLength;
|
|
char* allocatedBuffer =
|
|
static_cast<char*>(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<char*>(buffer->device->GetClient()->GetCmdSpace(requiredSize));
|
|
cmd.Serialize(allocatedBuffer, *buffer->device->GetClient());
|
|
}
|
|
|
|
WGPUFence ClientQueueCreateFence(WGPUQueue cSelf, WGPUFenceDescriptor const* descriptor) {
|
|
Queue* queue = reinterpret_cast<Queue*>(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<char*>(device->GetClient()->GetCmdSpace(requiredSize));
|
|
cmd.Serialize(allocatedBuffer, *device->GetClient());
|
|
|
|
WGPUFence cFence = reinterpret_cast<WGPUFence>(allocation->object.get());
|
|
|
|
Fence* fence = reinterpret_cast<Fence*>(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<Fence*>(cFence);
|
|
Queue* queue = reinterpret_cast<Queue*>(cQueue);
|
|
if (fence->queue != queue) {
|
|
ClientDeviceInjectError(reinterpret_cast<WGPUDevice>(fence->device),
|
|
WGPUErrorType_Validation,
|
|
"Fence must be signaled on the queue on which it was created.");
|
|
return;
|
|
}
|
|
if (signalValue <= fence->signaledValue) {
|
|
ClientDeviceInjectError(reinterpret_cast<WGPUDevice>(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<char*>(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<Device*>(cSelf);
|
|
device->SetUncapturedErrorCallback(callback, userdata);
|
|
}
|
|
|
|
}} // namespace dawn_wire::client
|