mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-08-11 14:39:13 +00:00
Adds a way to store the limits on the Adapter and the Device. For now, adapter limits are always the default limits, and device limits are stored but not used. This CL also adds usage of an ErrorObjectIdResolver and Provider in the WGPUDeviceProperties serialization and deserialization helpers. Serializing/deserializing this struct should never have objects. Bug: dawn:685 Change-Id: I1479b4407b0f9ec9f9b2bff62cad7caa693c99d7 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/63983 Commit-Queue: Austin Eng <enga@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
734 lines
34 KiB
C++
734 lines
34 KiB
C++
//* Copyright 2017 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/WireCmd_autogen.h"
|
|
|
|
#include "common/Assert.h"
|
|
#include "common/Log.h"
|
|
#include "dawn_wire/BufferConsumer_impl.h"
|
|
#include "dawn_wire/Wire.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <limits>
|
|
|
|
//* Helper macros so that the main [de]serialization functions can be written in a generic manner.
|
|
|
|
//* Outputs an rvalue that's the number of elements a pointer member points to.
|
|
{% macro member_length(member, record_accessor) -%}
|
|
{%- if member.length == "constant" -%}
|
|
{{member.constant_length}}u
|
|
{%- else -%}
|
|
{{record_accessor}}{{as_varName(member.length.name)}}
|
|
{%- endif -%}
|
|
{%- endmacro %}
|
|
|
|
//* Outputs the type that will be used on the wire for the member
|
|
{% macro member_transfer_type(member) -%}
|
|
{%- if member.type.category == "object" -%}
|
|
ObjectId
|
|
{%- elif member.type.category == "structure" -%}
|
|
{{as_cType(member.type.name)}}Transfer
|
|
{%- elif member.type.category == "bitmask" -%}
|
|
{{as_cType(member.type.name)}}Flags
|
|
{%- else -%}
|
|
{{ assert(as_cType(member.type.name) != "size_t") }}
|
|
{{as_cType(member.type.name)}}
|
|
{%- endif -%}
|
|
{%- endmacro %}
|
|
|
|
//* Outputs the size of one element of the type that will be used on the wire for the member
|
|
{% macro member_transfer_sizeof(member) -%}
|
|
sizeof({{member_transfer_type(member)}})
|
|
{%- endmacro %}
|
|
|
|
//* Outputs the serialization code to put `in` in `out`
|
|
{% macro serialize_member(member, in, out) %}
|
|
{%- if member.type.category == "object" -%}
|
|
{%- set Optional = "Optional" if member.optional else "" -%}
|
|
WIRE_TRY(provider.Get{{Optional}}Id({{in}}, &{{out}}));
|
|
{% elif member.type.category == "structure"%}
|
|
{%- set Provider = ", provider" if member.type.may_have_dawn_object else "" -%}
|
|
{% if member.annotation == "const*const*" %}
|
|
WIRE_TRY({{as_cType(member.type.name)}}Serialize(*{{in}}, &{{out}}, buffer{{Provider}}));
|
|
{% else %}
|
|
WIRE_TRY({{as_cType(member.type.name)}}Serialize({{in}}, &{{out}}, buffer{{Provider}}));
|
|
{% endif %}
|
|
{%- else -%}
|
|
{{out}} = {{in}};
|
|
{%- endif -%}
|
|
{% endmacro %}
|
|
|
|
//* Outputs the deserialization code to put `in` in `out`
|
|
{% macro deserialize_member(member, in, out) %}
|
|
{%- if member.type.category == "object" -%}
|
|
{%- set Optional = "Optional" if member.optional else "" -%}
|
|
WIRE_TRY(resolver.Get{{Optional}}FromId({{in}}, &{{out}}));
|
|
{%- elif member.type.category == "structure" -%}
|
|
WIRE_TRY({{as_cType(member.type.name)}}Deserialize(&{{out}}, &{{in}}, deserializeBuffer, allocator
|
|
{%- if member.type.may_have_dawn_object -%}
|
|
, resolver
|
|
{%- endif -%}
|
|
));
|
|
{%- else -%}
|
|
static_assert(sizeof({{out}}) >= sizeof({{in}}), "Deserialize assignment may not narrow.");
|
|
{{out}} = {{in}};
|
|
{%- endif -%}
|
|
{% endmacro %}
|
|
|
|
namespace {
|
|
|
|
struct WGPUChainedStructTransfer {
|
|
WGPUSType sType;
|
|
bool hasNext;
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
//* The main [de]serialization macro
|
|
//* Methods are very similar to structures that have one member corresponding to each arguments.
|
|
//* This macro takes advantage of the similarity to output [de]serialization code for a record
|
|
//* that is either a structure or a method, with some special cases for each.
|
|
{% macro write_record_serialization_helpers(record, name, members, is_cmd=False, is_return_command=False) %}
|
|
{% set Return = "Return" if is_return_command else "" %}
|
|
{% set Cmd = "Cmd" if is_cmd else "" %}
|
|
{% set Inherits = " : CmdHeader" if is_cmd else "" %}
|
|
|
|
//* Structure for the wire format of each of the records. Members that are values
|
|
//* are embedded directly in the structure. Other members are assumed to be in the
|
|
//* memory directly following the structure in the buffer.
|
|
struct {{Return}}{{name}}Transfer{{Inherits}} {
|
|
static_assert({{[is_cmd, record.extensible, record.chained].count(True)}} <= 1,
|
|
"Record must be at most one of is_cmd, extensible, and chained.");
|
|
{% if is_cmd %}
|
|
//* Start the transfer structure with the command ID, so that casting to WireCmd gives the ID.
|
|
{{Return}}WireCmd commandId;
|
|
{% elif record.extensible %}
|
|
bool hasNextInChain;
|
|
{% elif record.chained %}
|
|
WGPUChainedStructTransfer chain;
|
|
{% endif %}
|
|
|
|
//* Value types are directly in the command, objects being replaced with their IDs.
|
|
{% for member in members if member.annotation == "value" %}
|
|
{{member_transfer_type(member)}} {{as_varName(member.name)}};
|
|
{% endfor %}
|
|
|
|
//* const char* have their length embedded directly in the command.
|
|
{% for member in members if member.length == "strlen" %}
|
|
uint64_t {{as_varName(member.name)}}Strlen;
|
|
{% endfor %}
|
|
|
|
{% for member in members if member.optional and member.annotation != "value" and member.type.category != "object" %}
|
|
bool has_{{as_varName(member.name)}};
|
|
{% endfor %}
|
|
};
|
|
|
|
{% if is_cmd %}
|
|
static_assert(offsetof({{Return}}{{name}}Transfer, commandSize) == 0, "");
|
|
static_assert(offsetof({{Return}}{{name}}Transfer, commandId) == sizeof(CmdHeader), "");
|
|
{% endif %}
|
|
|
|
{% if record.chained %}
|
|
static_assert(offsetof({{Return}}{{name}}Transfer, chain) == 0, "");
|
|
{% endif %}
|
|
|
|
//* Returns the required transfer size for `record` in addition to the transfer structure.
|
|
DAWN_DECLARE_UNUSED size_t {{Return}}{{name}}GetExtraRequiredSize(const {{Return}}{{name}}{{Cmd}}& record) {
|
|
DAWN_UNUSED(record);
|
|
|
|
size_t result = 0;
|
|
|
|
//* Gather how much space will be needed for the extension chain.
|
|
{% if record.extensible %}
|
|
if (record.nextInChain != nullptr) {
|
|
result += GetChainedStructExtraRequiredSize(record.nextInChain);
|
|
}
|
|
{% endif %}
|
|
|
|
//* Special handling of const char* that have their length embedded directly in the command
|
|
{% for member in members if member.length == "strlen" %}
|
|
{% set memberName = as_varName(member.name) %}
|
|
|
|
{% if member.optional %}
|
|
bool has_{{memberName}} = record.{{memberName}} != nullptr;
|
|
if (has_{{memberName}})
|
|
{% endif %}
|
|
{
|
|
result += std::strlen(record.{{memberName}});
|
|
}
|
|
{% endfor %}
|
|
|
|
//* Gather how much space will be needed for pointer members.
|
|
{% for member in members if member.length != "strlen" and not member.skip_serialize %}
|
|
{% if member.type.category != "object" and member.optional %}
|
|
if (record.{{as_varName(member.name)}} != nullptr)
|
|
{% endif %}
|
|
{
|
|
{% if member.annotation != "value" %}
|
|
auto memberLength = {{member_length(member, "record.")}};
|
|
result += memberLength * {{member_transfer_sizeof(member)}};
|
|
//* Structures might contain more pointers so we need to add their extra size as well.
|
|
{% if member.type.category == "structure" %}
|
|
for (decltype(memberLength) i = 0; i < memberLength; ++i) {
|
|
{% if member.annotation == "const*const*" %}
|
|
result += {{as_cType(member.type.name)}}GetExtraRequiredSize(*record.{{as_varName(member.name)}}[i]);
|
|
{% else %}
|
|
{{assert(member.annotation == "const*")}}
|
|
result += {{as_cType(member.type.name)}}GetExtraRequiredSize(record.{{as_varName(member.name)}}[i]);
|
|
{% endif %}
|
|
}
|
|
{% endif %}
|
|
{% elif member.type.category == "structure" %}
|
|
result += {{as_cType(member.type.name)}}GetExtraRequiredSize(record.{{as_varName(member.name)}});
|
|
{% endif %}
|
|
}
|
|
{% endfor %}
|
|
|
|
return result;
|
|
}
|
|
// GetExtraRequiredSize isn't used for structures that are value members of other structures
|
|
// because we assume they cannot contain pointers themselves.
|
|
DAWN_UNUSED_FUNC({{Return}}{{name}}GetExtraRequiredSize);
|
|
|
|
//* Serializes `record` into `transfer`, using `buffer` to get more space for pointed-to data
|
|
//* and `provider` to serialize objects.
|
|
DAWN_DECLARE_UNUSED WireResult {{Return}}{{name}}Serialize(const {{Return}}{{name}}{{Cmd}}& record, {{Return}}{{name}}Transfer* transfer,
|
|
SerializeBuffer* buffer
|
|
{%- if record.may_have_dawn_object -%}
|
|
, const ObjectIdProvider& provider
|
|
{%- endif -%}
|
|
) {
|
|
DAWN_UNUSED(buffer);
|
|
|
|
//* Handle special transfer members of methods.
|
|
{% if is_cmd %}
|
|
transfer->commandId = {{Return}}WireCmd::{{name}};
|
|
{% endif %}
|
|
|
|
//* Value types are directly in the transfer record, objects being replaced with their IDs.
|
|
{% for member in members if member.annotation == "value" %}
|
|
{% set memberName = as_varName(member.name) %}
|
|
{{serialize_member(member, "record." + memberName, "transfer->" + memberName)}}
|
|
{% endfor %}
|
|
|
|
{% if record.extensible %}
|
|
if (record.nextInChain != nullptr) {
|
|
transfer->hasNextInChain = true;
|
|
WIRE_TRY(SerializeChainedStruct(record.nextInChain, buffer, provider));
|
|
} else {
|
|
transfer->hasNextInChain = false;
|
|
}
|
|
{% endif %}
|
|
|
|
{% if record.chained %}
|
|
//* Should be set by the root descriptor's call to SerializeChainedStruct.
|
|
ASSERT(transfer->chain.sType == {{as_cEnum(types["s type"].name, record.name)}});
|
|
ASSERT(transfer->chain.hasNext == (record.chain.next != nullptr));
|
|
{% endif %}
|
|
|
|
//* Special handling of const char* that have their length embedded directly in the command
|
|
{% for member in members if member.length == "strlen" %}
|
|
{% set memberName = as_varName(member.name) %}
|
|
|
|
{% if member.optional %}
|
|
bool has_{{memberName}} = record.{{memberName}} != nullptr;
|
|
transfer->has_{{memberName}} = has_{{memberName}};
|
|
if (has_{{memberName}})
|
|
{% endif %}
|
|
{
|
|
transfer->{{memberName}}Strlen = std::strlen(record.{{memberName}});
|
|
|
|
char* stringInBuffer;
|
|
WIRE_TRY(buffer->NextN(transfer->{{memberName}}Strlen, &stringInBuffer));
|
|
memcpy(stringInBuffer, record.{{memberName}}, transfer->{{memberName}}Strlen);
|
|
}
|
|
{% endfor %}
|
|
|
|
//* Allocate space and write the non-value arguments in it.
|
|
{% for member in members if member.annotation != "value" and member.length != "strlen" and not member.skip_serialize %}
|
|
{% set memberName = as_varName(member.name) %}
|
|
|
|
{% if member.type.category != "object" and member.optional %}
|
|
bool has_{{memberName}} = record.{{memberName}} != nullptr;
|
|
transfer->has_{{memberName}} = has_{{memberName}};
|
|
if (has_{{memberName}})
|
|
{% endif %}
|
|
{
|
|
auto memberLength = {{member_length(member, "record.")}};
|
|
|
|
{{member_transfer_type(member)}}* memberBuffer;
|
|
WIRE_TRY(buffer->NextN(memberLength, &memberBuffer));
|
|
|
|
//* This loop cannot overflow because it iterates up to |memberLength|. Even if
|
|
//* memberLength were the maximum integer value, |i| would become equal to it just before
|
|
//* exiting the loop, but not increment past or wrap around.
|
|
for (decltype(memberLength) i = 0; i < memberLength; ++i) {
|
|
{{serialize_member(member, "record." + memberName + "[i]", "memberBuffer[i]" )}}
|
|
}
|
|
}
|
|
{% endfor %}
|
|
return WireResult::Success;
|
|
}
|
|
DAWN_UNUSED_FUNC({{Return}}{{name}}Serialize);
|
|
|
|
//* Deserializes `transfer` into `record` getting more serialized data from `buffer` and `size`
|
|
//* if needed, using `allocator` to store pointed-to values and `resolver` to translate object
|
|
//* Ids to actual objects.
|
|
DAWN_DECLARE_UNUSED WireResult {{Return}}{{name}}Deserialize({{Return}}{{name}}{{Cmd}}* record, const volatile {{Return}}{{name}}Transfer* transfer,
|
|
DeserializeBuffer* deserializeBuffer, DeserializeAllocator* allocator
|
|
{%- if record.may_have_dawn_object -%}
|
|
, const ObjectIdResolver& resolver
|
|
{%- endif -%}
|
|
) {
|
|
DAWN_UNUSED(allocator);
|
|
|
|
{% if is_cmd %}
|
|
ASSERT(transfer->commandId == {{Return}}WireCmd::{{name}});
|
|
{% endif %}
|
|
|
|
{% if record.derived_method %}
|
|
record->selfId = transfer->self;
|
|
{% endif %}
|
|
|
|
//* Value types are directly in the transfer record, objects being replaced with their IDs.
|
|
{% for member in members if member.annotation == "value" %}
|
|
{% set memberName = as_varName(member.name) %}
|
|
{{deserialize_member(member, "transfer->" + memberName, "record->" + memberName)}}
|
|
{% endfor %}
|
|
|
|
{% if record.extensible %}
|
|
record->nextInChain = nullptr;
|
|
if (transfer->hasNextInChain) {
|
|
WIRE_TRY(DeserializeChainedStruct(&record->nextInChain, deserializeBuffer, allocator, resolver));
|
|
}
|
|
{% endif %}
|
|
|
|
{% if record.chained %}
|
|
//* Should be set by the root descriptor's call to DeserializeChainedStruct.
|
|
//* Don't check |record->chain.next| matches because it is not set until the
|
|
//* next iteration inside DeserializeChainedStruct.
|
|
ASSERT(record->chain.sType == {{as_cEnum(types["s type"].name, record.name)}});
|
|
ASSERT(record->chain.next == nullptr);
|
|
{% endif %}
|
|
|
|
//* Special handling of const char* that have their length embedded directly in the command
|
|
{% for member in members if member.length == "strlen" %}
|
|
{% set memberName = as_varName(member.name) %}
|
|
|
|
{% if member.optional %}
|
|
bool has_{{memberName}} = transfer->has_{{memberName}};
|
|
record->{{memberName}} = nullptr;
|
|
if (has_{{memberName}})
|
|
{% endif %}
|
|
{
|
|
uint64_t stringLength64 = transfer->{{memberName}}Strlen;
|
|
if (stringLength64 >= std::numeric_limits<size_t>::max()) {
|
|
//* Cannot allocate space for the string. It can be at most
|
|
//* size_t::max() - 1. We need 1 byte for the null-terminator.
|
|
return WireResult::FatalError;
|
|
}
|
|
size_t stringLength = static_cast<size_t>(stringLength64);
|
|
|
|
const volatile char* stringInBuffer;
|
|
WIRE_TRY(deserializeBuffer->ReadN(stringLength, &stringInBuffer));
|
|
|
|
char* copiedString;
|
|
WIRE_TRY(GetSpace(allocator, stringLength + 1, &copiedString));
|
|
//* We can cast away the volatile qualifier because DeserializeBuffer::ReadN already
|
|
//* validated that the range [stringInBuffer, stringInBuffer + stringLength) is valid.
|
|
//* memcpy may have an unknown access pattern, but this is fine since the string is only
|
|
//* data and won't affect control flow of this function.
|
|
memcpy(copiedString, const_cast<const char*>(stringInBuffer), stringLength);
|
|
copiedString[stringLength] = '\0';
|
|
record->{{memberName}} = copiedString;
|
|
}
|
|
{% endfor %}
|
|
|
|
//* Get extra buffer data, and copy pointed to values in extra allocated space.
|
|
{% for member in members if member.annotation != "value" and member.length != "strlen" %}
|
|
{% set memberName = as_varName(member.name) %}
|
|
|
|
{% if member.type.category != "object" and member.optional %}
|
|
//* Non-constant length optional members use length=0 to denote they aren't present.
|
|
//* Otherwise we could have length=N and has_member=false, causing reads from an
|
|
//* uninitialized pointer.
|
|
{{ assert(member.length == "constant") }}
|
|
bool has_{{memberName}} = transfer->has_{{memberName}};
|
|
record->{{memberName}} = nullptr;
|
|
if (has_{{memberName}})
|
|
{% endif %}
|
|
{
|
|
auto memberLength = {{member_length(member, "record->")}};
|
|
const volatile {{member_transfer_type(member)}}* memberBuffer;
|
|
WIRE_TRY(deserializeBuffer->ReadN(memberLength, &memberBuffer));
|
|
|
|
{{as_cType(member.type.name)}}* copiedMembers;
|
|
WIRE_TRY(GetSpace(allocator, memberLength, &copiedMembers));
|
|
{% if member.annotation == "const*const*" %}
|
|
{{as_cType(member.type.name)}}** pointerArray;
|
|
WIRE_TRY(GetSpace(allocator, memberLength, &pointerArray));
|
|
|
|
//* This loop cannot overflow because it iterates up to |memberLength|. Even if
|
|
//* memberLength were the maximum integer value, |i| would become equal to it just before
|
|
//* exiting the loop, but not increment past or wrap around.
|
|
for (decltype(memberLength) i = 0; i < memberLength; ++i) {
|
|
pointerArray[i] = &copiedMembers[i];
|
|
}
|
|
record->{{memberName}} = pointerArray;
|
|
{% else %}
|
|
record->{{memberName}} = copiedMembers;
|
|
{% endif %}
|
|
|
|
//* This loop cannot overflow because it iterates up to |memberLength|. Even if
|
|
//* memberLength were the maximum integer value, |i| would become equal to it just before
|
|
//* exiting the loop, but not increment past or wrap around.
|
|
for (decltype(memberLength) i = 0; i < memberLength; ++i) {
|
|
{{deserialize_member(member, "memberBuffer[i]", "copiedMembers[i]")}}
|
|
}
|
|
}
|
|
{% endfor %}
|
|
|
|
return WireResult::Success;
|
|
}
|
|
DAWN_UNUSED_FUNC({{Return}}{{name}}Deserialize);
|
|
{% endmacro %}
|
|
|
|
{% macro write_command_serialization_methods(command, is_return) %}
|
|
{% set Return = "Return" if is_return else "" %}
|
|
{% set Name = Return + command.name.CamelCase() %}
|
|
{% set Cmd = Name + "Cmd" %}
|
|
|
|
size_t {{Cmd}}::GetRequiredSize() const {
|
|
size_t size = sizeof({{Name}}Transfer) + {{Name}}GetExtraRequiredSize(*this);
|
|
return size;
|
|
}
|
|
|
|
WireResult {{Cmd}}::Serialize(size_t commandSize, SerializeBuffer* buffer
|
|
{%- if command.may_have_dawn_object -%}
|
|
, const ObjectIdProvider& provider
|
|
{%- endif -%}
|
|
) const {
|
|
{{Name}}Transfer* transfer;
|
|
WIRE_TRY(buffer->Next(&transfer));
|
|
transfer->commandSize = commandSize;
|
|
|
|
WIRE_TRY({{Name}}Serialize(*this, transfer, buffer
|
|
{%- if command.may_have_dawn_object -%}
|
|
, provider
|
|
{%- endif -%}
|
|
));
|
|
return WireResult::Success;
|
|
}
|
|
|
|
WireResult {{Cmd}}::Deserialize(DeserializeBuffer* deserializeBuffer, DeserializeAllocator* allocator
|
|
{%- if command.may_have_dawn_object -%}
|
|
, const ObjectIdResolver& resolver
|
|
{%- endif -%}
|
|
) {
|
|
const volatile {{Name}}Transfer* transfer;
|
|
WIRE_TRY(deserializeBuffer->Read(&transfer));
|
|
|
|
return {{Name}}Deserialize(this, transfer, deserializeBuffer, allocator
|
|
{%- if command.may_have_dawn_object -%}
|
|
, resolver
|
|
{%- endif -%}
|
|
);
|
|
}
|
|
{% endmacro %}
|
|
|
|
namespace dawn_wire {
|
|
|
|
ObjectHandle::ObjectHandle() = default;
|
|
ObjectHandle::ObjectHandle(ObjectId id, ObjectGeneration generation)
|
|
: id(id), generation(generation) {
|
|
}
|
|
|
|
ObjectHandle::ObjectHandle(const volatile ObjectHandle& rhs)
|
|
: id(rhs.id), generation(rhs.generation) {
|
|
}
|
|
ObjectHandle& ObjectHandle::operator=(const volatile ObjectHandle& rhs) {
|
|
id = rhs.id;
|
|
generation = rhs.generation;
|
|
return *this;
|
|
}
|
|
|
|
ObjectHandle& ObjectHandle::AssignFrom(const ObjectHandle& rhs) {
|
|
id = rhs.id;
|
|
generation = rhs.generation;
|
|
return *this;
|
|
}
|
|
ObjectHandle& ObjectHandle::AssignFrom(const volatile ObjectHandle& rhs) {
|
|
id = rhs.id;
|
|
generation = rhs.generation;
|
|
return *this;
|
|
}
|
|
|
|
namespace {
|
|
// Allocates enough space from allocator to countain T[count] and return it in out.
|
|
// Return FatalError if the allocator couldn't allocate the memory.
|
|
// Always writes to |out| on success.
|
|
template <typename T, typename N>
|
|
WireResult GetSpace(DeserializeAllocator* allocator, N count, T** out) {
|
|
constexpr size_t kMaxCountWithoutOverflows = std::numeric_limits<size_t>::max() / sizeof(T);
|
|
if (count > kMaxCountWithoutOverflows) {
|
|
return WireResult::FatalError;
|
|
}
|
|
|
|
size_t totalSize = sizeof(T) * count;
|
|
*out = static_cast<T*>(allocator->GetSpace(totalSize));
|
|
if (*out == nullptr) {
|
|
return WireResult::FatalError;
|
|
}
|
|
|
|
return WireResult::Success;
|
|
}
|
|
|
|
size_t GetChainedStructExtraRequiredSize(const WGPUChainedStruct* chainedStruct);
|
|
DAWN_NO_DISCARD WireResult SerializeChainedStruct(WGPUChainedStruct const* chainedStruct,
|
|
SerializeBuffer* buffer,
|
|
const ObjectIdProvider& provider);
|
|
WireResult DeserializeChainedStruct(const WGPUChainedStruct** outChainNext,
|
|
DeserializeBuffer* deserializeBuffer,
|
|
DeserializeAllocator* allocator,
|
|
const ObjectIdResolver& resolver);
|
|
|
|
//* Output structure [de]serialization first because it is used by commands.
|
|
{% for type in by_category["structure"] %}
|
|
{% set name = as_cType(type.name) %}
|
|
{% if type.name.CamelCase() not in client_side_structures %}
|
|
{{write_record_serialization_helpers(type, name, type.members,
|
|
is_cmd=False)}}
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
size_t GetChainedStructExtraRequiredSize(const WGPUChainedStruct* chainedStruct) {
|
|
ASSERT(chainedStruct != nullptr);
|
|
size_t result = 0;
|
|
while (chainedStruct != nullptr) {
|
|
switch (chainedStruct->sType) {
|
|
{% for sType in types["s type"].values if sType.valid and sType.name.CamelCase() not in client_side_structures %}
|
|
case {{as_cEnum(types["s type"].name, sType.name)}}: {
|
|
const auto& typedStruct = *reinterpret_cast<{{as_cType(sType.name)}} const *>(chainedStruct);
|
|
result += sizeof({{as_cType(sType.name)}}Transfer);
|
|
result += {{as_cType(sType.name)}}GetExtraRequiredSize(typedStruct);
|
|
chainedStruct = typedStruct.chain.next;
|
|
break;
|
|
}
|
|
{% endfor %}
|
|
default:
|
|
// Invalid enum. Reserve space just for the transfer header (sType and hasNext).
|
|
result += sizeof(WGPUChainedStructTransfer);
|
|
chainedStruct = chainedStruct->next;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DAWN_NO_DISCARD WireResult SerializeChainedStruct(WGPUChainedStruct const* chainedStruct,
|
|
SerializeBuffer* buffer,
|
|
const ObjectIdProvider& provider) {
|
|
ASSERT(chainedStruct != nullptr);
|
|
ASSERT(buffer != nullptr);
|
|
do {
|
|
switch (chainedStruct->sType) {
|
|
{% for sType in types["s type"].values if sType.valid and sType.name.CamelCase() not in client_side_structures %}
|
|
{% set CType = as_cType(sType.name) %}
|
|
case {{as_cEnum(types["s type"].name, sType.name)}}: {
|
|
|
|
{{CType}}Transfer* transfer;
|
|
WIRE_TRY(buffer->Next(&transfer));
|
|
transfer->chain.sType = chainedStruct->sType;
|
|
transfer->chain.hasNext = chainedStruct->next != nullptr;
|
|
|
|
WIRE_TRY({{CType}}Serialize(*reinterpret_cast<{{CType}} const*>(chainedStruct), transfer, buffer
|
|
{%- if types[sType.name.get()].may_have_dawn_object -%}
|
|
, provider
|
|
{%- endif -%}
|
|
));
|
|
|
|
chainedStruct = chainedStruct->next;
|
|
} break;
|
|
{% endfor %}
|
|
default: {
|
|
// Invalid enum. Serialize just the transfer header with Invalid as the sType.
|
|
// TODO(crbug.com/dawn/369): Unknown sTypes are silently discarded.
|
|
if (chainedStruct->sType != WGPUSType_Invalid) {
|
|
dawn::WarningLog() << "Unknown sType " << chainedStruct->sType << " discarded.";
|
|
}
|
|
|
|
WGPUChainedStructTransfer* transfer;
|
|
WIRE_TRY(buffer->Next(&transfer));
|
|
transfer->sType = WGPUSType_Invalid;
|
|
transfer->hasNext = chainedStruct->next != nullptr;
|
|
|
|
// Still move on in case there are valid structs after this.
|
|
chainedStruct = chainedStruct->next;
|
|
break;
|
|
}
|
|
}
|
|
} while (chainedStruct != nullptr);
|
|
return WireResult::Success;
|
|
}
|
|
|
|
WireResult DeserializeChainedStruct(const WGPUChainedStruct** outChainNext,
|
|
DeserializeBuffer* deserializeBuffer,
|
|
DeserializeAllocator* allocator,
|
|
const ObjectIdResolver& resolver) {
|
|
bool hasNext;
|
|
do {
|
|
const volatile WGPUChainedStructTransfer* header;
|
|
WIRE_TRY(deserializeBuffer->Peek(&header));
|
|
WGPUSType sType = header->sType;
|
|
switch (sType) {
|
|
{% for sType in types["s type"].values if sType.valid and sType.name.CamelCase() not in client_side_structures %}
|
|
{% set CType = as_cType(sType.name) %}
|
|
case {{as_cEnum(types["s type"].name, sType.name)}}: {
|
|
const volatile {{CType}}Transfer* transfer;
|
|
WIRE_TRY(deserializeBuffer->Read(&transfer));
|
|
|
|
{{CType}}* outStruct;
|
|
WIRE_TRY(GetSpace(allocator, sizeof({{CType}}), &outStruct));
|
|
outStruct->chain.sType = sType;
|
|
outStruct->chain.next = nullptr;
|
|
|
|
*outChainNext = &outStruct->chain;
|
|
outChainNext = &outStruct->chain.next;
|
|
|
|
WIRE_TRY({{CType}}Deserialize(outStruct, transfer, deserializeBuffer, allocator
|
|
{%- if types[sType.name.get()].may_have_dawn_object -%}
|
|
, resolver
|
|
{%- endif -%}
|
|
));
|
|
|
|
hasNext = transfer->chain.hasNext;
|
|
} break;
|
|
{% endfor %}
|
|
default: {
|
|
// Invalid enum. Deserialize just the transfer header with Invalid as the sType.
|
|
// TODO(crbug.com/dawn/369): Unknown sTypes are silently discarded.
|
|
if (sType != WGPUSType_Invalid) {
|
|
dawn::WarningLog() << "Unknown sType " << sType << " discarded.";
|
|
}
|
|
|
|
const volatile WGPUChainedStructTransfer* transfer;
|
|
WIRE_TRY(deserializeBuffer->Read(&transfer));
|
|
|
|
WGPUChainedStruct* outStruct;
|
|
WIRE_TRY(GetSpace(allocator, sizeof(WGPUChainedStruct), &outStruct));
|
|
outStruct->sType = WGPUSType_Invalid;
|
|
outStruct->next = nullptr;
|
|
|
|
// Still move on in case there are valid structs after this.
|
|
*outChainNext = outStruct;
|
|
outChainNext = &outStruct->next;
|
|
hasNext = transfer->hasNext;
|
|
break;
|
|
}
|
|
}
|
|
} while (hasNext);
|
|
|
|
return WireResult::Success;
|
|
}
|
|
|
|
//* Output [de]serialization helpers for commands
|
|
{% for command in cmd_records["command"] %}
|
|
{% set name = command.name.CamelCase() %}
|
|
{{write_record_serialization_helpers(command, name, command.members,
|
|
is_cmd=True)}}
|
|
{% endfor %}
|
|
|
|
//* Output [de]serialization helpers for return commands
|
|
{% for command in cmd_records["return command"] %}
|
|
{% set name = command.name.CamelCase() %}
|
|
{{write_record_serialization_helpers(command, name, command.members,
|
|
is_cmd=True, is_return_command=True)}}
|
|
{% endfor %}
|
|
} // anonymous namespace
|
|
|
|
{% for command in cmd_records["command"] %}
|
|
{{ write_command_serialization_methods(command, False) }}
|
|
{% endfor %}
|
|
|
|
{% for command in cmd_records["return command"] %}
|
|
{{ write_command_serialization_methods(command, True) }}
|
|
{% endfor %}
|
|
|
|
// Implementation of ObjectIdResolver that always errors.
|
|
// Used when the generator adds a provider argument because of a chained
|
|
// struct, but in practice, a chained struct in that location is invalid.
|
|
class ErrorObjectIdResolver final : public ObjectIdResolver {
|
|
public:
|
|
{% for type in by_category["object"] %}
|
|
WireResult GetFromId(ObjectId id, {{as_cType(type.name)}}* out) const override {
|
|
return WireResult::FatalError;
|
|
}
|
|
WireResult GetOptionalFromId(ObjectId id, {{as_cType(type.name)}}* out) const override {
|
|
return WireResult::FatalError;
|
|
}
|
|
{% endfor %}
|
|
};
|
|
|
|
// Implementation of ObjectIdProvider that always errors.
|
|
// Used when the generator adds a provider argument because of a chained
|
|
// struct, but in practice, a chained struct in that location is invalid.
|
|
class ErrorObjectIdProvider final : public ObjectIdProvider {
|
|
public:
|
|
{% for type in by_category["object"] %}
|
|
WireResult GetId({{as_cType(type.name)}} object, ObjectId* out) const override {
|
|
return WireResult::FatalError;
|
|
}
|
|
WireResult GetOptionalId({{as_cType(type.name)}} object, ObjectId* out) const override {
|
|
return WireResult::FatalError;
|
|
}
|
|
{% endfor %}
|
|
};
|
|
|
|
// Implementations of serialization/deserialization of WPGUDeviceProperties.
|
|
size_t SerializedWGPUDevicePropertiesSize(const WGPUDeviceProperties* deviceProperties) {
|
|
return sizeof(WGPUDeviceProperties) +
|
|
WGPUDevicePropertiesGetExtraRequiredSize(*deviceProperties);
|
|
}
|
|
|
|
void SerializeWGPUDeviceProperties(const WGPUDeviceProperties* deviceProperties,
|
|
char* buffer) {
|
|
SerializeBuffer serializeBuffer(buffer, SerializedWGPUDevicePropertiesSize(deviceProperties));
|
|
|
|
WGPUDevicePropertiesTransfer* transfer;
|
|
|
|
WireResult result = serializeBuffer.Next(&transfer);
|
|
ASSERT(result == WireResult::Success);
|
|
|
|
ErrorObjectIdProvider provider;
|
|
result = WGPUDevicePropertiesSerialize(*deviceProperties, transfer, &serializeBuffer, provider);
|
|
ASSERT(result == WireResult::Success);
|
|
}
|
|
|
|
bool DeserializeWGPUDeviceProperties(WGPUDeviceProperties* deviceProperties,
|
|
const volatile char* buffer,
|
|
size_t size) {
|
|
const volatile WGPUDevicePropertiesTransfer* transfer;
|
|
DeserializeBuffer deserializeBuffer(buffer, size);
|
|
if (deserializeBuffer.Read(&transfer) != WireResult::Success) {
|
|
return false;
|
|
}
|
|
|
|
ErrorObjectIdResolver resolver;
|
|
return WGPUDevicePropertiesDeserialize(deviceProperties, transfer, &deserializeBuffer,
|
|
nullptr, resolver) == WireResult::Success;
|
|
}
|
|
|
|
} // namespace dawn_wire
|