Add wire_cmd.py and dawn_wire.json to autogenerate all wire commands.

Unify code generation for Client->Server and Server->Client commands.
Methods in dawn.json are converted into command records and additional
commands are specified in dawn_wire.json. This can then be used to
completely generate the command handlers and command struct definitions.

Bug: dawn:88
Change-Id: Ic796796ede0aafe02e14f1f96790324dad92f4c0
Reviewed-on: https://dawn-review.googlesource.com/c/3800
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Austin Eng 2019-01-15 20:49:53 +00:00 committed by Commit Bot service account
parent a483fac90d
commit c7f416c0c9
14 changed files with 822 additions and 661 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
*.pyc
# Directories added by gclient sync and the GN build
.gclient
.gclient_entries

View File

@ -54,6 +54,8 @@ template("dawn_generator") {
# target using templates in this directory.
generator_args = [
rebase_path("dawn.json", root_build_dir),
"--wire-json",
rebase_path("dawn_wire.json", root_build_dir),
"--template-dir",
rebase_path("generator/templates", root_build_dir),
"--targets",
@ -695,8 +697,8 @@ dawn_static_and_shared_library("libdawn_native") {
]
deps = [
":libdawn_native_sources",
":dawn_common",
":libdawn_native_sources",
]
sources = [
"src/dawn_native/DawnNative.cpp",
@ -761,7 +763,10 @@ dawn_static_and_shared_library("libdawn_wire") {
configs = [ ":dawn_internal" ]
sources = get_target_outputs(":libdawn_wire_gen")
sources += [ "src/dawn_wire/WireCmd.h" ]
sources += [
"src/dawn_wire/WireDeserializeAllocator.cpp",
"src/dawn_wire/WireDeserializeAllocator.h",
]
# Make headers publically visible
public_deps = [
@ -849,8 +854,8 @@ test("dawn_unittests") {
":dawn_headers",
":dawn_utils",
":libdawn",
":libdawn_native_sources",
":libdawn_native",
":libdawn_native_sources",
":libdawn_wire",
":mock_dawn_gen",
"third_party:gmock_and_gtest",

View File

@ -1106,6 +1106,18 @@
{"value": 11, "name": "unorm r8 g8"}
]
},
"ObjectType": {
"_comment": "Only used for the wire",
"category": "native"
},
"ObjectId": {
"_comment": "Only used for the wire",
"category": "native"
},
"ObjectHandle": {
"_comment": "Only used for the wire",
"category": "native"
},
"void": {
"category": "native"
},

80
dawn_wire.json Normal file
View File

@ -0,0 +1,80 @@
{
"_comment": [
"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."
],
"commands": {
"buffer map async": [
{ "name": "buffer id", "type": "ObjectId" },
{ "name": "request serial", "type": "uint32_t" },
{ "name": "start", "type": "uint32_t" },
{ "name": "size", "type": "uint32_t" },
{ "name": "is write", "type": "bool" }
],
"buffer update mapped data": [
{ "name": "buffer id", "type": "ObjectId" },
{ "name": "data length", "type": "uint32_t" },
{ "name": "data", "type": "uint8_t", "annotation": "const*", "length": "data length" }
],
"destroy object": [
{ "name": "object type", "type": "ObjectType" },
{ "name": "object id", "type": "ObjectId" }
]
},
"return commands": {
"buffer map read async callback": [
{ "name": "buffer", "type": "ObjectHandle" },
{ "name": "request serial", "type": "uint32_t" },
{ "name": "status", "type": "uint32_t" },
{ "name": "data length", "type": "uint32_t" },
{ "name": "data", "type": "uint8_t", "annotation": "const*", "length": "data length" }
],
"buffer map write async callback": [
{ "name": "buffer", "type": "ObjectHandle" },
{ "name": "request serial", "type": "uint32_t" },
{ "name": "status", "type": "uint32_t" }
],
"device error callback": [
{ "name": "message", "type": "char", "annotation": "const*", "length": "strlen" }
],
"fence update completed value": [
{ "name": "fence", "type": "ObjectHandle" },
{ "name": "value", "type": "uint64_t" }
]
},
"special items": {
"client_side_commands": [
"FenceGetCompletedValue"
],
"client_proxied_commands": [
"BufferUnmap",
"DeviceCreateFence",
"QueueSignal"
],
"client_special_objects": [
"Buffer",
"Device",
"Fence"
],
"server_custom_pre_handler_commands": [
"BufferUnmap"
],
"server_custom_post_handler_commands": [
"QueueSignal"
],
"server_reverse_lookup_objects": [
"Fence"
]
}
}

149
generator/common.py Normal file
View File

@ -0,0 +1,149 @@
# 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.
from collections import namedtuple
class Name:
def __init__(self, name, native=False):
self.native = native
if native:
self.chunks = [name]
else:
self.chunks = name.split(' ')
def CamelChunk(self, chunk):
return chunk[0].upper() + chunk[1:]
def canonical_case(self):
return (' '.join(self.chunks)).lower()
def concatcase(self):
return ''.join(self.chunks)
def camelCase(self):
return self.chunks[0] + ''.join([self.CamelChunk(chunk) for chunk in self.chunks[1:]])
def CamelCase(self):
return ''.join([self.CamelChunk(chunk) for chunk in self.chunks])
def SNAKE_CASE(self):
return '_'.join([chunk.upper() for chunk in self.chunks])
def snake_case(self):
return '_'.join(self.chunks)
class Type:
def __init__(self, name, json_data, native=False):
self.json_data = json_data
self.dict_name = name
self.name = Name(name, native=native)
self.category = json_data['category']
self.is_builder = self.name.canonical_case().endswith(" builder")
EnumValue = namedtuple('EnumValue', ['name', 'value'])
class EnumType(Type):
def __init__(self, name, json_data):
Type.__init__(self, name, json_data)
self.values = [EnumValue(Name(m['name']), m['value']) for m in self.json_data['values']]
BitmaskValue = namedtuple('BitmaskValue', ['name', 'value'])
class BitmaskType(Type):
def __init__(self, name, json_data):
Type.__init__(self, name, json_data)
self.values = [BitmaskValue(Name(m['name']), m['value']) for m in self.json_data['values']]
self.full_mask = 0
for value in self.values:
self.full_mask = self.full_mask | value.value
class NativeType(Type):
def __init__(self, name, json_data):
Type.__init__(self, name, json_data, native=True)
class NativelyDefined(Type):
def __init__(self, name, json_data):
Type.__init__(self, name, json_data)
# Methods and structures are both "records", so record members correspond to
# method arguments or structure members.
class RecordMember:
def __init__(self, name, typ, annotation, optional, is_return_value):
self.name = name
self.type = typ
self.annotation = annotation
self.length = None
self.optional = optional
self.is_return_value = is_return_value
Method = namedtuple('Method', ['name', 'return_type', 'arguments'])
class ObjectType(Type):
def __init__(self, name, json_data):
Type.__init__(self, name, json_data)
self.methods = []
self.native_methods = []
self.built_type = None
class Record:
def __init__(self, name):
self.name = Name(name)
self.members = []
self.has_dawn_object = False
def update_metadata(self):
def has_dawn_object(member):
if isinstance(member.type, ObjectType):
return True
elif isinstance(member.type, StructureType):
return member.type.has_dawn_object
else:
return False
self.has_dawn_object = any(has_dawn_object(member) for member in self.members)
class StructureType(Record, Type):
def __init__(self, name, json_data):
Record.__init__(self, name)
Type.__init__(self, name, json_data)
self.extensible = json_data.get("extensible", False)
class Command(Record):
def __init__(self, name, members=None):
Record.__init__(self, name)
self.members = members or []
self.derived_object = None
self.derived_method = None
def linked_record_members(json_data, types):
members = []
members_by_name = {}
for m in json_data:
member = RecordMember(Name(m['name']), types[m['type']],
m.get('annotation', 'value'), m.get('optional', False),
m.get('is_return_value', False))
members.append(member)
members_by_name[member.name.canonical_case()] = member
for (member, m) in zip(members, json_data):
if member.annotation != 'value':
if not 'length' in m:
if member.type.category == 'structure':
member.length = "constant"
member.constant_length = 1
else:
assert(False)
elif m['length'] == 'strlen':
member.length = 'strlen'
else:
member.length = members_by_name[m['length']]
return members

View File

@ -17,90 +17,9 @@
# COMMON
############################################################
from collections import namedtuple
class Name:
def __init__(self, name, native=False):
self.native = native
if native:
self.chunks = [name]
else:
self.chunks = name.split(' ')
def CamelChunk(self, chunk):
return chunk[0].upper() + chunk[1:]
def canonical_case(self):
return (' '.join(self.chunks)).lower()
def concatcase(self):
return ''.join(self.chunks)
def camelCase(self):
return self.chunks[0] + ''.join([self.CamelChunk(chunk) for chunk in self.chunks[1:]])
def CamelCase(self):
return ''.join([self.CamelChunk(chunk) for chunk in self.chunks])
def SNAKE_CASE(self):
return '_'.join([chunk.upper() for chunk in self.chunks])
def snake_case(self):
return '_'.join(self.chunks)
class Type:
def __init__(self, name, json_data, native=False):
self.json_data = json_data
self.dict_name = name
self.name = Name(name, native=native)
self.category = json_data['category']
self.is_builder = self.name.canonical_case().endswith(" builder")
EnumValue = namedtuple('EnumValue', ['name', 'value'])
class EnumType(Type):
def __init__(self, name, json_data):
Type.__init__(self, name, json_data)
self.values = [EnumValue(Name(m['name']), m['value']) for m in self.json_data['values']]
BitmaskValue = namedtuple('BitmaskValue', ['name', 'value'])
class BitmaskType(Type):
def __init__(self, name, json_data):
Type.__init__(self, name, json_data)
self.values = [BitmaskValue(Name(m['name']), m['value']) for m in self.json_data['values']]
self.full_mask = 0
for value in self.values:
self.full_mask = self.full_mask | value.value
class NativeType(Type):
def __init__(self, name, json_data):
Type.__init__(self, name, json_data, native=True)
class NativelyDefined(Type):
def __init__(self, name, json_data):
Type.__init__(self, name, json_data)
# Methods and structures are both "records", so record members correspond to
# method arguments or structure members.
class RecordMember:
def __init__(self, name, typ, annotation, optional):
self.name = name
self.type = typ
self.annotation = annotation
self.length = None
self.optional = optional
Method = namedtuple('Method', ['name', 'return_type', 'arguments'])
class ObjectType(Type):
def __init__(self, name, json_data):
Type.__init__(self, name, json_data)
self.methods = []
self.native_methods = []
self.built_type = None
class StructureType(Type):
def __init__(self, name, json_data):
Type.__init__(self, name, json_data)
self.extensible = json_data.get("extensible", False)
self.members = []
from common import Name
import common
import wire_cmd
############################################################
# PARSE
@ -111,35 +30,10 @@ def is_native_method(method):
return method.return_type.category == "natively defined" or \
any([arg.type.category == "natively defined" for arg in method.arguments])
def linked_record_members(json_data, types):
members = []
members_by_name = {}
for m in json_data:
member = RecordMember(Name(m['name']), types[m['type']],
m.get('annotation', 'value'), m.get('optional', False))
members.append(member)
members_by_name[member.name.canonical_case()] = member
for (member, m) in zip(members, json_data):
if member.annotation != 'value':
if not 'length' in m:
if member.type.category == 'structure':
member.length = "constant"
member.constant_length = 1
else:
assert(False)
elif m['length'] == 'strlen':
member.length = 'strlen'
else:
member.length = members_by_name[m['length']]
return members
def link_object(obj, types):
def make_method(json_data):
arguments = linked_record_members(json_data.get('args', []), types)
return Method(Name(json_data['name']), types[json_data.get('returns', 'void')], arguments)
arguments = common.linked_record_members(json_data.get('args', []), types)
return common.Method(Name(json_data['name']), types[json_data.get('returns', 'void')], arguments)
methods = [make_method(m) for m in obj.json_data.get('methods', [])]
obj.methods = [method for method in methods if not is_native_method(method)]
@ -154,7 +48,7 @@ def link_object(obj, types):
assert(obj.built_type != None)
def link_structure(struct, types):
struct.members = linked_record_members(struct.json_data['members'], types)
struct.members = common.linked_record_members(struct.json_data['members'], types)
# Sort structures so that if struct A has struct B as a member, then B is listed before A
# This is a form of topological sort where we try to keep the order reasonably similar to the
@ -194,12 +88,12 @@ def topo_sort_structure(structs):
def parse_json(json):
category_to_parser = {
'bitmask': BitmaskType,
'enum': EnumType,
'native': NativeType,
'natively defined': NativelyDefined,
'object': ObjectType,
'structure': StructureType,
'bitmask': common.BitmaskType,
'enum': common.EnumType,
'native': common.NativeType,
'natively defined': common.NativelyDefined,
'object': common.ObjectType,
'structure': common.StructureType,
}
types = {}
@ -227,6 +121,9 @@ def parse_json(json):
by_category['structure'] = topo_sort_structure(by_category['structure'])
for struct in by_category['structure']:
struct.update_metadata()
return {
'types': types,
'by_category': by_category
@ -392,18 +289,18 @@ def cpp_native_methods(types, typ):
methods = typ.methods + typ.native_methods
if typ.is_builder:
methods.append(Method(Name('set error callback'), types['void'], [
RecordMember(Name('callback'), types['builder error callback'], 'value', False),
RecordMember(Name('userdata1'), types['callback userdata'], 'value', False),
RecordMember(Name('userdata2'), types['callback userdata'], 'value', False),
methods.append(common.Method(Name('set error callback'), types['void'], [
common.RecordMember(Name('callback'), types['builder error callback'], 'value', False, False),
common.RecordMember(Name('userdata1'), types['callback userdata'], 'value', False, False),
common.RecordMember(Name('userdata2'), types['callback userdata'], 'value', False, False),
]))
return methods
def c_native_methods(types, typ):
return cpp_native_methods(types, typ) + [
Method(Name('reference'), types['void'], []),
Method(Name('release'), types['void'], []),
common.Method(Name('reference'), types['void'], []),
common.Method(Name('release'), types['void'], []),
]
def js_native_methods(types, typ):
@ -412,7 +309,7 @@ def js_native_methods(types, typ):
def debug(text):
print(text)
def get_renders_for_targets(api_params, targets):
def get_renders_for_targets(api_params, wire_json, targets):
base_params = {
'enumerate': enumerate,
'format': format,
@ -471,17 +368,20 @@ def get_renders_for_targets(api_params, targets):
renders.append(FileRender('dawn_native/ProcTable.cpp', 'dawn_native/ProcTable.cpp', frontend_params))
if 'dawn_wire' in targets:
additional_params = wire_cmd.compute_wire_params(api_params, wire_json)
wire_params = [
base_params,
api_params,
c_params,
{
'as_wireType': lambda typ: typ.name.CamelCase() + '*' if typ.category == 'object' else as_cppType(typ.name)
}
},
additional_params
]
renders.append(FileRender('dawn_wire/TypeTraits.h', 'dawn_wire/TypeTraits_autogen.h', wire_params))
renders.append(FileRender('dawn_wire/WireCmd.h', 'dawn_wire/WireCmd_autogen.h', wire_params))
renders.append(FileRender('dawn_wire/WireCmd.cpp', 'dawn_wire/WireCmd_autogen.cpp', wire_params))
renders.append(FileRender('dawn_wire/TypeTraits.h', 'dawn_wire/TypeTraits_autogen.h', wire_params))
renders.append(FileRender('dawn_wire/WireClient.cpp', 'dawn_wire/WireClient.cpp', wire_params))
renders.append(FileRender('dawn_wire/WireServer.cpp', 'dawn_wire/WireServer.cpp', wire_params))
@ -507,6 +407,7 @@ def main():
formatter_class = argparse.ArgumentDefaultsHelpFormatter
)
parser.add_argument('json', metavar='DAWN_JSON', nargs=1, type=str, help ='The DAWN JSON definition to use.')
parser.add_argument('--wire-json', default=None, type=str, help='The DAWN WIRE JSON definition to use.')
parser.add_argument('-t', '--template-dir', default='templates', type=str, help='Directory with template files.')
parser.add_argument('-T', '--targets', required=True, type=str, help='Comma-separated subset of targets to output. Available targets: ' + ', '.join(allowed_targets))
parser.add_argument(kExtraPythonPath, default=None, type=str, help='Additional python path to set before loading Jinja2')
@ -522,7 +423,17 @@ def main():
api_params = parse_json(loaded_json)
targets = args.targets.split(',')
renders = get_renders_for_targets(api_params, targets)
dependencies = [
os.path.join(os.path.abspath(os.path.dirname(__file__)), "common.py")
]
loaded_wire_json = None
if args.wire_json:
with open(args.wire_json) as f:
loaded_wire_json = json.loads(f.read())
dependencies.append(args.wire_json)
renders = get_renders_for_targets(api_params, loaded_wire_json, targets)
# The caller wants to assert that the outputs are what it expects.
# Load the file and compare with our renders.
@ -544,8 +455,9 @@ def main():
if args.output_json_tarball != None:
output_to_json(outputs, args.output_json_tarball)
dependencies = [args.template_dir + os.path.sep + render.template for render in renders]
dependencies += [args.template_dir + os.path.sep + render.template for render in renders]
dependencies.append(args.json[0])
dependencies.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), "wire_cmd.py"))
output_depfile(args.depfile, args.output_json_tarball, dependencies)
if __name__ == '__main__':

View File

@ -13,7 +13,8 @@
//* limitations under the License.
#include "dawn_wire/Wire.h"
#include "dawn_wire/WireCmd.h"
#include "dawn_wire/WireCmd_autogen.h"
#include "dawn_wire/WireDeserializeAllocator.h"
#include "common/Assert.h"
#include "common/SerialMap.h"
@ -66,12 +67,7 @@ namespace dawn_wire {
BuilderCallbackData builderCallback;
};
{% set special_objects = [
"device",
"buffer",
"fence",
] %}
{% for type in by_category["object"] if not type.name.canonical_case() in special_objects %}
{% for type in by_category["object"] if not type.name.CamelCase() in client_special_objects %}
struct {{type.name.CamelCase()}} : ObjectBase {
using ObjectBase::ObjectBase;
};
@ -269,8 +265,6 @@ namespace dawn_wire {
CommandSerializer* mSerializer = nullptr;
};
{% set client_side_commands = ["FenceGetCompletedValue"] %}
//* Implementation of the client API functions.
{% for type in by_category["object"] %}
{% set Type = type.name.CamelCase() %}
@ -305,8 +299,7 @@ namespace dawn_wire {
self->builderCallback.canCall = false;
{% endif %}
cmd.resultId = allocation->object->id;
cmd.resultSerial = allocation->serial;
cmd.result = ObjectHandle{allocation->object->id, allocation->serial};
{% endif %}
{% for arg in method.arguments %}
@ -353,8 +346,9 @@ namespace dawn_wire {
cmd.objectType = ObjectType::{{type.name.CamelCase()}};
cmd.objectId = obj->id;
auto allocCmd = static_cast<decltype(cmd)*>(obj->device->GetCmdSpace(sizeof(cmd)));
*allocCmd = cmd;
size_t requiredSize = cmd.GetRequiredSize();
char* allocatedBuffer = static_cast<char*>(obj->device->GetCmdSpace(requiredSize));
cmd.Serialize(allocatedBuffer);
obj->device->{{type.name.camelCase()}}.Free(obj);
}
@ -386,8 +380,9 @@ namespace dawn_wire {
cmd.size = size;
cmd.isWrite = false;
auto allocCmd = static_cast<decltype(cmd)*>(buffer->device->GetCmdSpace(sizeof(cmd)));
*allocCmd = cmd;
size_t requiredSize = cmd.GetRequiredSize();
char* allocatedBuffer = static_cast<char*>(buffer->device->GetCmdSpace(requiredSize));
cmd.Serialize(allocatedBuffer);
}
void ClientBufferMapWriteAsync(dawnBuffer cBuffer, uint32_t start, uint32_t size, dawnBufferMapWriteCallback callback, dawnCallbackUserdata userdata) {
@ -410,8 +405,9 @@ namespace dawn_wire {
cmd.size = size;
cmd.isWrite = true;
auto allocCmd = static_cast<decltype(cmd)*>(buffer->device->GetCmdSpace(sizeof(cmd)));
*allocCmd = cmd;
size_t requiredSize = cmd.GetRequiredSize();
char* allocatedBuffer = static_cast<char*>(buffer->device->GetCmdSpace(requiredSize));
cmd.Serialize(allocatedBuffer);
}
uint64_t ClientFenceGetCompletedValue(dawnFence cSelf) {
@ -458,12 +454,11 @@ namespace dawn_wire {
BufferUpdateMappedDataCmd cmd;
cmd.bufferId = buffer->id;
cmd.dataLength = static_cast<uint32_t>(buffer->mappedDataSize);
cmd.data = reinterpret_cast<const uint8_t*>(buffer->mappedData);
auto allocCmd = static_cast<decltype(cmd)*>(buffer->device->GetCmdSpace(sizeof(cmd)));
*allocCmd = cmd;
void* dataAlloc = buffer->device->GetCmdSpace(cmd.dataLength);
memcpy(dataAlloc, buffer->mappedData, cmd.dataLength);
size_t requiredSize = cmd.GetRequiredSize();
char* allocatedBuffer = static_cast<char*>(buffer->device->GetCmdSpace(requiredSize));
cmd.Serialize(allocatedBuffer);
}
free(buffer->mappedData);
@ -510,14 +505,12 @@ namespace dawn_wire {
// - An autogenerated Client{{suffix}} method that sends the command on the wire
// - A manual ProxyClient{{suffix}} method that will be inserted in the proctable instead of
// the autogenerated one, and that will have to call Client{{suffix}}
{% set proxied_commands = ["BufferUnmap", "DeviceCreateFence", "QueueSignal"] %}
dawnProcTable GetProcs() {
dawnProcTable table;
{% for type in by_category["object"] %}
{% for method in native_methods(type) %}
{% set suffix = as_MethodSuffix(type.name, method.name) %}
{% if suffix in proxied_commands %}
{% if suffix in client_proxied_commands %}
table.{{as_varName(type.name, method.name)}} = ProxyClient{{suffix}};
{% else %}
table.{{as_varName(type.name, method.name)}} = Client{{suffix}};
@ -538,23 +531,11 @@ namespace dawn_wire {
bool success = false;
switch (cmdId) {
case ReturnWireCmd::DeviceErrorCallback:
success = HandleDeviceErrorCallbackCmd(&commands, &size);
break;
{% for type in by_category["object"] if type.is_builder %}
case ReturnWireCmd::{{type.name.CamelCase()}}ErrorCallback:
success = Handle{{type.name.CamelCase()}}ErrorCallbackCmd(&commands, &size);
{% for command in cmd_records["return command"] %}
case ReturnWireCmd::{{command.name.CamelCase()}}:
success = Handle{{command.name.CamelCase()}}(&commands, &size);
break;
{% endfor %}
case ReturnWireCmd::BufferMapReadAsyncCallback:
success = HandleBufferMapReadAsyncCallback(&commands, &size);
break;
case ReturnWireCmd::BufferMapWriteAsyncCallback:
success = HandleBufferMapWriteAsyncCallback(&commands, &size);
break;
case ReturnWireCmd::FenceUpdateCompletedValue:
success = HandleFenceUpdateCompletedValue(&commands, &size);
break;
default:
success = false;
}
@ -562,6 +543,7 @@ namespace dawn_wire {
if (!success) {
return nullptr;
}
mAllocator.Reset();
}
if (size != 0) {
@ -573,72 +555,47 @@ namespace dawn_wire {
private:
Device* mDevice = nullptr;
WireDeserializeAllocator mAllocator;
//* Helper function for the getting of the command data in command handlers.
//* Checks there is enough data left, updates the buffer / size and returns
//* the command (or nullptr for an error).
template <typename T>
static const T* GetData(const char** buffer, size_t* size, size_t count) {
// TODO(cwallez@chromium.org): Check for overflow
size_t totalSize = count * sizeof(T);
if (*size < totalSize) {
return nullptr;
}
bool HandleDeviceErrorCallback(const char** commands, size_t* size) {
ReturnDeviceErrorCallbackCmd cmd;
DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator);
const T* data = reinterpret_cast<const T*>(*buffer);
*buffer += totalSize;
*size -= totalSize;
return data;
}
template <typename T>
static const T* GetCommand(const char** commands, size_t* size) {
return GetData<T>(commands, size, 1);
}
bool HandleDeviceErrorCallbackCmd(const char** commands, size_t* size) {
const auto* cmd = GetCommand<ReturnDeviceErrorCallbackCmd>(commands, size);
if (cmd == nullptr) {
if (deserializeResult == DeserializeResult::FatalError) {
return false;
}
const char* message = GetData<char>(commands, size, cmd->messageStrlen + 1);
if (message == nullptr || message[cmd->messageStrlen] != '\0') {
return false;
}
mDevice->HandleError(message);
DAWN_ASSERT(cmd.message != nullptr);
mDevice->HandleError(cmd.message);
return true;
}
{% for type in by_category["object"] if type.is_builder %}
{% set Type = type.name.CamelCase() %}
bool Handle{{Type}}ErrorCallbackCmd(const char** commands, size_t* size) {
const auto* cmd = GetCommand<Return{{Type}}ErrorCallbackCmd>(commands, size);
if (cmd == nullptr) {
bool Handle{{Type}}ErrorCallback(const char** commands, size_t* size) {
Return{{Type}}ErrorCallbackCmd cmd;
DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator);
if (deserializeResult == DeserializeResult::FatalError) {
return false;
}
const char* message = GetData<char>(commands, size, cmd->messageStrlen + 1);
if (message == nullptr || message[cmd->messageStrlen] != '\0') {
return false;
}
DAWN_ASSERT(cmd.message != nullptr);
auto* builtObject = mDevice->{{type.built_type.name.camelCase()}}.GetObject(cmd->builtObjectId);
uint32_t objectSerial = mDevice->{{type.built_type.name.camelCase()}}.GetSerial(cmd->builtObjectId);
auto* builtObject = mDevice->{{type.built_type.name.camelCase()}}.GetObject(cmd.builtObject.id);
uint32_t objectSerial = mDevice->{{type.built_type.name.camelCase()}}.GetSerial(cmd.builtObject.id);
//* The object might have been deleted or a new object created with the same ID.
if (builtObject == nullptr || objectSerial != cmd->builtObjectSerial) {
if (builtObject == nullptr || objectSerial != cmd.builtObject.serial) {
return true;
}
bool called = builtObject->builderCallback.Call(static_cast<dawnBuilderErrorStatus>(cmd->status), message);
bool called = builtObject->builderCallback.Call(static_cast<dawnBuilderErrorStatus>(cmd.status), cmd.message);
// Unhandled builder errors are forwarded to the device
if (!called && cmd->status != DAWN_BUILDER_ERROR_STATUS_SUCCESS && cmd->status != DAWN_BUILDER_ERROR_STATUS_UNKNOWN) {
mDevice->HandleError(("Unhandled builder error: " + std::string(message)).c_str());
if (!called && cmd.status != DAWN_BUILDER_ERROR_STATUS_SUCCESS && cmd.status != DAWN_BUILDER_ERROR_STATUS_UNKNOWN) {
mDevice->HandleError(("Unhandled builder error: " + std::string(cmd.message)).c_str());
}
return true;
@ -646,31 +603,23 @@ namespace dawn_wire {
{% endfor %}
bool HandleBufferMapReadAsyncCallback(const char** commands, size_t* size) {
const auto* cmd = GetCommand<ReturnBufferMapReadAsyncCallbackCmd>(commands, size);
if (cmd == nullptr) {
ReturnBufferMapReadAsyncCallbackCmd cmd;
DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator);
if (deserializeResult == DeserializeResult::FatalError) {
return false;
}
//* Unconditionnally get the data from the buffer so that the correct amount of data is
//* consumed from the buffer, even when we ignore the command and early out.
const char* requestData = nullptr;
if (cmd->status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS) {
requestData = GetData<char>(commands, size, cmd->dataLength);
if (requestData == nullptr) {
return false;
}
}
auto* buffer = mDevice->buffer.GetObject(cmd->bufferId);
uint32_t bufferSerial = mDevice->buffer.GetSerial(cmd->bufferId);
auto* buffer = mDevice->buffer.GetObject(cmd.buffer.id);
uint32_t bufferSerial = mDevice->buffer.GetSerial(cmd.buffer.id);
//* The buffer might have been deleted or recreated so this isn't an error.
if (buffer == nullptr || bufferSerial != cmd->bufferSerial) {
if (buffer == nullptr || bufferSerial != cmd.buffer.serial) {
return true;
}
//* The requests can have been deleted via an Unmap so this isn't an error.
auto requestIt = buffer->requests.find(cmd->requestSerial);
auto requestIt = buffer->requests.find(cmd.requestSerial);
if (requestIt == buffer->requests.end()) {
return true;
}
@ -686,14 +635,14 @@ namespace dawn_wire {
buffer->requests.erase(requestIt);
//* On success, we copy the data locally because the IPC buffer isn't valid outside of this function
if (cmd->status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS) {
if (cmd.status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS) {
//* The server didn't send the right amount of data, this is an error and could cause
//* the application to crash if we did call the callback.
if (request.size != cmd->dataLength) {
if (request.size != cmd.dataLength) {
return false;
}
ASSERT(requestData != nullptr);
ASSERT(cmd.data != nullptr);
if (buffer->mappedData != nullptr) {
return false;
@ -702,32 +651,34 @@ namespace dawn_wire {
buffer->isWriteMapped = false;
buffer->mappedDataSize = request.size;
buffer->mappedData = malloc(request.size);
memcpy(buffer->mappedData, requestData, request.size);
memcpy(buffer->mappedData, cmd.data, request.size);
request.readCallback(static_cast<dawnBufferMapAsyncStatus>(cmd->status), buffer->mappedData, request.userdata);
request.readCallback(static_cast<dawnBufferMapAsyncStatus>(cmd.status), buffer->mappedData, request.userdata);
} else {
request.readCallback(static_cast<dawnBufferMapAsyncStatus>(cmd->status), nullptr, request.userdata);
request.readCallback(static_cast<dawnBufferMapAsyncStatus>(cmd.status), nullptr, request.userdata);
}
return true;
}
bool HandleBufferMapWriteAsyncCallback(const char** commands, size_t* size) {
const auto* cmd = GetCommand<ReturnBufferMapWriteAsyncCallbackCmd>(commands, size);
if (cmd == nullptr) {
ReturnBufferMapWriteAsyncCallbackCmd cmd;
DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator);
if (deserializeResult == DeserializeResult::FatalError) {
return false;
}
auto* buffer = mDevice->buffer.GetObject(cmd->bufferId);
uint32_t bufferSerial = mDevice->buffer.GetSerial(cmd->bufferId);
auto* buffer = mDevice->buffer.GetObject(cmd.buffer.id);
uint32_t bufferSerial = mDevice->buffer.GetSerial(cmd.buffer.id);
//* The buffer might have been deleted or recreated so this isn't an error.
if (buffer == nullptr || bufferSerial != cmd->bufferSerial) {
if (buffer == nullptr || bufferSerial != cmd.buffer.serial) {
return true;
}
//* The requests can have been deleted via an Unmap so this isn't an error.
auto requestIt = buffer->requests.find(cmd->requestSerial);
auto requestIt = buffer->requests.find(cmd.requestSerial);
if (requestIt == buffer->requests.end()) {
return true;
}
@ -742,7 +693,7 @@ namespace dawn_wire {
buffer->requests.erase(requestIt);
//* On success, we copy the data locally because the IPC buffer isn't valid outside of this function
if (cmd->status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS) {
if (cmd.status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS) {
if (buffer->mappedData != nullptr) {
return false;
}
@ -752,29 +703,31 @@ namespace dawn_wire {
buffer->mappedData = malloc(request.size);
memset(buffer->mappedData, 0, request.size);
request.writeCallback(static_cast<dawnBufferMapAsyncStatus>(cmd->status), buffer->mappedData, request.userdata);
request.writeCallback(static_cast<dawnBufferMapAsyncStatus>(cmd.status), buffer->mappedData, request.userdata);
} else {
request.writeCallback(static_cast<dawnBufferMapAsyncStatus>(cmd->status), nullptr, request.userdata);
request.writeCallback(static_cast<dawnBufferMapAsyncStatus>(cmd.status), nullptr, request.userdata);
}
return true;
}
bool HandleFenceUpdateCompletedValue(const char** commands, size_t* size) {
const auto* cmd = GetCommand<ReturnFenceUpdateCompletedValueCmd>(commands, size);
if (cmd == nullptr) {
ReturnFenceUpdateCompletedValueCmd cmd;
DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator);
if (deserializeResult == DeserializeResult::FatalError) {
return false;
}
auto* fence = mDevice->fence.GetObject(cmd->fenceId);
uint32_t fenceSerial = mDevice->fence.GetSerial(cmd->fenceId);
auto* fence = mDevice->fence.GetObject(cmd.fence.id);
uint32_t fenceSerial = mDevice->fence.GetSerial(cmd.fence.id);
//* The fence might have been deleted or recreated so this isn't an error.
if (fence == nullptr || fenceSerial != cmd->fenceSerial) {
if (fence == nullptr || fenceSerial != cmd.fence.serial) {
return true;
}
fence->completedValue = cmd->value;
fence->completedValue = cmd.value;
fence->CheckPassedFences();
return true;
}

View File

@ -12,7 +12,7 @@
//* See the License for the specific language governing permissions and
//* limitations under the License.
#include "dawn_wire/WireCmd.h"
#include "dawn_wire/WireCmd_autogen.h"
#include "common/Assert.h"
@ -49,10 +49,14 @@
//* 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 "" %}
{%- set Optional = "Optional" if member.optional else "" -%}
{{out}} = provider.Get{{Optional}}Id({{in}});
{% elif member.type.category == "structure"%}
{{as_cType(member.type.name)}}Serialize({{in}}, &{{out}}, buffer, provider);
{%- elif member.type.category == "structure" -%}
{{as_cType(member.type.name)}}Serialize({{in}}, &{{out}}, buffer
{%- if member.type.has_dawn_object -%}
, provider
{%- endif -%}
);
{%- else -%}
{{out}} = {{in}};
{%- endif -%}
@ -61,42 +65,34 @@
//* 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 "" %}
{%- 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, resolver));
{%- elif member.type.category == "structure" -%}
DESERIALIZE_TRY({{as_cType(member.type.name)}}Deserialize(&{{out}}, &{{in}}, buffer, size, allocator
{%- if member.type.has_dawn_object -%}
, resolver
{%- endif -%}
));
{%- else -%}
{{out}} = {{in}};
{%- endif -%}
{% endmacro %}
//* 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_serialization_methods(name, members, as_method=None, as_struct=None, is_return_command=False) %}
{% macro write_record_serialization_helpers(record, name, members, is_cmd=False, is_method=False, is_return_command=False) %}
{% set Return = "Return" if is_return_command else "" %}
{% set is_method = as_method != None %}
{% set is_struct = as_struct != None %}
{% set Cmd = "Cmd" 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 {{name}}Transfer {
{% if is_method %}
struct {{Return}}{{name}}Transfer {
{% if is_cmd %}
//* Start the transfer structure with the command ID, so that casting to WireCmd gives the ID.
{{Return}}WireCmd commandId;
//* Methods always have an implicit "self" argument.
ObjectId self;
//* Methods that return objects need to declare to the server which ID will be used for the
//* return value.
{% if as_method.return_type.category == "object" %}
ObjectId resultId;
ObjectSerial resultSerial;
{% endif %}
{% endif %}
//* Value types are directly in the command, objects being replaced with their IDs.
@ -111,7 +107,7 @@
};
//* Returns the required transfer size for `record` in addition to the transfer structure.
DAWN_DECLARE_UNUSED size_t {{name}}GetExtraRequiredSize(const {{name}}& record) {
DAWN_DECLARE_UNUSED size_t {{Return}}{{name}}GetExtraRequiredSize(const {{Return}}{{name}}{{Cmd}}& record) {
DAWN_UNUSED(record);
size_t result = 0;
@ -140,24 +136,21 @@
}
// GetExtraRequiredSize isn't used for structures that are value members of other structures
// because we assume they cannot contain pointers themselves.
DAWN_UNUSED_FUNC({{name}}GetExtraRequiredSize);
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.
void {{name}}Serialize(const {{name}}& record, {{name}}Transfer* transfer,
char** buffer, const ObjectIdProvider& provider) {
DAWN_UNUSED(provider);
void {{Return}}{{name}}Serialize(const {{Return}}{{name}}{{Cmd}}& record, {{Return}}{{name}}Transfer* transfer,
char** buffer
{%- if record.has_dawn_object -%}
, const ObjectIdProvider& provider
{%- endif -%}
) {
DAWN_UNUSED(buffer);
//* Handle special transfer members of methods.
{% if is_method %}
{% if as_method.return_type.category == "object" %}
transfer->resultId = record.resultId;
transfer->resultSerial = record.resultSerial;
{% endif %}
{% if is_cmd %}
transfer->commandId = {{Return}}WireCmd::{{name}};
transfer->self = provider.GetId(record.self);
{% endif %}
//* Value types are directly in the transfer record, objects being replaced with their IDs.
@ -193,30 +186,56 @@
//* 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.
DeserializeResult {{name}}Deserialize({{name}}* record, const {{name}}Transfer* transfer,
const char** buffer, size_t* size, DeserializeAllocator* allocator, const ObjectIdResolver& resolver) {
DeserializeResult {{Return}}{{name}}Deserialize({{Return}}{{name}}{{Cmd}}* record, const {{Return}}{{name}}Transfer* transfer,
const char** buffer, size_t* size, DeserializeAllocator* allocator
{%- if record.has_dawn_object -%}
, const ObjectIdResolver& resolver
{%- endif -%}
) {
DAWN_UNUSED(allocator);
DAWN_UNUSED(resolver);
DAWN_UNUSED(buffer);
DAWN_UNUSED(size);
{% if is_cmd %}
ASSERT(transfer->commandId == {{Return}}WireCmd::{{name}});
{% endif %}
//* First assign result ObjectHandles:
//* Deserialize guarantees they are filled even if there is an ID for an error object
//* for the Maybe monad mechanism.
//* TODO(enga): This won't need to be done first once we have "WebGPU error handling".
{% set return_handles = members
|selectattr("is_return_value")
|selectattr("annotation", "equalto", "value")
|selectattr("type.dict_name", "equalto", "ObjectHandle")
|list %}
//* Strip return_handles so we don't deserialize it again
{% set members = members|reject("in", return_handles)|list %}
{% for member in return_handles %}
{% set memberName = as_varName(member.name) %}
{{deserialize_member(member, "transfer->" + memberName, "record->" + memberName)}}
{% endfor %}
//* Handle special transfer members for methods
{% if is_method %}
{% if as_method.return_type.category == "object" %}
record->resultId = transfer->resultId;
record->resultSerial = transfer->resultSerial;
{% endif %}
ASSERT(transfer->commandId == {{Return}}WireCmd::{{name}});
//* First assign selfId:
//* Deserialize guarantees they are filled even if there is an ID for an error object
//* for the Maybe monad mechanism.
//* TODO(enga): This won't need to be done first once we have "WebGPU error handling".
//* We can also remove is_method
record->selfId = transfer->self;
//* This conversion is done after the copying of result* and selfId: Deserialize
//* guarantees they are filled even if there is an ID for an error object for the
//* Maybe monad mechanism.
DESERIALIZE_TRY(resolver.GetFromId(record->selfId, &record->self));
//* Strip self so we don't deserialize it again
{% set members = members|rejectattr("name.chunks", "equalto", ["self"])|list %}
//* The object resolver returns a success even if the object is null because the
//* frontend is reponsible to validate that (null objects sometimes have special
//* frontend is responsible to validate that (null objects sometimes have special
//* meanings). However it is never valid to call a method on a null object so we
//* can error out in that case.
if (record->self == nullptr) {
@ -224,7 +243,7 @@
}
{% endif %}
{% if is_struct and as_struct.extensible %}
{% if record.extensible %}
record->nextInChain = nullptr;
{% endif %}
@ -272,6 +291,47 @@
}
{% 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(char* buffer
{%- if command.has_dawn_object -%}
, const ObjectIdProvider& objectIdProvider
{%- endif -%}
) const {
auto transfer = reinterpret_cast<{{Name}}Transfer*>(buffer);
buffer += sizeof({{Name}}Transfer);
{{Name}}Serialize(*this, transfer, &buffer
{%- if command.has_dawn_object -%}
, objectIdProvider
{%- endif -%}
);
}
DeserializeResult {{Cmd}}::Deserialize(const char** buffer, size_t* size, DeserializeAllocator* allocator
{%- if command.has_dawn_object -%}
, const ObjectIdResolver& resolver
{%- endif -%}
) {
const {{Name}}Transfer* transfer = nullptr;
DESERIALIZE_TRY(GetPtrFromBuffer(buffer, size, 1, &transfer));
return {{Name}}Deserialize(this, transfer, buffer, size, allocator
{%- if command.has_dawn_object -%}
, resolver
{%- endif -%}
);
}
{% endmacro %}
namespace dawn_wire {
// Macro to simplify error handling, similar to DAWN_TRY but for DeserializeResult.
@ -324,46 +384,35 @@ namespace dawn_wire {
return DeserializeResult::Success;
}
//* Output structure [de]serialization first because it is used by methods.
//* Output structure [de]serialization first because it is used by commands.
{% for type in by_category["structure"] %}
{% set name = as_cType(type.name) %}
{{write_serialization_methods(name, type.members, as_struct=type)}}
{{write_record_serialization_helpers(type, name, type.members,
is_cmd=False)}}
{% endfor %}
// * Output [de]serialization helpers for methods
{% for type in by_category["object"] %}
{% for method in type.methods %}
{% set name = as_MethodSuffix(type.name, method.name) %}
//* 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, is_method=command.derived_method != None)}}
{% endfor %}
using {{name}} = {{name}}Cmd;
{{write_serialization_methods(name, method.arguments, as_method=method)}}
{% 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_method=command.derived_method != None,
is_return_command=True)}}
{% endfor %}
} // anonymous namespace
{% for type in by_category["object"] %}
{% for method in type.methods %}
{% set name = as_MethodSuffix(type.name, method.name) %}
{% set Cmd = name + "Cmd" %}
{% for command in cmd_records["command"] %}
{{ write_command_serialization_methods(command, False) }}
{% endfor %}
size_t {{Cmd}}::GetRequiredSize() const {
return sizeof({{name}}Transfer) + {{name}}GetExtraRequiredSize(*this);
}
void {{Cmd}}::Serialize(char* buffer, const ObjectIdProvider& objectIdProvider) const {
auto transfer = reinterpret_cast<{{name}}Transfer*>(buffer);
buffer += sizeof({{name}}Transfer);
{{name}}Serialize(*this, transfer, &buffer, objectIdProvider);
}
DeserializeResult {{Cmd}}::Deserialize(const char** buffer, size_t* size, DeserializeAllocator* allocator, const ObjectIdResolver& resolver) {
const {{name}}Transfer* transfer = nullptr;
DESERIALIZE_TRY(GetPtrFromBuffer(buffer, size, 1, &transfer));
return {{name}}Deserialize(this, transfer, buffer, size, allocator, resolver);
}
{% endfor %}
{% for command in cmd_records["return command"] %}
{{ write_command_serialization_methods(command, True) }}
{% endfor %}
} // namespace dawn_wire

View File

@ -15,10 +15,16 @@
#ifndef DAWNWIRE_WIRECMD_AUTOGEN_H_
#define DAWNWIRE_WIRECMD_AUTOGEN_H_
#include <dawn/dawn.h>
namespace dawn_wire {
using ObjectId = uint32_t;
using ObjectSerial = uint32_t;
struct ObjectHandle {
ObjectId id;
ObjectSerial serial;
};
enum class DeserializeResult {
Success,
@ -61,84 +67,68 @@ namespace dawn_wire {
//* Enum used as a prefix to each command on the wire format.
enum class WireCmd : uint32_t {
{% for type in by_category["object"] %}
{% for method in type.methods %}
{{as_MethodSuffix(type.name, method.name)}},
{% endfor %}
{% for command in cmd_records["command"] %}
{{command.name.CamelCase()}},
{% endfor %}
BufferMapAsync,
BufferUpdateMappedDataCmd,
DestroyObject,
};
{% for type in by_category["object"] %}
{% for method in type.methods %}
{% set Suffix = as_MethodSuffix(type.name, method.name) %}
{% set Cmd = Suffix + "Cmd" %}
//* These are "structure" version of the list of arguments to the different Dawn methods.
//* They provide helpers to serialize/deserialize to/from a buffer.
struct {{Cmd}} {
//* From a filled structure, compute how much size will be used in the serialization buffer.
size_t GetRequiredSize() const;
//* Serialize the structure and everything it points to into serializeBuffer which must be
//* big enough to contain all the data (as queried from GetRequiredSize).
void Serialize(char* serializeBuffer, const ObjectIdProvider& objectIdProvider) const;
//* Deserializes the structure from a buffer, consuming a maximum of *size bytes. When this
//* function returns, buffer and size will be updated by the number of bytes consumed to
//* deserialize the structure. Structures containing pointers will use allocator to get
//* scratch space to deserialize the pointed-to data.
//* Deserialize returns:
//* - Success if everything went well (yay!)
//* - FatalError is something bad happened (buffer too small for example)
//* - ErrorObject if one if the deserialized object is an error value, for the implementation
//* of the Maybe monad.
//* If the return value is not FatalError, selfId, resultId and resultSerial (if present) are
//* filled.
DeserializeResult Deserialize(const char** buffer, size_t* size, DeserializeAllocator* allocator, const ObjectIdResolver& resolver);
{{as_cType(type.name)}} self;
//* Command handlers want to know the object ID in addition to the backing object.
//* Doesn't need to be filled before Serialize, or GetRequiredSize.
ObjectId selfId;
//* Commands creating objects say which ID the created object will be referred as.
{% if method.return_type.category == "object" %}
ObjectId resultId;
ObjectSerial resultSerial;
{% endif %}
{% for arg in method.arguments %}
{{as_annotated_cType(arg)}};
{% endfor %}
};
{% endfor %}
{% endfor %}
//* Enum used as a prefix to each command on the return wire format.
enum class ReturnWireCmd : uint32_t {
DeviceErrorCallback,
{% for type in by_category["object"] if type.is_builder %}
{{type.name.CamelCase()}}ErrorCallback,
{% for command in cmd_records["return command"] %}
{{command.name.CamelCase()}},
{% endfor %}
BufferMapReadAsyncCallback,
BufferMapWriteAsyncCallback,
FenceUpdateCompletedValue,
};
//* Command for the server calling a builder status callback.
{% for type in by_category["object"] if type.is_builder %}
struct Return{{type.name.CamelCase()}}ErrorCallbackCmd {
ReturnWireCmd commandId = ReturnWireCmd::{{type.name.CamelCase()}}ErrorCallback;
{% macro write_command_struct(command, is_return_command) %}
{% set Return = "Return" if is_return_command else "" %}
{% set Cmd = command.name.CamelCase() + "Cmd" %}
struct {{Return}}{{Cmd}} {
//* From a filled structure, compute how much size will be used in the serialization buffer.
size_t GetRequiredSize() const;
ObjectId builtObjectId;
ObjectSerial builtObjectSerial;
uint32_t status;
size_t messageStrlen;
};
//* Serialize the structure and everything it points to into serializeBuffer which must be
//* big enough to contain all the data (as queried from GetRequiredSize).
void Serialize(char* serializeBuffer
{%- if command.has_dawn_object -%}
, const ObjectIdProvider& objectIdProvider
{%- endif -%}
) const;
//* Deserializes the structure from a buffer, consuming a maximum of *size bytes. When this
//* function returns, buffer and size will be updated by the number of bytes consumed to
//* deserialize the structure. Structures containing pointers will use allocator to get
//* scratch space to deserialize the pointed-to data.
//* Deserialize returns:
//* - Success if everything went well (yay!)
//* - FatalError is something bad happened (buffer too small for example)
//* - ErrorObject if one if the deserialized object is an error value, for the implementation
//* of the Maybe monad.
//* If the return value is not FatalError, selfId, resultId and resultSerial (if present) are
//* filled.
DeserializeResult Deserialize(const char** buffer, size_t* size, DeserializeAllocator* allocator
{%- if command.has_dawn_object -%}
, const ObjectIdResolver& resolver
{%- endif -%}
);
{% if command.derived_method %}
//* Command handlers want to know the object ID in addition to the backing object.
//* Doesn't need to be filled before Serialize, or GetRequiredSize.
ObjectId selfId;
{% endif %}
{% for member in command.members %}
{{as_annotated_cType(member)}};
{% endfor %}
};
{% endmacro %}
{% for command in cmd_records["command"] %}
{{write_command_struct(command, False)}}
{% endfor %}
{% for command in cmd_records["return command"] %}
{{write_command_struct(command, True)}}
{% endfor %}
} // namespace dawn_wire

View File

@ -14,7 +14,8 @@
#include "dawn_wire/TypeTraits_autogen.h"
#include "dawn_wire/Wire.h"
#include "dawn_wire/WireCmd.h"
#include "dawn_wire/WireCmd_autogen.h"
#include "dawn_wire/WireDeserializeAllocator.h"
#include "common/Assert.h"
@ -32,8 +33,7 @@ namespace dawn_wire {
struct MapUserdata {
Server* server;
uint32_t bufferId;
uint32_t bufferSerial;
ObjectHandle buffer;
uint32_t requestSerial;
uint32_t size;
bool isWrite;
@ -41,8 +41,7 @@ namespace dawn_wire {
struct FenceCompletionUserdata {
Server* server;
uint32_t fenceId;
uint32_t fenceSerial;
ObjectHandle fence;
uint64_t value;
};
@ -69,8 +68,7 @@ namespace dawn_wire {
template <typename T>
struct ObjectData<T, true> : public ObjectDataBase<T> {
uint32_t builtObjectId = 0;
uint32_t builtObjectSerial = 0;
ObjectHandle builtObject = ObjectHandle{0, 0};
};
template <>
@ -214,59 +212,6 @@ namespace dawn_wire {
void ForwardFenceCompletedValue(dawnFenceCompletionStatus status,
dawnCallbackUserdata userdata);
// A really really simple implementation of the DeserializeAllocator. It's main feature
// is that it has some inline storage so as to avoid allocations for the majority of
// commands.
class ServerAllocator : public DeserializeAllocator {
public:
ServerAllocator() {
Reset();
}
~ServerAllocator() {
Reset();
}
void* GetSpace(size_t size) override {
// Return space in the current buffer if possible first.
if (mRemainingSize >= size) {
char* buffer = mCurrentBuffer;
mCurrentBuffer += size;
mRemainingSize -= size;
return buffer;
}
// Otherwise allocate a new buffer and try again.
size_t allocationSize = std::max(size, size_t(2048));
char* allocation = static_cast<char*>(malloc(allocationSize));
if (allocation == nullptr) {
return nullptr;
}
mAllocations.push_back(allocation);
mCurrentBuffer = allocation;
mRemainingSize = allocationSize;
return GetSpace(size);
}
void Reset() {
for (auto allocation : mAllocations) {
free(allocation);
}
mAllocations.clear();
// The initial buffer is the inline buffer so that some allocations can be skipped
mCurrentBuffer = mStaticBuffer;
mRemainingSize = sizeof(mStaticBuffer);
}
private:
size_t mRemainingSize = 0;
char* mCurrentBuffer = nullptr;
char mStaticBuffer[2048];
std::vector<char*> mAllocations;
};
class Server : public CommandHandler, public ObjectIdResolver {
public:
Server(dawnDevice device, const dawnProcTable& procs, CommandSerializer* serializer)
@ -294,13 +239,11 @@ namespace dawn_wire {
void OnDeviceError(const char* message) {
ReturnDeviceErrorCallbackCmd cmd;
cmd.messageStrlen = std::strlen(message);
cmd.message = message;
auto allocCmd = static_cast<ReturnDeviceErrorCallbackCmd*>(GetCmdSpace(sizeof(cmd)));
*allocCmd = cmd;
char* messageAlloc = static_cast<char*>(GetCmdSpace(cmd.messageStrlen + 1));
strcpy(messageAlloc, message);
size_t requiredSize = cmd.GetRequiredSize();
char* allocatedBuffer = static_cast<char*>(GetCmdSpace(requiredSize));
cmd.Serialize(allocatedBuffer);
}
{% for type in by_category["object"] if type.is_builder%}
@ -319,18 +262,16 @@ namespace dawn_wire {
if (status != DAWN_BUILDER_ERROR_STATUS_UNKNOWN) {
//* Unknown is the only status that can be returned without a call to GetResult
//* so we are guaranteed to have created an object.
ASSERT(builder->builtObjectId != 0);
ASSERT(builder->builtObject.id != 0);
Return{{Type}}ErrorCallbackCmd cmd;
cmd.builtObjectId = builder->builtObjectId;
cmd.builtObjectSerial = builder->builtObjectSerial;
cmd.builtObject = builder->builtObject;
cmd.status = status;
cmd.messageStrlen = std::strlen(message);
cmd.message = message;
auto allocCmd = static_cast<Return{{Type}}ErrorCallbackCmd*>(GetCmdSpace(sizeof(cmd)));
*allocCmd = cmd;
char* messageAlloc = static_cast<char*>(GetCmdSpace(strlen(message) + 1));
strcpy(messageAlloc, message);
size_t requiredSize = cmd.GetRequiredSize();
char* allocatedBuffer = static_cast<char*>(GetCmdSpace(requiredSize));
cmd.Serialize(allocatedBuffer);
}
}
{% endfor %}
@ -339,46 +280,44 @@ namespace dawn_wire {
std::unique_ptr<MapUserdata> data(userdata);
// Skip sending the callback if the buffer has already been destroyed.
auto* bufferData = mKnownBuffer.Get(data->bufferId);
if (bufferData == nullptr || bufferData->serial != data->bufferSerial) {
auto* bufferData = mKnownBuffer.Get(data->buffer.id);
if (bufferData == nullptr || bufferData->serial != data->buffer.serial) {
return;
}
ReturnBufferMapReadAsyncCallbackCmd cmd;
cmd.bufferId = data->bufferId;
cmd.bufferSerial = data->bufferSerial;
cmd.buffer = data->buffer;
cmd.requestSerial = data->requestSerial;
cmd.status = status;
cmd.dataLength = 0;
auto allocCmd = static_cast<ReturnBufferMapReadAsyncCallbackCmd*>(GetCmdSpace(sizeof(cmd)));
*allocCmd = cmd;
cmd.data = reinterpret_cast<const uint8_t*>(ptr);
if (status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS) {
allocCmd->dataLength = data->size;
void* dataAlloc = GetCmdSpace(data->size);
memcpy(dataAlloc, ptr, data->size);
cmd.dataLength = data->size;
}
size_t requiredSize = cmd.GetRequiredSize();
char* allocatedBuffer = static_cast<char*>(GetCmdSpace(requiredSize));
cmd.Serialize(allocatedBuffer);
}
void OnMapWriteAsyncCallback(dawnBufferMapAsyncStatus status, void* ptr, MapUserdata* userdata) {
std::unique_ptr<MapUserdata> data(userdata);
// Skip sending the callback if the buffer has already been destroyed.
auto* bufferData = mKnownBuffer.Get(data->bufferId);
if (bufferData == nullptr || bufferData->serial != data->bufferSerial) {
auto* bufferData = mKnownBuffer.Get(data->buffer.id);
if (bufferData == nullptr || bufferData->serial != data->buffer.serial) {
return;
}
ReturnBufferMapWriteAsyncCallbackCmd cmd;
cmd.bufferId = data->bufferId;
cmd.bufferSerial = data->bufferSerial;
cmd.buffer = data->buffer;
cmd.requestSerial = data->requestSerial;
cmd.status = status;
auto allocCmd = static_cast<ReturnBufferMapWriteAsyncCallbackCmd*>(GetCmdSpace(sizeof(cmd)));
*allocCmd = cmd;
size_t requiredSize = cmd.GetRequiredSize();
char* allocatedBuffer = static_cast<char*>(GetCmdSpace(requiredSize));
cmd.Serialize(allocatedBuffer);
if (status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS) {
bufferData->mappedData = ptr;
@ -390,15 +329,14 @@ namespace dawn_wire {
std::unique_ptr<FenceCompletionUserdata> data(userdata);
ReturnFenceUpdateCompletedValueCmd cmd;
cmd.fenceId = data->fenceId;
cmd.fenceSerial = data->fenceSerial;
cmd.fence = data->fence;
cmd.value = data->value;
auto allocCmd = static_cast<ReturnFenceUpdateCompletedValueCmd*>(GetCmdSpace(sizeof(cmd)));
*allocCmd = cmd;
size_t requiredSize = cmd.GetRequiredSize();
char* allocatedBuffer = static_cast<char*>(GetCmdSpace(requiredSize));
cmd.Serialize(allocatedBuffer);
}
{% set client_side_commands = ["FenceGetCompletedValue"] %}
const char* HandleCommands(const char* commands, size_t size) override {
mProcs.deviceTick(mKnownDevice.Get(1)->handle);
@ -407,25 +345,11 @@ namespace dawn_wire {
bool success = false;
switch (cmdId) {
{% for type in by_category["object"] %}
{% for method in type.methods %}
{% set Suffix = as_MethodSuffix(type.name, method.name) %}
{% if Suffix not in client_side_commands %}
case WireCmd::{{Suffix}}:
success = Handle{{Suffix}}(&commands, &size);
break;
{% endif %}
{% endfor %}
{% for command in cmd_records["command"] %}
case WireCmd::{{command.name.CamelCase()}}:
success = Handle{{command.name.CamelCase()}}(&commands, &size);
break;
{% endfor %}
case WireCmd::BufferMapAsync:
success = HandleBufferMapAsync(&commands, &size);
break;
case WireCmd::BufferUpdateMappedDataCmd:
success = HandleBufferUpdateMappedData(&commands, &size);
break;
case WireCmd::DestroyObject:
success = HandleDestroyObject(&commands, &size);
break;
default:
success = false;
}
@ -447,7 +371,7 @@ namespace dawn_wire {
dawnProcTable mProcs;
CommandSerializer* mSerializer = nullptr;
ServerAllocator mAllocator;
WireDeserializeAllocator mAllocator;
void* GetCmdSpace(size_t size) {
return mSerializer->GetCmdSpace(size);
@ -484,36 +408,10 @@ namespace dawn_wire {
KnownObjects<{{as_cType(type.name)}}> mKnown{{type.name.CamelCase()}};
{% endfor %}
{% set reverse_lookup_object_types = ["Fence"] %}
{% for type in by_category["object"] if type.name.CamelCase() in reverse_lookup_object_types %}
{% for type in by_category["object"] if type.name.CamelCase() in server_reverse_lookup_objects %}
ObjectIdLookupTable<{{as_cType(type.name)}}> m{{type.name.CamelCase()}}IdTable;
{% endfor %}
//* Helper function for the getting of the command data in command handlers.
//* Checks there is enough data left, updates the buffer / size and returns
//* the command (or nullptr for an error).
template <typename T>
static const T* GetData(const char** buffer, size_t* size, size_t count) {
// TODO(cwallez@chromium.org): Check for overflow
size_t totalSize = count * sizeof(T);
if (*size < totalSize) {
return nullptr;
}
const T* data = reinterpret_cast<const T*>(*buffer);
*buffer += totalSize;
*size -= totalSize;
return data;
}
template <typename T>
static const T* GetCommand(const char** commands, size_t* size) {
return GetData<T>(commands, size, 1);
}
{% set custom_pre_handler_commands = ["BufferUnmap"] %}
bool PreHandleBufferUnmap(const BufferUnmapCmd& cmd) {
auto* selfData = mKnownBuffer.Get(cmd.selfId);
ASSERT(selfData != nullptr);
@ -523,8 +421,6 @@ namespace dawn_wire {
return true;
}
{% set custom_post_handler_commands = ["QueueSignal"] %}
bool PostHandleQueueSignal(const QueueSignalCmd& cmd) {
if (cmd.fence == nullptr) {
return false;
@ -536,8 +432,7 @@ namespace dawn_wire {
auto* data = new FenceCompletionUserdata;
data->server = this;
data->fenceId = fenceId;
data->fenceSerial = fence->serial;
data->fence = ObjectHandle{fenceId, fence->serial};
data->value = cmd.signalValue;
auto userdata = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(data));
@ -560,7 +455,7 @@ namespace dawn_wire {
return false;
}
{% if Suffix in custom_pre_handler_commands %}
{% if Suffix in server_custom_pre_handler_commands %}
if (!PreHandle{{Suffix}}(cmd)) {
return false;
}
@ -575,15 +470,14 @@ namespace dawn_wire {
{% set returns = return_type.name.canonical_case() != "void" %}
{% if returns %}
{% set Type = method.return_type.name.CamelCase() %}
auto* resultData = mKnown{{Type}}.Allocate(cmd.resultId);
auto* resultData = mKnown{{Type}}.Allocate(cmd.result.id);
if (resultData == nullptr) {
return false;
}
resultData->serial = cmd.resultSerial;
resultData->serial = cmd.result.serial;
{% if type.is_builder %}
selfData->builtObjectId = cmd.resultId;
selfData->builtObjectSerial = cmd.resultSerial;
selfData->builtObject = cmd.result;
{% endif %}
{% endif %}
@ -608,7 +502,7 @@ namespace dawn_wire {
{%- endfor -%}
);
{% if Suffix in custom_post_handler_commands %}
{% if Suffix in server_custom_post_handler_commands %}
if (!PostHandle{{Suffix}}(cmd)) {
return false;
}
@ -618,10 +512,10 @@ namespace dawn_wire {
resultData->handle = result;
resultData->valid = result != nullptr;
{% if return_type.name.CamelCase() in reverse_lookup_object_types %}
{% if return_type.name.CamelCase() in server_reverse_lookup_objects %}
//* For created objects, store a mapping from them back to their client IDs
if (result) {
m{{return_type.name.CamelCase()}}IdTable.Store(result, cmd.resultId);
m{{return_type.name.CamelCase()}}IdTable.Store(result, cmd.result.id);
}
{% endif %}
@ -630,7 +524,7 @@ namespace dawn_wire {
{% if return_type.is_builder %}
if (result != nullptr) {
uint64_t userdata1 = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this));
uint64_t userdata2 = (uint64_t(resultData->serial) << uint64_t(32)) + cmd.resultId;
uint64_t userdata2 = (uint64_t(resultData->serial) << uint64_t(32)) + cmd.result.id;
mProcs.{{as_varName(return_type.name, Name("set error callback"))}}(result, Forward{{return_type.name.CamelCase()}}ToClient, userdata1, userdata2);
}
{% endif %}
@ -645,16 +539,18 @@ namespace dawn_wire {
bool HandleBufferMapAsync(const char** commands, size_t* size) {
//* These requests are just forwarded to the buffer, with userdata containing what the client
//* will require in the return command.
const auto* cmd = GetCommand<BufferMapAsyncCmd>(commands, size);
if (cmd == nullptr) {
BufferMapAsyncCmd cmd;
DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator);
if (deserializeResult == DeserializeResult::FatalError) {
return false;
}
ObjectId bufferId = cmd->bufferId;
uint32_t requestSerial = cmd->requestSerial;
uint32_t requestSize = cmd->size;
uint32_t requestStart = cmd->start;
bool isWrite = cmd->isWrite;
ObjectId bufferId = cmd.bufferId;
uint32_t requestSerial = cmd.requestSerial;
uint32_t requestSize = cmd.size;
uint32_t requestStart = cmd.start;
bool isWrite = cmd.isWrite;
//* The null object isn't valid as `self`
if (bufferId == 0) {
@ -668,8 +564,7 @@ namespace dawn_wire {
auto* data = new MapUserdata;
data->server = this;
data->bufferId = bufferId;
data->bufferSerial = buffer->serial;
data->buffer = ObjectHandle{bufferId, buffer->serial};
data->requestSerial = requestSerial;
data->size = requestSize;
data->isWrite = isWrite;
@ -696,13 +591,15 @@ namespace dawn_wire {
}
bool HandleBufferUpdateMappedData(const char** commands, size_t* size) {
const auto* cmd = GetCommand<BufferUpdateMappedDataCmd>(commands, size);
if (cmd == nullptr) {
BufferUpdateMappedDataCmd cmd;
DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator);
if (deserializeResult == DeserializeResult::FatalError) {
return false;
}
ObjectId bufferId = cmd->bufferId;
size_t dataLength = cmd->dataLength;
ObjectId bufferId = cmd.bufferId;
size_t dataLength = cmd.dataLength;
//* The null object isn't valid as `self`
if (bufferId == 0) {
@ -715,29 +612,28 @@ namespace dawn_wire {
return false;
}
const char* data = GetData<char>(commands, size, dataLength);
if (data == nullptr) {
return false;
}
DAWN_ASSERT(cmd.data != nullptr);
memcpy(buffer->mappedData, data, dataLength);
memcpy(buffer->mappedData, cmd.data, dataLength);
return true;
}
bool HandleDestroyObject(const char** commands, size_t* size) {
const auto* cmd = GetCommand<DestroyObjectCmd>(commands, size);
if (cmd == nullptr) {
DestroyObjectCmd cmd;
DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator);
if (deserializeResult == DeserializeResult::FatalError) {
return false;
}
ObjectId objectId = cmd->objectId;
ObjectId objectId = cmd.objectId;
//* ID 0 are reserved for nullptr and cannot be destroyed.
if (objectId == 0) {
return false;
}
switch (cmd->objectType) {
switch (cmd.objectType) {
{% for type in by_category["object"] %}
{% set ObjectType = type.name.CamelCase() %}
case ObjectType::{{ObjectType}}: {
@ -749,7 +645,7 @@ namespace dawn_wire {
if (data == nullptr) {
return false;
}
{% if type.name.CamelCase() in reverse_lookup_object_types %}
{% if type.name.CamelCase() in server_reverse_lookup_objects %}
m{{type.name.CamelCase()}}IdTable.Remove(data->handle);
{% endif %}

93
generator/wire_cmd.py Normal file
View File

@ -0,0 +1,93 @@
# 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.
from collections import namedtuple
from common import Name
import common
def concat_names(*names):
return ' '.join([name.canonical_case() for name in names])
# Create wire commands from api methods
def compute_wire_params(api_params, wire_json):
wire_params = api_params.copy()
types = wire_params['types']
commands = []
return_commands = []
object_result_member = common.RecordMember(Name('result'), types['ObjectHandle'], 'value', False, True)
string_message_member = common.RecordMember(Name('message'), types['char'], 'const*', False, False)
string_message_member.length = 'strlen'
built_object_member = common.RecordMember(Name('built object'), types['ObjectHandle'], 'value', False, False)
callback_status_member = common.RecordMember(Name('status'), types['uint32_t'], 'value', False, False)
# Generate commands from object methods
for api_object in wire_params['by_category']['object']:
for method in api_object.methods:
if method.return_type.category != 'object' and method.return_type.name.canonical_case() != 'void':
# No other return types supported
continue
# Create object method commands by prepending "self"
members = [common.RecordMember(Name('self'), types[api_object.dict_name], 'value', False, False)]
members += method.arguments
# Client->Server commands that return an object return the result object handle
if method.return_type.category == 'object':
members.append(object_result_member)
command_name = concat_names(api_object.name, method.name)
command = common.Command(command_name, members)
command.derived_object = api_object
command.derived_method = method
commands.append(command)
# Create builder return ErrorCallback commands
# This can be removed when WebGPU error handling is implemented
if api_object.is_builder:
command_name = concat_names(api_object.name, Name('error callback'))
command = common.Command(command_name, [
built_object_member,
callback_status_member,
string_message_member,
])
command.derived_object = api_object
return_commands.append(command)
for (name, json_data) in wire_json['commands'].items():
commands.append(common.Command(name, common.linked_record_members(json_data, types)))
for (name, json_data) in wire_json['return commands'].items():
return_commands.append(common.Command(name, common.linked_record_members(json_data, types)))
wire_params['cmd_records'] = {
'command': commands,
'return command': return_commands
}
for commands in wire_params['cmd_records'].values():
for command in commands:
command.update_metadata()
commands.sort(key=lambda c: c.name.canonical_case())
wire_params.update(wire_json.get('special items', {}))
return wire_params

View File

@ -1,83 +0,0 @@
// 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.
#ifndef DAWNWIRE_WIRECMD_H_
#define DAWNWIRE_WIRECMD_H_
#include <dawn/dawn.h>
#include "dawn_wire/WireCmd_autogen.h"
namespace dawn_wire {
struct ReturnDeviceErrorCallbackCmd {
ReturnWireCmd commandId = ReturnWireCmd::DeviceErrorCallback;
size_t messageStrlen;
};
struct BufferMapAsyncCmd {
WireCmd commandId = WireCmd::BufferMapAsync;
ObjectId bufferId;
ObjectSerial requestSerial;
uint32_t start;
uint32_t size;
bool isWrite;
};
struct ReturnBufferMapReadAsyncCallbackCmd {
ReturnWireCmd commandId = ReturnWireCmd::BufferMapReadAsyncCallback;
ObjectId bufferId;
ObjectSerial bufferSerial;
uint32_t requestSerial;
uint32_t status;
uint32_t dataLength;
};
struct ReturnBufferMapWriteAsyncCallbackCmd {
ReturnWireCmd commandId = ReturnWireCmd::BufferMapWriteAsyncCallback;
ObjectId bufferId;
ObjectSerial bufferSerial;
uint32_t requestSerial;
uint32_t status;
};
struct ReturnFenceUpdateCompletedValueCmd {
ReturnWireCmd commandId = ReturnWireCmd::FenceUpdateCompletedValue;
ObjectId fenceId;
ObjectSerial fenceSerial;
uint64_t value;
};
struct BufferUpdateMappedDataCmd {
WireCmd commandId = WireCmd::BufferUpdateMappedDataCmd;
ObjectId bufferId;
uint32_t dataLength;
};
struct DestroyObjectCmd {
WireCmd commandId = WireCmd::DestroyObject;
ObjectType objectType;
ObjectId objectId;
};
} // namespace dawn_wire
#endif // DAWNWIRE_WIRECMD_H_

View File

@ -0,0 +1,60 @@
// Copyright 2019 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dawn_wire/WireDeserializeAllocator.h"
#include <algorithm>
namespace dawn_wire {
WireDeserializeAllocator::WireDeserializeAllocator() {
Reset();
}
WireDeserializeAllocator::~WireDeserializeAllocator() {
Reset();
}
void* WireDeserializeAllocator::GetSpace(size_t size) {
// Return space in the current buffer if possible first.
if (mRemainingSize >= size) {
char* buffer = mCurrentBuffer;
mCurrentBuffer += size;
mRemainingSize -= size;
return buffer;
}
// Otherwise allocate a new buffer and try again.
size_t allocationSize = std::max(size, size_t(2048));
char* allocation = static_cast<char*>(malloc(allocationSize));
if (allocation == nullptr) {
return nullptr;
}
mAllocations.push_back(allocation);
mCurrentBuffer = allocation;
mRemainingSize = allocationSize;
return GetSpace(size);
}
void WireDeserializeAllocator::Reset() {
for (auto allocation : mAllocations) {
free(allocation);
}
mAllocations.clear();
// The initial buffer is the inline buffer so that some allocations can be skipped
mCurrentBuffer = mStaticBuffer;
mRemainingSize = sizeof(mStaticBuffer);
}
} // namespace dawn_wire

View File

@ -0,0 +1,43 @@
// 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.
#ifndef DAWNWIRE_WIREDESERIALIZEALLOCATOR_H_
#define DAWNWIRE_WIREDESERIALIZEALLOCATOR_H_
#include "dawn_wire/WireCmd_autogen.h"
#include <vector>
namespace dawn_wire {
// A really really simple implementation of the DeserializeAllocator. It's main feature
// is that it has some inline storage so as to avoid allocations for the majority of
// commands.
class WireDeserializeAllocator : public DeserializeAllocator {
public:
WireDeserializeAllocator();
~WireDeserializeAllocator();
void* GetSpace(size_t size) override;
void Reset();
private:
size_t mRemainingSize = 0;
char* mCurrentBuffer = nullptr;
char mStaticBuffer[2048];
std::vector<char*> mAllocations;
};
} // namespace dawn_wire
#endif // DAWNWIRE_WIREDESERIALIZEALLOCATOR_H_