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:
parent
a483fac90d
commit
c7f416c0c9
|
@ -1,3 +1,5 @@
|
|||
*.pyc
|
||||
|
||||
# Directories added by gclient sync and the GN build
|
||||
.gclient
|
||||
.gclient_entries
|
||||
|
|
11
BUILD.gn
11
BUILD.gn
|
@ -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",
|
||||
|
|
12
dawn.json
12
dawn.json
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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__':
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
Loading…
Reference in New Issue