dawn_wire: Make all objects owned by the client
This removes the logic where the Client owns the Device and the Device owns all other objects. Ownership should be tracked in dawn_native either with refcounting or validation to disallow operations after an object's parent has been destroyed. This simplifies the wire client code in that the client only tracks allocated handles and does not manage parent/child lifetimes. This is an important simplification so we can support multiple WebGPU instances, adapters, and devices on a single wire. Bug: dawn:384 Change-Id: I8ecc7c368130b8917202150c467b5f0e7d4b753e Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/37000 Commit-Queue: Austin Eng <enga@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
parent
f6ef7530ab
commit
f0d7cc4f5a
|
@ -34,14 +34,14 @@ namespace dawn_wire { namespace client {
|
|||
{%- endmacro %}
|
||||
|
||||
{% for type in by_category["object"] %}
|
||||
DAWN_DECLARE_UNUSED bool DeviceMatches(const Device* device, const {{as_cType(type.name)}} obj) {
|
||||
return device == reinterpret_cast<const {{as_wireType(type)}}>(obj)->device;
|
||||
DAWN_DECLARE_UNUSED bool ClientMatches(const Client* client, const {{as_cType(type.name)}} obj) {
|
||||
return client == reinterpret_cast<const {{as_wireType(type)}}>(obj)->client;
|
||||
}
|
||||
|
||||
DAWN_DECLARE_UNUSED bool DeviceMatches(const Device* device, const {{as_cType(type.name)}} *const obj, uint32_t count = 1) {
|
||||
DAWN_DECLARE_UNUSED bool ClientMatches(const Client* client, const {{as_cType(type.name)}} *const obj, uint32_t count = 1) {
|
||||
ASSERT(count == 0 || obj != nullptr);
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
if (!DeviceMatches(device, obj[i])) {
|
||||
if (!ClientMatches(client, obj[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -49,12 +49,12 @@ namespace dawn_wire { namespace client {
|
|||
}
|
||||
{% endfor %}
|
||||
|
||||
bool DeviceMatches(const Device* device, WGPUChainedStruct const* chainedStruct);
|
||||
bool ClientMatches(const Client* client, WGPUChainedStruct const* chainedStruct);
|
||||
|
||||
{% for type in by_category["structure"] if type.may_have_dawn_object %}
|
||||
DAWN_DECLARE_UNUSED bool DeviceMatches(const Device* device, const {{as_cType(type.name)}}& obj) {
|
||||
DAWN_DECLARE_UNUSED bool ClientMatches(const Client* client, const {{as_cType(type.name)}}& obj) {
|
||||
{% if type.extensible %}
|
||||
if (!DeviceMatches(device, obj.nextInChain)) {
|
||||
if (!ClientMatches(client, obj.nextInChain)) {
|
||||
return false;
|
||||
}
|
||||
{% endif %}
|
||||
|
@ -63,7 +63,7 @@ namespace dawn_wire { namespace client {
|
|||
if (obj.{{as_varName(member.name)}} != nullptr)
|
||||
{% endif %}
|
||||
{
|
||||
if (!DeviceMatches(device, obj.{{as_varName(member.name)}}
|
||||
if (!ClientMatches(client, obj.{{as_varName(member.name)}}
|
||||
{%- if member.annotation != "value" and member.length != "strlen" -%}
|
||||
, {{member_length(member, "obj.")}}
|
||||
{%- endif -%})) {
|
||||
|
@ -74,9 +74,9 @@ namespace dawn_wire { namespace client {
|
|||
return true;
|
||||
}
|
||||
|
||||
DAWN_DECLARE_UNUSED bool DeviceMatches(const Device* device, const {{as_cType(type.name)}} *const obj, uint32_t count = 1) {
|
||||
DAWN_DECLARE_UNUSED bool ClientMatches(const Client* client, const {{as_cType(type.name)}} *const obj, uint32_t count = 1) {
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
if (!DeviceMatches(device, obj[i])) {
|
||||
if (!ClientMatches(client, obj[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -84,14 +84,14 @@ namespace dawn_wire { namespace client {
|
|||
}
|
||||
{% endfor %}
|
||||
|
||||
bool DeviceMatches(const Device* device, WGPUChainedStruct const* chainedStruct) {
|
||||
bool ClientMatches(const Client* client, WGPUChainedStruct const* chainedStruct) {
|
||||
while (chainedStruct != nullptr) {
|
||||
switch (chainedStruct->sType) {
|
||||
{% for sType in types["s type"].values if sType.valid %}
|
||||
{% set CType = as_cType(sType.name) %}
|
||||
case {{as_cEnum(types["s type"].name, sType.name)}}: {
|
||||
{% if types[sType.name.get()].may_have_dawn_object %}
|
||||
if (!DeviceMatches(device, reinterpret_cast<const {{CType}}*>(chainedStruct))) {
|
||||
if (!ClientMatches(client, reinterpret_cast<const {{CType}}*>(chainedStruct))) {
|
||||
return false;
|
||||
}
|
||||
{% endif %}
|
||||
|
@ -103,7 +103,7 @@ namespace dawn_wire { namespace client {
|
|||
default:
|
||||
UNREACHABLE();
|
||||
dawn::WarningLog()
|
||||
<< "All objects may not be from the same device. "
|
||||
<< "All objects may not be from the same client. "
|
||||
<< "Unknown sType " << chainedStruct->sType << " discarded.";
|
||||
return false;
|
||||
}
|
||||
|
@ -133,10 +133,10 @@ namespace dawn_wire { namespace client {
|
|||
) {
|
||||
{% if len(method.arguments) > 0 %}
|
||||
{
|
||||
bool sameDevice = true;
|
||||
bool sameClient = true;
|
||||
auto self = reinterpret_cast<{{as_wireType(type)}}>(cSelf);
|
||||
Device* device = self->device;
|
||||
DAWN_UNUSED(device);
|
||||
Client* client = self->client;
|
||||
DAWN_UNUSED(client);
|
||||
|
||||
do {
|
||||
{% for arg in method.arguments if arg.type.may_have_dawn_object or arg.type.category == "object" %}
|
||||
|
@ -144,26 +144,26 @@ namespace dawn_wire { namespace client {
|
|||
if ({{as_varName(arg.name)}} != nullptr)
|
||||
{% endif %}
|
||||
{
|
||||
if (!DeviceMatches(device, {{as_varName(arg.name)}}
|
||||
if (!ClientMatches(client, {{as_varName(arg.name)}}
|
||||
{%- if arg.annotation != "value" and arg.length != "strlen" -%}
|
||||
, {{member_length(arg, "")}}
|
||||
{%- endif -%})) {
|
||||
sameDevice = false;
|
||||
sameClient = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
{% endfor %}
|
||||
} while (false);
|
||||
|
||||
if (DAWN_UNLIKELY(!sameDevice)) {
|
||||
device->InjectError(WGPUErrorType_Validation,
|
||||
if (DAWN_UNLIKELY(!sameClient)) {
|
||||
reinterpret_cast<Device*>(client->GetDevice())->InjectError(WGPUErrorType_Validation,
|
||||
"All objects must be from the same device.");
|
||||
{% if method.return_type.category == "object" %}
|
||||
// Allocate an object without registering it on the server. This is backed by a real allocation on
|
||||
// the client so commands can be sent with it. But because it's not allocated on the server, it will
|
||||
// be a fatal error to use it.
|
||||
auto self = reinterpret_cast<{{as_wireType(type)}}>(cSelf);
|
||||
auto* allocation = self->device->GetClient()->{{method.return_type.name.CamelCase()}}Allocator().New(self->device);
|
||||
auto* allocation = self->client->{{method.return_type.name.CamelCase()}}Allocator().New(self->client);
|
||||
return reinterpret_cast<{{as_cType(method.return_type.name)}}>(allocation->object.get());
|
||||
{% elif method.return_type.name.canonical_case() == "void" %}
|
||||
return;
|
||||
|
@ -176,7 +176,6 @@ namespace dawn_wire { namespace client {
|
|||
|
||||
auto self = reinterpret_cast<{{as_wireType(type)}}>(cSelf);
|
||||
{% if Suffix not in client_handwritten_commands %}
|
||||
Device* device = self->device;
|
||||
{{Suffix}}Cmd cmd;
|
||||
|
||||
//* Create the structure going on the wire on the stack and fill it with the value
|
||||
|
@ -185,7 +184,7 @@ namespace dawn_wire { namespace client {
|
|||
|
||||
//* For object creation, store the object ID the client will use for the result.
|
||||
{% if method.return_type.category == "object" %}
|
||||
auto* allocation = self->device->GetClient()->{{method.return_type.name.CamelCase()}}Allocator().New(self->device);
|
||||
auto* allocation = self->client->{{method.return_type.name.CamelCase()}}Allocator().New(self->client);
|
||||
cmd.result = ObjectHandle{allocation->object->id, allocation->generation};
|
||||
{% endif %}
|
||||
|
||||
|
@ -194,7 +193,7 @@ namespace dawn_wire { namespace client {
|
|||
{% endfor %}
|
||||
|
||||
//* Allocate space to send the command and copy the value args over.
|
||||
device->GetClient()->SerializeCommand(cmd);
|
||||
self->client->SerializeCommand(cmd);
|
||||
|
||||
{% if method.return_type.category == "object" %}
|
||||
return reinterpret_cast<{{as_cType(method.return_type.name)}}>(allocation->object.get());
|
||||
|
@ -222,8 +221,8 @@ namespace dawn_wire { namespace client {
|
|||
cmd.objectType = ObjectType::{{type.name.CamelCase()}};
|
||||
cmd.objectId = obj->id;
|
||||
|
||||
obj->device->GetClient()->SerializeCommand(cmd);
|
||||
obj->device->GetClient()->{{type.name.CamelCase()}}Allocator().Free(obj);
|
||||
obj->client->SerializeCommand(cmd);
|
||||
obj->client->{{type.name.CamelCase()}}Allocator().Free(obj);
|
||||
}
|
||||
|
||||
void Client{{as_MethodSuffix(type.name, Name("reference"))}}({{cType}} cObj) {
|
||||
|
|
|
@ -20,15 +20,15 @@
|
|||
namespace dawn_wire { namespace client {
|
||||
|
||||
// static
|
||||
WGPUBuffer Buffer::Create(Device* device_, const WGPUBufferDescriptor* descriptor) {
|
||||
Client* wireClient = device_->GetClient();
|
||||
WGPUBuffer Buffer::Create(Device* device, const WGPUBufferDescriptor* descriptor) {
|
||||
Client* wireClient = device->client;
|
||||
|
||||
bool mappable =
|
||||
(descriptor->usage & (WGPUBufferUsage_MapRead | WGPUBufferUsage_MapWrite)) != 0 ||
|
||||
descriptor->mappedAtCreation;
|
||||
if (mappable && descriptor->size >= std::numeric_limits<size_t>::max()) {
|
||||
device_->InjectError(WGPUErrorType_OutOfMemory, "Buffer is too large for map usage");
|
||||
return device_->CreateErrorBuffer();
|
||||
device->InjectError(WGPUErrorType_OutOfMemory, "Buffer is too large for map usage");
|
||||
return device->CreateErrorBuffer();
|
||||
}
|
||||
|
||||
std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
|
||||
|
@ -42,16 +42,16 @@ namespace dawn_wire { namespace client {
|
|||
writeHandle.reset(
|
||||
wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size));
|
||||
if (writeHandle == nullptr) {
|
||||
device_->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed");
|
||||
return device_->CreateErrorBuffer();
|
||||
device->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed");
|
||||
return device->CreateErrorBuffer();
|
||||
}
|
||||
|
||||
// Open the handle, it may fail by returning a nullptr in writeData.
|
||||
size_t writeDataLength = 0;
|
||||
std::tie(writeData, writeDataLength) = writeHandle->Open();
|
||||
if (writeData == nullptr) {
|
||||
device_->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed");
|
||||
return device_->CreateErrorBuffer();
|
||||
device->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed");
|
||||
return device->CreateErrorBuffer();
|
||||
}
|
||||
ASSERT(writeDataLength == descriptor->size);
|
||||
|
||||
|
@ -60,12 +60,13 @@ namespace dawn_wire { namespace client {
|
|||
}
|
||||
|
||||
// Create the buffer and send the creation command.
|
||||
auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(device_);
|
||||
auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(wireClient);
|
||||
Buffer* buffer = bufferObjectAndSerial->object.get();
|
||||
buffer->mDevice = device;
|
||||
buffer->mSize = descriptor->size;
|
||||
|
||||
DeviceCreateBufferCmd cmd;
|
||||
cmd.device = ToAPI(device_);
|
||||
cmd.device = ToAPI(device);
|
||||
cmd.descriptor = descriptor;
|
||||
cmd.result = ObjectHandle{buffer->id, bufferObjectAndSerial->generation};
|
||||
cmd.handleCreateInfoLength = writeHandleCreateInfoLength;
|
||||
|
@ -88,13 +89,14 @@ namespace dawn_wire { namespace client {
|
|||
}
|
||||
|
||||
// static
|
||||
WGPUBuffer Buffer::CreateError(Device* device_) {
|
||||
auto* allocation = device_->GetClient()->BufferAllocator().New(device_);
|
||||
WGPUBuffer Buffer::CreateError(Device* device) {
|
||||
auto* allocation = device->client->BufferAllocator().New(device->client);
|
||||
allocation->object->mDevice = device;
|
||||
|
||||
DeviceCreateErrorBufferCmd cmd;
|
||||
cmd.self = ToAPI(device_);
|
||||
cmd.self = ToAPI(device);
|
||||
cmd.result = ObjectHandle{allocation->object->id, allocation->generation};
|
||||
device_->GetClient()->SerializeCommand(cmd);
|
||||
device->client->SerializeCommand(cmd);
|
||||
|
||||
return ToAPI(allocation->object.get());
|
||||
}
|
||||
|
@ -124,7 +126,7 @@ namespace dawn_wire { namespace client {
|
|||
size_t size,
|
||||
WGPUBufferMapCallback callback,
|
||||
void* userdata) {
|
||||
if (device->GetClient()->IsDisconnected()) {
|
||||
if (client->IsDisconnected()) {
|
||||
return callback(WGPUBufferMapAsyncStatus_DeviceLost, userdata);
|
||||
}
|
||||
|
||||
|
@ -138,7 +140,8 @@ namespace dawn_wire { namespace client {
|
|||
|
||||
// Step 1. Do early validation of READ ^ WRITE because the server rejects mode = 0.
|
||||
if (!(isReadMode ^ isWriteMode)) {
|
||||
device->InjectError(WGPUErrorType_Validation, "MapAsync error (you figure out :P)");
|
||||
mDevice->InjectError(WGPUErrorType_Validation,
|
||||
"MapAsync mode must be exactly one of Read or Write");
|
||||
if (callback != nullptr) {
|
||||
callback(WGPUBufferMapAsyncStatus_Error, userdata);
|
||||
}
|
||||
|
@ -158,19 +161,17 @@ namespace dawn_wire { namespace client {
|
|||
|
||||
// Step 2a: Create the read / write handles for this request.
|
||||
if (isReadMode) {
|
||||
request.readHandle.reset(
|
||||
device->GetClient()->GetMemoryTransferService()->CreateReadHandle(size));
|
||||
request.readHandle.reset(client->GetMemoryTransferService()->CreateReadHandle(size));
|
||||
if (request.readHandle == nullptr) {
|
||||
device->InjectError(WGPUErrorType_OutOfMemory, "Failed to create buffer mapping");
|
||||
mDevice->InjectError(WGPUErrorType_OutOfMemory, "Failed to create buffer mapping");
|
||||
callback(WGPUBufferMapAsyncStatus_Error, userdata);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ASSERT(isWriteMode);
|
||||
request.writeHandle.reset(
|
||||
device->GetClient()->GetMemoryTransferService()->CreateWriteHandle(size));
|
||||
request.writeHandle.reset(client->GetMemoryTransferService()->CreateWriteHandle(size));
|
||||
if (request.writeHandle == nullptr) {
|
||||
device->InjectError(WGPUErrorType_OutOfMemory, "Failed to create buffer mapping");
|
||||
mDevice->InjectError(WGPUErrorType_OutOfMemory, "Failed to create buffer mapping");
|
||||
callback(WGPUBufferMapAsyncStatus_Error, userdata);
|
||||
return;
|
||||
}
|
||||
|
@ -188,15 +189,15 @@ namespace dawn_wire { namespace client {
|
|||
// Step 3a. Fill the handle create info in the command.
|
||||
if (isReadMode) {
|
||||
cmd.handleCreateInfoLength = request.readHandle->SerializeCreateSize();
|
||||
device->GetClient()->SerializeCommand(
|
||||
cmd, cmd.handleCreateInfoLength,
|
||||
[&](char* cmdSpace) { request.readHandle->SerializeCreate(cmdSpace); });
|
||||
client->SerializeCommand(cmd, cmd.handleCreateInfoLength, [&](char* cmdSpace) {
|
||||
request.readHandle->SerializeCreate(cmdSpace);
|
||||
});
|
||||
} else {
|
||||
ASSERT(isWriteMode);
|
||||
cmd.handleCreateInfoLength = request.writeHandle->SerializeCreateSize();
|
||||
device->GetClient()->SerializeCommand(
|
||||
cmd, cmd.handleCreateInfoLength,
|
||||
[&](char* cmdSpace) { request.writeHandle->SerializeCreate(cmdSpace); });
|
||||
client->SerializeCommand(cmd, cmd.handleCreateInfoLength, [&](char* cmdSpace) {
|
||||
request.writeHandle->SerializeCreate(cmdSpace);
|
||||
});
|
||||
}
|
||||
|
||||
// Step 4. Register this request so that we can retrieve it from its serial when the server
|
||||
|
@ -323,7 +324,7 @@ namespace dawn_wire { namespace client {
|
|||
cmd.writeFlushInfoLength = writeFlushInfoLength;
|
||||
cmd.writeFlushInfo = nullptr;
|
||||
|
||||
device->GetClient()->SerializeCommand(cmd, writeFlushInfoLength, [&](char* cmdSpace) {
|
||||
client->SerializeCommand(cmd, writeFlushInfoLength, [&](char* cmdSpace) {
|
||||
// Serialize flush metadata into the space after the command.
|
||||
// This closes the handle for writing.
|
||||
mWriteHandle->SerializeFlush(cmdSpace);
|
||||
|
@ -347,7 +348,7 @@ namespace dawn_wire { namespace client {
|
|||
|
||||
BufferUnmapCmd cmd;
|
||||
cmd.self = ToAPI(this);
|
||||
device->GetClient()->SerializeCommand(cmd);
|
||||
client->SerializeCommand(cmd);
|
||||
}
|
||||
|
||||
void Buffer::Destroy() {
|
||||
|
@ -365,7 +366,7 @@ namespace dawn_wire { namespace client {
|
|||
|
||||
BufferDestroyCmd cmd;
|
||||
cmd.self = ToAPI(this);
|
||||
device->GetClient()->SerializeCommand(cmd);
|
||||
client->SerializeCommand(cmd);
|
||||
}
|
||||
|
||||
bool Buffer::IsMappedForReading() const {
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
namespace dawn_wire { namespace client {
|
||||
|
||||
class Device;
|
||||
|
||||
class Buffer final : public ObjectBase {
|
||||
public:
|
||||
using ObjectBase::ObjectBase;
|
||||
|
@ -55,6 +57,8 @@ namespace dawn_wire { namespace client {
|
|||
bool IsMappedForWriting() const;
|
||||
bool CheckGetMappedRangeOffsetSize(size_t offset, size_t size) const;
|
||||
|
||||
Device* mDevice;
|
||||
|
||||
// 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.
|
||||
|
|
|
@ -57,10 +57,23 @@ namespace dawn_wire { namespace client {
|
|||
}
|
||||
|
||||
void Client::DestroyAllObjects() {
|
||||
while (!mDevices.empty()) {
|
||||
// Note: We don't send a DestroyObject command for the device
|
||||
// since freeing a device object is done out of band.
|
||||
DeviceAllocator().Free(static_cast<Device*>(mDevices.head()->value()));
|
||||
for (auto& objectList : mObjects) {
|
||||
ObjectType objectType = static_cast<ObjectType>(&objectList - mObjects.data());
|
||||
while (!objectList.empty()) {
|
||||
ObjectBase* object = objectList.head()->value();
|
||||
if (object == mDevice) {
|
||||
// Note: We don't send a DestroyObject command for the device
|
||||
// since freeing a device object is done out of band.
|
||||
DeviceAllocator().Free(mDevice);
|
||||
continue;
|
||||
}
|
||||
|
||||
DestroyObjectCmd cmd;
|
||||
cmd.objectType = objectType;
|
||||
cmd.objectId = object->id;
|
||||
SerializeCommand(cmd);
|
||||
FreeObject(objectType, object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,9 +84,8 @@ namespace dawn_wire { namespace client {
|
|||
return reinterpret_cast<WGPUDeviceImpl*>(mDevice);
|
||||
}
|
||||
|
||||
ReservedTexture Client::ReserveTexture(WGPUDevice cDevice) {
|
||||
Device* device = FromAPI(cDevice);
|
||||
auto* allocation = TextureAllocator().New(device);
|
||||
ReservedTexture Client::ReserveTexture(WGPUDevice device) {
|
||||
auto* allocation = TextureAllocator().New(this);
|
||||
|
||||
ReservedTexture result;
|
||||
result.texture = ToAPI(allocation->object.get());
|
||||
|
@ -87,12 +99,14 @@ namespace dawn_wire { namespace client {
|
|||
mSerializer = ChunkedCommandSerializer(NoopCommandSerializer::GetInstance());
|
||||
if (mDevice != nullptr) {
|
||||
mDevice->HandleDeviceLost("GPU connection lost");
|
||||
mDevice->CancelCallbacksForDisconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void Client::TrackObject(Device* device) {
|
||||
mDevices.Append(device);
|
||||
for (auto& objectList : mObjects) {
|
||||
LinkNode<ObjectBase>* object = objectList.head();
|
||||
while (object != objectList.end()) {
|
||||
object->value()->CancelCallbacksForDisconnect();
|
||||
object = object->next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Client::IsDisconnected() const {
|
||||
|
|
|
@ -62,7 +62,10 @@ namespace dawn_wire { namespace client {
|
|||
void Disconnect();
|
||||
bool IsDisconnected() const;
|
||||
|
||||
void TrackObject(Device* device);
|
||||
template <typename T>
|
||||
void TrackObject(T* object) {
|
||||
mObjects[ObjectTypeToTypeEnum<T>::value].Append(object);
|
||||
}
|
||||
|
||||
private:
|
||||
void DestroyAllObjects();
|
||||
|
@ -75,7 +78,7 @@ namespace dawn_wire { namespace client {
|
|||
MemoryTransferService* mMemoryTransferService = nullptr;
|
||||
std::unique_ptr<MemoryTransferService> mOwnedMemoryTransferService = nullptr;
|
||||
|
||||
LinkedList<ObjectBase> mDevices;
|
||||
PerObjectType<LinkedList<ObjectBase>> mObjects;
|
||||
bool mDisconnected = false;
|
||||
};
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
|
||||
namespace dawn_wire { namespace client {
|
||||
|
||||
Device::Device(Client* client, uint32_t initialRefcount, uint32_t initialId)
|
||||
: ObjectBase(this, initialRefcount, initialId), mClient(client) {
|
||||
Device::Device(Client* clientIn, uint32_t initialRefcount, uint32_t initialId)
|
||||
: ObjectBase(clientIn, initialRefcount, initialId) {
|
||||
#if defined(DAWN_ENABLE_ASSERTS)
|
||||
mErrorCallback = [](WGPUErrorType, char const*, void*) {
|
||||
static bool calledOnce = false;
|
||||
|
@ -46,14 +46,14 @@ namespace dawn_wire { namespace client {
|
|||
};
|
||||
#endif // DAWN_ENABLE_ASSERTS
|
||||
// Get the default queue for this device.
|
||||
auto* allocation = mClient->QueueAllocator().New(this);
|
||||
auto* allocation = client->QueueAllocator().New(client);
|
||||
mDefaultQueue = allocation->object.get();
|
||||
|
||||
DeviceGetDefaultQueueCmd cmd;
|
||||
cmd.self = ToAPI(this);
|
||||
cmd.result = ObjectHandle{allocation->object->id, allocation->generation};
|
||||
|
||||
mClient->SerializeCommand(cmd);
|
||||
client->SerializeCommand(cmd);
|
||||
}
|
||||
|
||||
Device::~Device() {
|
||||
|
@ -77,27 +77,6 @@ namespace dawn_wire { namespace client {
|
|||
"Device destroyed before callback", it.second.userdata);
|
||||
}
|
||||
}
|
||||
|
||||
DestroyAllObjects();
|
||||
}
|
||||
|
||||
void Device::DestroyAllObjects() {
|
||||
for (auto& objectList : mObjects) {
|
||||
ObjectType objectType = static_cast<ObjectType>(&objectList - mObjects.data());
|
||||
while (!objectList.empty()) {
|
||||
ObjectBase* object = objectList.head()->value();
|
||||
|
||||
DestroyObjectCmd cmd;
|
||||
cmd.objectType = objectType;
|
||||
cmd.objectId = object->id;
|
||||
mClient->SerializeCommand(cmd);
|
||||
mClient->FreeObject(objectType, object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Client* Device::GetClient() {
|
||||
return mClient;
|
||||
}
|
||||
|
||||
void Device::HandleError(WGPUErrorType errorType, const char* message) {
|
||||
|
@ -133,14 +112,6 @@ namespace dawn_wire { namespace client {
|
|||
it.second.callback(WGPUErrorType_DeviceLost, "Device lost", it.second.userdata);
|
||||
}
|
||||
mErrorScopes.clear();
|
||||
|
||||
for (auto& objectList : mObjects) {
|
||||
LinkNode<ObjectBase>* object = objectList.head();
|
||||
while (object != objectList.end()) {
|
||||
object->value()->CancelCallbacksForDisconnect();
|
||||
object = object->next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Device::SetUncapturedErrorCallback(WGPUErrorCallback errorCallback, void* errorUserdata) {
|
||||
|
@ -160,7 +131,7 @@ namespace dawn_wire { namespace client {
|
|||
cmd.self = ToAPI(this);
|
||||
cmd.filter = filter;
|
||||
|
||||
mClient->SerializeCommand(cmd);
|
||||
client->SerializeCommand(cmd);
|
||||
}
|
||||
|
||||
bool Device::PopErrorScope(WGPUErrorCallback callback, void* userdata) {
|
||||
|
@ -169,7 +140,7 @@ namespace dawn_wire { namespace client {
|
|||
}
|
||||
mErrorScopeStackSize--;
|
||||
|
||||
if (GetClient()->IsDisconnected()) {
|
||||
if (client->IsDisconnected()) {
|
||||
callback(WGPUErrorType_DeviceLost, "GPU device disconnected", userdata);
|
||||
return true;
|
||||
}
|
||||
|
@ -183,7 +154,7 @@ namespace dawn_wire { namespace client {
|
|||
cmd.device = ToAPI(this);
|
||||
cmd.requestSerial = serial;
|
||||
|
||||
mClient->SerializeCommand(cmd);
|
||||
client->SerializeCommand(cmd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -219,7 +190,7 @@ namespace dawn_wire { namespace client {
|
|||
cmd.self = ToAPI(this);
|
||||
cmd.type = type;
|
||||
cmd.message = message;
|
||||
mClient->SerializeCommand(cmd);
|
||||
client->SerializeCommand(cmd);
|
||||
}
|
||||
|
||||
WGPUBuffer Device::CreateBuffer(const WGPUBufferDescriptor* descriptor) {
|
||||
|
@ -238,7 +209,7 @@ namespace dawn_wire { namespace client {
|
|||
void Device::CreateReadyComputePipeline(WGPUComputePipelineDescriptor const* descriptor,
|
||||
WGPUCreateReadyComputePipelineCallback callback,
|
||||
void* userdata) {
|
||||
if (device->GetClient()->IsDisconnected()) {
|
||||
if (client->IsDisconnected()) {
|
||||
return callback(WGPUCreateReadyPipelineStatus_DeviceLost, nullptr,
|
||||
"GPU device disconnected", userdata);
|
||||
}
|
||||
|
@ -251,14 +222,14 @@ namespace dawn_wire { namespace client {
|
|||
ASSERT(mCreateReadyPipelineRequests.find(serial) == mCreateReadyPipelineRequests.end());
|
||||
cmd.requestSerial = serial;
|
||||
|
||||
auto* allocation = GetClient()->ComputePipelineAllocator().New(this);
|
||||
auto* allocation = client->ComputePipelineAllocator().New(client);
|
||||
CreateReadyPipelineRequest request = {};
|
||||
request.createReadyComputePipelineCallback = callback;
|
||||
request.userdata = userdata;
|
||||
request.pipelineObjectID = allocation->object->id;
|
||||
|
||||
cmd.pipelineObjectHandle = ObjectHandle{allocation->object->id, allocation->generation};
|
||||
GetClient()->SerializeCommand(cmd);
|
||||
client->SerializeCommand(cmd);
|
||||
|
||||
mCreateReadyPipelineRequests[serial] = std::move(request);
|
||||
}
|
||||
|
@ -275,12 +246,12 @@ namespace dawn_wire { namespace client {
|
|||
mCreateReadyPipelineRequests.erase(requestIt);
|
||||
|
||||
auto pipelineAllocation =
|
||||
GetClient()->ComputePipelineAllocator().GetObject(request.pipelineObjectID);
|
||||
client->ComputePipelineAllocator().GetObject(request.pipelineObjectID);
|
||||
|
||||
// If the return status is a failure we should give a null pipeline to the callback and
|
||||
// free the allocation both on the client side and the server side.
|
||||
if (status != WGPUCreateReadyPipelineStatus_Success) {
|
||||
GetClient()->ComputePipelineAllocator().Free(pipelineAllocation);
|
||||
client->ComputePipelineAllocator().Free(pipelineAllocation);
|
||||
request.createReadyComputePipelineCallback(status, nullptr, message, request.userdata);
|
||||
return true;
|
||||
}
|
||||
|
@ -294,7 +265,7 @@ namespace dawn_wire { namespace client {
|
|||
void Device::CreateReadyRenderPipeline(WGPURenderPipelineDescriptor const* descriptor,
|
||||
WGPUCreateReadyRenderPipelineCallback callback,
|
||||
void* userdata) {
|
||||
if (GetClient()->IsDisconnected()) {
|
||||
if (client->IsDisconnected()) {
|
||||
return callback(WGPUCreateReadyPipelineStatus_DeviceLost, nullptr,
|
||||
"GPU device disconnected", userdata);
|
||||
}
|
||||
|
@ -306,14 +277,14 @@ namespace dawn_wire { namespace client {
|
|||
ASSERT(mCreateReadyPipelineRequests.find(serial) == mCreateReadyPipelineRequests.end());
|
||||
cmd.requestSerial = serial;
|
||||
|
||||
auto* allocation = GetClient()->RenderPipelineAllocator().New(this);
|
||||
auto* allocation = client->RenderPipelineAllocator().New(client);
|
||||
CreateReadyPipelineRequest request = {};
|
||||
request.createReadyRenderPipelineCallback = callback;
|
||||
request.userdata = userdata;
|
||||
request.pipelineObjectID = allocation->object->id;
|
||||
|
||||
cmd.pipelineObjectHandle = ObjectHandle(allocation->object->id, allocation->generation);
|
||||
GetClient()->SerializeCommand(cmd);
|
||||
client->SerializeCommand(cmd);
|
||||
|
||||
mCreateReadyPipelineRequests[serial] = std::move(request);
|
||||
}
|
||||
|
@ -330,12 +301,12 @@ namespace dawn_wire { namespace client {
|
|||
mCreateReadyPipelineRequests.erase(requestIt);
|
||||
|
||||
auto pipelineAllocation =
|
||||
GetClient()->RenderPipelineAllocator().GetObject(request.pipelineObjectID);
|
||||
client->RenderPipelineAllocator().GetObject(request.pipelineObjectID);
|
||||
|
||||
// If the return status is a failure we should give a null pipeline to the callback and
|
||||
// free the allocation both on the client side and the server side.
|
||||
if (status != WGPUCreateReadyPipelineStatus_Success) {
|
||||
GetClient()->RenderPipelineAllocator().Free(pipelineAllocation);
|
||||
client->RenderPipelineAllocator().Free(pipelineAllocation);
|
||||
request.createReadyRenderPipelineCallback(status, nullptr, message, request.userdata);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@ namespace dawn_wire { namespace client {
|
|||
Device(Client* client, uint32_t refcount, uint32_t id);
|
||||
~Device();
|
||||
|
||||
Client* GetClient();
|
||||
void SetUncapturedErrorCallback(WGPUErrorCallback errorCallback, void* errorUserdata);
|
||||
void SetDeviceLostCallback(WGPUDeviceLostCallback errorCallback, void* errorUserdata);
|
||||
void InjectError(WGPUErrorType type, const char* message);
|
||||
|
@ -63,16 +62,9 @@ namespace dawn_wire { namespace client {
|
|||
|
||||
WGPUQueue GetDefaultQueue();
|
||||
|
||||
template <typename T>
|
||||
void TrackObject(T* object) {
|
||||
mObjects[ObjectTypeToTypeEnum<T>::value].Append(object);
|
||||
}
|
||||
|
||||
void CancelCallbacksForDisconnect() override;
|
||||
|
||||
private:
|
||||
void DestroyAllObjects();
|
||||
|
||||
struct ErrorScopeData {
|
||||
WGPUErrorCallback callback = nullptr;
|
||||
void* userdata = nullptr;
|
||||
|
@ -90,7 +82,6 @@ namespace dawn_wire { namespace client {
|
|||
std::map<uint64_t, CreateReadyPipelineRequest> mCreateReadyPipelineRequests;
|
||||
uint64_t mCreateReadyPipelineRequestSerial = 0;
|
||||
|
||||
Client* mClient = nullptr;
|
||||
WGPUErrorCallback mErrorCallback = nullptr;
|
||||
WGPUDeviceLostCallback mDeviceLostCallback = nullptr;
|
||||
bool mDidRunLostCallback = false;
|
||||
|
@ -98,8 +89,6 @@ namespace dawn_wire { namespace client {
|
|||
void* mDeviceLostUserdata = nullptr;
|
||||
|
||||
Queue* mDefaultQueue = nullptr;
|
||||
|
||||
PerObjectType<LinkedList<ObjectBase>> mObjects;
|
||||
};
|
||||
|
||||
}} // namespace dawn_wire::client
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include "dawn_wire/client/Fence.h"
|
||||
|
||||
#include "dawn_wire/client/Client.h"
|
||||
#include "dawn_wire/client/Device.h"
|
||||
|
||||
namespace dawn_wire { namespace client {
|
||||
|
||||
|
@ -48,7 +47,7 @@ namespace dawn_wire { namespace client {
|
|||
void Fence::OnCompletion(uint64_t value,
|
||||
WGPUFenceOnCompletionCallback callback,
|
||||
void* userdata) {
|
||||
if (device->GetClient()->IsDisconnected()) {
|
||||
if (client->IsDisconnected()) {
|
||||
return callback(WGPUFenceCompletionStatus_DeviceLost, userdata);
|
||||
}
|
||||
|
||||
|
@ -62,7 +61,7 @@ namespace dawn_wire { namespace client {
|
|||
|
||||
mOnCompletionRequests[serial] = {callback, userdata};
|
||||
|
||||
this->device->GetClient()->SerializeCommand(cmd);
|
||||
client->SerializeCommand(cmd);
|
||||
}
|
||||
|
||||
void Fence::OnUpdateCompletedValueCallback(uint64_t value) {
|
||||
|
|
|
@ -25,14 +25,8 @@
|
|||
|
||||
namespace dawn_wire { namespace client {
|
||||
|
||||
class Client;
|
||||
class Device;
|
||||
|
||||
template <typename T>
|
||||
class ObjectAllocator {
|
||||
using ObjectOwner =
|
||||
typename std::conditional<std::is_same<T, Device>::value, Client, Device>::type;
|
||||
|
||||
public:
|
||||
struct ObjectAndSerial {
|
||||
ObjectAndSerial(std::unique_ptr<T> object, uint32_t generation)
|
||||
|
@ -47,10 +41,11 @@ namespace dawn_wire { namespace client {
|
|||
mObjects.emplace_back(nullptr, 0);
|
||||
}
|
||||
|
||||
ObjectAndSerial* New(ObjectOwner* owner) {
|
||||
template <typename Client>
|
||||
ObjectAndSerial* New(Client* client) {
|
||||
uint32_t id = GetNewId();
|
||||
auto object = std::make_unique<T>(owner, 1, id);
|
||||
owner->TrackObject(object.get());
|
||||
auto object = std::make_unique<T>(client, 1, id);
|
||||
client->TrackObject(object.get());
|
||||
|
||||
if (id >= mObjects.size()) {
|
||||
ASSERT(id == mObjects.size());
|
||||
|
@ -109,7 +104,6 @@ namespace dawn_wire { namespace client {
|
|||
uint32_t mCurrentId = 1;
|
||||
std::vector<uint32_t> mFreeIds;
|
||||
std::vector<ObjectAndSerial> mObjects;
|
||||
Device* mDevice;
|
||||
};
|
||||
}} // namespace dawn_wire::client
|
||||
|
||||
|
|
|
@ -22,16 +22,16 @@
|
|||
|
||||
namespace dawn_wire { namespace client {
|
||||
|
||||
class Device;
|
||||
class Client;
|
||||
|
||||
// All non-Device objects of the client side have:
|
||||
// - A pointer to the device to get where to serialize commands
|
||||
// All objects on the client side have:
|
||||
// - A pointer to the Client to get where to serialize commands
|
||||
// - The external reference count
|
||||
// - An ID that is used to refer to this object when talking with the server side
|
||||
// - A next/prev pointer. They are part of a linked list of objects of the same type.
|
||||
struct ObjectBase : public LinkNode<ObjectBase> {
|
||||
ObjectBase(Device* device, uint32_t refcount, uint32_t id)
|
||||
: device(device), refcount(refcount), id(id) {
|
||||
ObjectBase(Client* client, uint32_t refcount, uint32_t id)
|
||||
: client(client), refcount(refcount), id(id) {
|
||||
}
|
||||
|
||||
~ObjectBase() {
|
||||
|
@ -41,7 +41,7 @@ namespace dawn_wire { namespace client {
|
|||
virtual void CancelCallbacksForDisconnect() {
|
||||
}
|
||||
|
||||
Device* const device;
|
||||
Client* const client;
|
||||
uint32_t refcount;
|
||||
const uint32_t id;
|
||||
};
|
||||
|
|
|
@ -20,13 +20,13 @@
|
|||
namespace dawn_wire { namespace client {
|
||||
|
||||
WGPUFence Queue::CreateFence(WGPUFenceDescriptor const* descriptor) {
|
||||
auto* allocation = device->GetClient()->FenceAllocator().New(device);
|
||||
auto* allocation = client->FenceAllocator().New(client);
|
||||
|
||||
QueueCreateFenceCmd cmd;
|
||||
cmd.self = ToAPI(this);
|
||||
cmd.result = ObjectHandle{allocation->object->id, allocation->generation};
|
||||
cmd.descriptor = descriptor;
|
||||
device->GetClient()->SerializeCommand(cmd);
|
||||
client->SerializeCommand(cmd);
|
||||
|
||||
Fence* fence = allocation->object.get();
|
||||
fence->Initialize(this, descriptor);
|
||||
|
@ -46,7 +46,7 @@ namespace dawn_wire { namespace client {
|
|||
cmd.data = static_cast<const uint8_t*>(data);
|
||||
cmd.size = size;
|
||||
|
||||
device->GetClient()->SerializeCommand(cmd);
|
||||
client->SerializeCommand(cmd);
|
||||
}
|
||||
|
||||
void Queue::WriteTexture(const WGPUTextureCopyView* destination,
|
||||
|
@ -62,7 +62,7 @@ namespace dawn_wire { namespace client {
|
|||
cmd.dataLayout = dataLayout;
|
||||
cmd.writeSize = writeSize;
|
||||
|
||||
device->GetClient()->SerializeCommand(cmd);
|
||||
client->SerializeCommand(cmd);
|
||||
}
|
||||
|
||||
}} // namespace dawn_wire::client
|
||||
|
|
Loading…
Reference in New Issue