Austin Eng cac0442277 dawn_wire: Support chunked commands
This CL adds support for chunking large commands by first serializing
large commands first into a separate buffer, and then sending the
buffer data chunk by chunk.

This code path is used for large writeBuffer and writeTexture, as well
as the inline memory transfer service for buffer mapping. The transfer
for writeBuffer and writeTexture will be optimized further in Chrome,
and the inline memory transfer service is currently used only in tests.

Bug: chromium:1123861, chromium:951558
Change-Id: I02491a44e653e2383174958d9c3d4a4db6fd7bde
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/28882
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
2020-10-13 22:35:34 +00:00

680 lines
30 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 "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}}
{%- 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 -%}
{{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 "" -%}
{{out}} = provider.Get{{Optional}}Id({{in}});
{% elif member.type.category == "structure"%}
{%- set Provider = ", provider" if member.type.may_have_dawn_object else "" -%}
{% if member.annotation == "const*const*" %}
{{as_cType(member.type.name)}}Serialize(*{{in}}, &{{out}}, buffer{{Provider}});
{% else %}
{{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 "" -%}
DESERIALIZE_TRY(resolver.Get{{Optional}}FromId({{in}}, &{{out}}));
{%- elif member.type.category == "structure" -%}
DESERIALIZE_TRY({{as_cType(member.type.name)}}Deserialize(&{{out}}, &{{in}}, buffer, size, allocator
{%- if member.type.may_have_dawn_object -%}
, resolver
{%- endif -%}
));
{%- else -%}
{{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" %}
size_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" %}
size_t 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 (size_t 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 void {{Return}}{{name}}Serialize(const {{Return}}{{name}}{{Cmd}}& record, {{Return}}{{name}}Transfer* transfer,
char** 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;
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}});
memcpy(*buffer, record.{{memberName}}, transfer->{{memberName}}Strlen);
*buffer += 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 %}
{
size_t memberLength = {{member_length(member, "record.")}};
auto memberBuffer = reinterpret_cast<{{member_transfer_type(member)}}*>(*buffer);
*buffer += memberLength * {{member_transfer_sizeof(member)}};
for (size_t i = 0; i < memberLength; ++i) {
{{serialize_member(member, "record." + memberName + "[i]", "memberBuffer[i]" )}}
}
}
{% endfor %}
}
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 DeserializeResult {{Return}}{{name}}Deserialize({{Return}}{{name}}{{Cmd}}* record, const volatile {{Return}}{{name}}Transfer* transfer,
const volatile char** buffer, size_t* size, DeserializeAllocator* allocator
{%- if record.may_have_dawn_object -%}
, const ObjectIdResolver& resolver
{%- endif -%}
) {
DAWN_UNUSED(allocator);
DAWN_UNUSED(buffer);
DAWN_UNUSED(size);
{% 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) {
DESERIALIZE_TRY(DeserializeChainedStruct(&record->nextInChain, buffer, size, 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 %}
{
size_t stringLength = transfer->{{memberName}}Strlen;
const volatile char* stringInBuffer = nullptr;
DESERIALIZE_TRY(GetPtrFromBuffer(buffer, size, stringLength, &stringInBuffer));
char* copiedString = nullptr;
DESERIALIZE_TRY(GetSpace(allocator, stringLength + 1, &copiedString));
std::copy(stringInBuffer, stringInBuffer + stringLength, copiedString);
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 %}
bool has_{{memberName}} = transfer->has_{{memberName}};
record->{{memberName}} = nullptr;
if (has_{{memberName}})
{% endif %}
{
size_t memberLength = {{member_length(member, "record->")}};
auto memberBuffer = reinterpret_cast<const volatile {{member_transfer_type(member)}}*>(buffer);
DESERIALIZE_TRY(GetPtrFromBuffer(buffer, size, memberLength, &memberBuffer));
{{as_cType(member.type.name)}}* copiedMembers = nullptr;
DESERIALIZE_TRY(GetSpace(allocator, memberLength, &copiedMembers));
{% if member.annotation == "const*const*" %}
{{as_cType(member.type.name)}}** pointerArray = nullptr;
DESERIALIZE_TRY(GetSpace(allocator, memberLength, &pointerArray));
for (size_t i = 0; i < memberLength; ++i) {
pointerArray[i] = &copiedMembers[i];
}
record->{{memberName}} = pointerArray;
{% else %}
record->{{memberName}} = copiedMembers;
{% endif %}
for (size_t i = 0; i < memberLength; ++i) {
{{deserialize_member(member, "memberBuffer[i]", "copiedMembers[i]")}}
}
}
{% endfor %}
return DeserializeResult::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;
}
void {{Cmd}}::Serialize(size_t commandSize, char* buffer
{%- if not is_return -%}
, const ObjectIdProvider& objectIdProvider
{%- endif -%}
) const {
auto transfer = reinterpret_cast<{{Name}}Transfer*>(buffer);
transfer->commandSize = commandSize;
buffer += sizeof({{Name}}Transfer);
{{Name}}Serialize(*this, transfer, &buffer
{%- if command.may_have_dawn_object -%}
, objectIdProvider
{%- endif -%}
);
}
DeserializeResult {{Cmd}}::Deserialize(const volatile char** buffer, size_t* size, DeserializeAllocator* allocator
{%- if command.may_have_dawn_object -%}
, const ObjectIdResolver& resolver
{%- endif -%}
) {
const volatile {{Name}}Transfer* transfer = nullptr;
DESERIALIZE_TRY(GetPtrFromBuffer(buffer, size, 1, &transfer));
return {{Name}}Deserialize(this, transfer, buffer, size, allocator
{%- if command.may_have_dawn_object -%}
, resolver
{%- endif -%}
);
}
{% endmacro %}
namespace dawn_wire {
// Macro to simplify error handling, similar to DAWN_TRY but for DeserializeResult.
#define DESERIALIZE_TRY(EXPR) \
do { \
DeserializeResult exprResult = EXPR; \
if (exprResult != DeserializeResult::Success) { \
return exprResult; \
} \
} while (0)
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 {
// Consumes from (buffer, size) enough memory to contain T[count] and return it in data.
// Returns FatalError if not enough memory was available
template <typename T>
DeserializeResult GetPtrFromBuffer(const volatile char** buffer, size_t* size, size_t count, const volatile T** data) {
constexpr size_t kMaxCountWithoutOverflows = std::numeric_limits<size_t>::max() / sizeof(T);
if (count > kMaxCountWithoutOverflows) {
return DeserializeResult::FatalError;
}
size_t totalSize = sizeof(T) * count;
if (totalSize > *size) {
return DeserializeResult::FatalError;
}
*data = reinterpret_cast<const volatile T*>(*buffer);
*buffer += totalSize;
*size -= totalSize;
return DeserializeResult::Success;
}
// Allocates enough space from allocator to countain T[count] and return it in out.
// Return FatalError if the allocator couldn't allocate the memory.
template <typename T>
DeserializeResult GetSpace(DeserializeAllocator* allocator, size_t count, T** out) {
constexpr size_t kMaxCountWithoutOverflows = std::numeric_limits<size_t>::max() / sizeof(T);
if (count > kMaxCountWithoutOverflows) {
return DeserializeResult::FatalError;
}
size_t totalSize = sizeof(T) * count;
*out = static_cast<T*>(allocator->GetSpace(totalSize));
if (*out == nullptr) {
return DeserializeResult::FatalError;
}
return DeserializeResult::Success;
}
size_t GetChainedStructExtraRequiredSize(const WGPUChainedStruct* chainedStruct);
void SerializeChainedStruct(WGPUChainedStruct const* chainedStruct,
char** buffer,
const ObjectIdProvider& provider);
DeserializeResult DeserializeChainedStruct(const WGPUChainedStruct** outChainNext,
const volatile char** buffer,
size_t* size,
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).
// Stop iterating because this is an error.
// TODO(crbug.com/dawn/369): Unknown sTypes are silently discarded.
ASSERT(chainedStruct->sType == WGPUSType_Invalid);
result += sizeof(WGPUChainedStructTransfer);
return result;
}
}
return result;
}
void SerializeChainedStruct(WGPUChainedStruct const* chainedStruct,
char** 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)}}: {
auto* transfer = reinterpret_cast<{{CType}}Transfer*>(*buffer);
transfer->chain.sType = chainedStruct->sType;
transfer->chain.hasNext = chainedStruct->next != nullptr;
*buffer += sizeof({{CType}}Transfer);
{{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.
ASSERT(chainedStruct->sType == WGPUSType_Invalid);
WGPUChainedStructTransfer* transfer = reinterpret_cast<WGPUChainedStructTransfer*>(*buffer);
transfer->sType = WGPUSType_Invalid;
transfer->hasNext = false;
*buffer += sizeof(WGPUChainedStructTransfer);
return;
}
}
} while (chainedStruct != nullptr);
}
DeserializeResult DeserializeChainedStruct(const WGPUChainedStruct** outChainNext,
const volatile char** buffer,
size_t* size,
DeserializeAllocator* allocator,
const ObjectIdResolver& resolver) {
bool hasNext;
do {
if (*size < sizeof(WGPUChainedStructTransfer)) {
return DeserializeResult::FatalError;
}
WGPUSType sType =
reinterpret_cast<const volatile WGPUChainedStructTransfer*>(*buffer)->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 = nullptr;
DESERIALIZE_TRY(GetPtrFromBuffer(buffer, size, 1, &transfer));
{{CType}}* outStruct = nullptr;
DESERIALIZE_TRY(GetSpace(allocator, sizeof({{CType}}), &outStruct));
outStruct->chain.sType = sType;
outStruct->chain.next = nullptr;
*outChainNext = &outStruct->chain;
outChainNext = &outStruct->chain.next;
DESERIALIZE_TRY({{CType}}Deserialize(outStruct, transfer, buffer, size, allocator
{%- if types[sType.name.get()].may_have_dawn_object -%}
, resolver
{%- endif -%}
));
hasNext = transfer->chain.hasNext;
} break;
{% endfor %}
default:
return DeserializeResult::FatalError;
}
} while (hasNext);
return DeserializeResult::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 %}
// Implementations of serialization/deserialization of WPGUDeviceProperties.
size_t SerializedWGPUDevicePropertiesSize(const WGPUDeviceProperties* deviceProperties) {
return sizeof(WGPUDeviceProperties) +
WGPUDevicePropertiesGetExtraRequiredSize(*deviceProperties);
}
void SerializeWGPUDeviceProperties(const WGPUDeviceProperties* deviceProperties,
char* serializeBuffer) {
size_t devicePropertiesSize = SerializedWGPUDevicePropertiesSize(deviceProperties);
WGPUDevicePropertiesTransfer* transfer =
reinterpret_cast<WGPUDevicePropertiesTransfer*>(serializeBuffer);
serializeBuffer += devicePropertiesSize;
WGPUDevicePropertiesSerialize(*deviceProperties, transfer, &serializeBuffer);
}
bool DeserializeWGPUDeviceProperties(WGPUDeviceProperties* deviceProperties,
const volatile char* deserializeBuffer) {
size_t devicePropertiesSize = SerializedWGPUDevicePropertiesSize(deviceProperties);
const volatile WGPUDevicePropertiesTransfer* transfer = nullptr;
if (GetPtrFromBuffer(&deserializeBuffer, &devicePropertiesSize, 1, &transfer) !=
DeserializeResult::Success) {
return false;
}
return WGPUDevicePropertiesDeserialize(deviceProperties, transfer, &deserializeBuffer,
&devicePropertiesSize,
nullptr) == DeserializeResult::Success;
}
} // namespace dawn_wire