mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-05-13 19:01:24 +00:00
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>
308 lines
13 KiB
C++
308 lines
13 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 "common/Log.h"
|
|
#include "dawn_wire/client/ApiObjects.h"
|
|
#include "dawn_wire/client/Client.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace dawn_wire { namespace client {
|
|
namespace {
|
|
|
|
//* Outputs an rvalue that's the number of elements a pointer member points to.
|
|
{% macro member_length(member, accessor) -%}
|
|
{%- if member.length == "constant" -%}
|
|
{{member.constant_length}}
|
|
{%- else -%}
|
|
{{accessor}}{{as_varName(member.length.name)}}
|
|
{%- endif -%}
|
|
{%- endmacro %}
|
|
|
|
{% for type in by_category["object"] %}
|
|
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 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 (!ClientMatches(client, obj[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
{% endfor %}
|
|
|
|
bool ClientMatches(const Client* client, WGPUChainedStruct const* chainedStruct);
|
|
|
|
{% for type in by_category["structure"] if type.may_have_dawn_object %}
|
|
DAWN_DECLARE_UNUSED bool ClientMatches(const Client* client, const {{as_cType(type.name)}}& obj) {
|
|
{% if type.extensible %}
|
|
if (!ClientMatches(client, obj.nextInChain)) {
|
|
return false;
|
|
}
|
|
{% endif %}
|
|
{% for member in type.members if member.type.may_have_dawn_object or member.type.category == "object" %}
|
|
{% if member.optional %}
|
|
if (obj.{{as_varName(member.name)}} != nullptr)
|
|
{% endif %}
|
|
{
|
|
if (!ClientMatches(client, obj.{{as_varName(member.name)}}
|
|
{%- if member.annotation != "value" and member.length != "strlen" -%}
|
|
, {{member_length(member, "obj.")}}
|
|
{%- endif -%})) {
|
|
return false;
|
|
}
|
|
}
|
|
{% endfor %}
|
|
return true;
|
|
}
|
|
|
|
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 (!ClientMatches(client, obj[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
{% endfor %}
|
|
|
|
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 (!ClientMatches(client, reinterpret_cast<const {{CType}}*>(chainedStruct))) {
|
|
return false;
|
|
}
|
|
{% endif %}
|
|
break;
|
|
}
|
|
{% endfor %}
|
|
case WGPUSType_Invalid:
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
dawn::WarningLog()
|
|
<< "All objects may not be from the same client. "
|
|
<< "Unknown sType " << chainedStruct->sType << " discarded.";
|
|
return false;
|
|
}
|
|
chainedStruct = chainedStruct->next;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
//* Implementation of the client API functions.
|
|
{% for type in by_category["object"] %}
|
|
{% set Type = type.name.CamelCase() %}
|
|
{% set cType = as_cType(type.name) %}
|
|
|
|
{% for method in type.methods %}
|
|
{% set Suffix = as_MethodSuffix(type.name, method.name) %}
|
|
|
|
{% if Suffix in client_handwritten_commands %}
|
|
static
|
|
{% endif %}
|
|
{{as_cType(method.return_type.name)}} Client{{Suffix}}(
|
|
{{-cType}} cSelf
|
|
{%- for arg in method.arguments -%}
|
|
, {{as_annotated_cType(arg)}}
|
|
{%- endfor -%}
|
|
) {
|
|
{% if len(method.arguments) > 0 %}
|
|
{
|
|
bool sameClient = true;
|
|
auto self = reinterpret_cast<{{as_wireType(type)}}>(cSelf);
|
|
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" %}
|
|
{% if arg.optional %}
|
|
if ({{as_varName(arg.name)}} != nullptr)
|
|
{% endif %}
|
|
{
|
|
if (!ClientMatches(client, {{as_varName(arg.name)}}
|
|
{%- if arg.annotation != "value" and arg.length != "strlen" -%}
|
|
, {{member_length(arg, "")}}
|
|
{%- endif -%})) {
|
|
sameClient = false;
|
|
break;
|
|
}
|
|
}
|
|
{% endfor %}
|
|
} while (false);
|
|
|
|
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->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;
|
|
{% else %}
|
|
return {};
|
|
{% endif %}
|
|
}
|
|
}
|
|
{% endif %}
|
|
|
|
auto self = reinterpret_cast<{{as_wireType(type)}}>(cSelf);
|
|
{% if Suffix not in client_handwritten_commands %}
|
|
{{Suffix}}Cmd cmd;
|
|
|
|
//* Create the structure going on the wire on the stack and fill it with the value
|
|
//* arguments so it can compute its size.
|
|
cmd.self = cSelf;
|
|
|
|
//* For object creation, store the object ID the client will use for the result.
|
|
{% if method.return_type.category == "object" %}
|
|
auto* allocation = self->client->{{method.return_type.name.CamelCase()}}Allocator().New(self->client);
|
|
cmd.result = ObjectHandle{allocation->object->id, allocation->generation};
|
|
{% endif %}
|
|
|
|
{% for arg in method.arguments %}
|
|
cmd.{{as_varName(arg.name)}} = {{as_varName(arg.name)}};
|
|
{% endfor %}
|
|
|
|
//* Allocate space to send the command and copy the value args over.
|
|
self->client->SerializeCommand(cmd);
|
|
|
|
{% if method.return_type.category == "object" %}
|
|
return reinterpret_cast<{{as_cType(method.return_type.name)}}>(allocation->object.get());
|
|
{% endif %}
|
|
{% else %}
|
|
return self->{{method.name.CamelCase()}}(
|
|
{%- for arg in method.arguments -%}
|
|
{%if not loop.first %}, {% endif %} {{as_varName(arg.name)}}
|
|
{%- endfor -%});
|
|
{% endif %}
|
|
}
|
|
{% endfor %}
|
|
|
|
{% if not type.name.canonical_case() == "device" %}
|
|
//* When an object's refcount reaches 0, notify the server side of it and delete it.
|
|
void Client{{as_MethodSuffix(type.name, Name("release"))}}({{cType}} cObj) {
|
|
{{Type}}* obj = reinterpret_cast<{{Type}}*>(cObj);
|
|
obj->refcount --;
|
|
|
|
if (obj->refcount > 0) {
|
|
return;
|
|
}
|
|
|
|
DestroyObjectCmd cmd;
|
|
cmd.objectType = ObjectType::{{type.name.CamelCase()}};
|
|
cmd.objectId = obj->id;
|
|
|
|
obj->client->SerializeCommand(cmd);
|
|
obj->client->{{type.name.CamelCase()}}Allocator().Free(obj);
|
|
}
|
|
|
|
void Client{{as_MethodSuffix(type.name, Name("reference"))}}({{cType}} cObj) {
|
|
{{Type}}* obj = reinterpret_cast<{{Type}}*>(cObj);
|
|
obj->refcount ++;
|
|
}
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
namespace {
|
|
WGPUInstance ClientCreateInstance(WGPUInstanceDescriptor const* descriptor) {
|
|
UNREACHABLE();
|
|
return nullptr;
|
|
}
|
|
|
|
void ClientDeviceReference(WGPUDevice) {
|
|
}
|
|
|
|
void ClientDeviceRelease(WGPUDevice) {
|
|
}
|
|
|
|
struct ProcEntry {
|
|
WGPUProc proc;
|
|
const char* name;
|
|
};
|
|
static const ProcEntry sProcMap[] = {
|
|
{% for (type, method) in c_methods_sorted_by_name %}
|
|
{ reinterpret_cast<WGPUProc>(Client{{as_MethodSuffix(type.name, method.name)}}), "{{as_cMethod(type.name, method.name)}}" },
|
|
{% endfor %}
|
|
};
|
|
static constexpr size_t sProcMapSize = sizeof(sProcMap) / sizeof(sProcMap[0]);
|
|
} // anonymous namespace
|
|
|
|
WGPUProc ClientGetProcAddress(WGPUDevice, const char* procName) {
|
|
if (procName == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
const ProcEntry* entry = std::lower_bound(&sProcMap[0], &sProcMap[sProcMapSize], procName,
|
|
[](const ProcEntry &a, const char *b) -> bool {
|
|
return strcmp(a.name, b) < 0;
|
|
}
|
|
);
|
|
|
|
if (entry != &sProcMap[sProcMapSize] && strcmp(entry->name, procName) == 0) {
|
|
return entry->proc;
|
|
}
|
|
|
|
// Special case the two free-standing functions of the API.
|
|
if (strcmp(procName, "wgpuGetProcAddress") == 0) {
|
|
return reinterpret_cast<WGPUProc>(ClientGetProcAddress);
|
|
}
|
|
|
|
if (strcmp(procName, "wgpuCreateInstance") == 0) {
|
|
return reinterpret_cast<WGPUProc>(ClientCreateInstance);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
std::vector<const char*> GetProcMapNamesForTesting() {
|
|
std::vector<const char*> result;
|
|
result.reserve(sProcMapSize);
|
|
for (const ProcEntry& entry : sProcMap) {
|
|
result.push_back(entry.name);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static DawnProcTable gProcTable = {
|
|
ClientGetProcAddress,
|
|
ClientCreateInstance,
|
|
{% for type in by_category["object"] %}
|
|
{% for method in c_methods(type) %}
|
|
Client{{as_MethodSuffix(type.name, method.name)}},
|
|
{% endfor %}
|
|
{% endfor %}
|
|
};
|
|
const DawnProcTable& GetProcs() {
|
|
return gProcTable;
|
|
}
|
|
}} // namespace dawn_wire::client
|