2017-09-04 15:08:36 +10:00
|
|
|
#!/usr/bin/env python3
|
2019-03-13 14:49:21 +11:00
|
|
|
import argparse
|
2018-06-11 14:55:49 +10:00
|
|
|
import sys
|
2017-09-04 15:08:36 +10:00
|
|
|
import logging
|
2019-01-05 15:29:13 +11:00
|
|
|
from typing import List, Dict, Set, TextIO
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-01-05 15:13:13 +11:00
|
|
|
import xml.etree.ElementTree
|
2017-09-06 12:11:20 +10:00
|
|
|
|
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
###############################################################################
|
2019-01-05 12:04:31 +11:00
|
|
|
def rename(name: str):
|
2017-09-06 12:11:20 +10:00
|
|
|
return name
|
|
|
|
|
|
|
|
|
|
|
|
###############################################################################
|
2019-01-05 12:14:15 +11:00
|
|
|
class Registry:
|
2019-03-13 14:49:21 +11:00
|
|
|
types: Dict[str, 'Type'] = {}
|
2019-01-05 15:22:09 +11:00
|
|
|
extensions: Dict = {}
|
|
|
|
features: Dict = {}
|
|
|
|
applied: Set = set()
|
2017-09-06 12:11:20 +10:00
|
|
|
|
2019-01-05 15:22:09 +11:00
|
|
|
def __init__(self):
|
2019-01-05 12:14:15 +11:00
|
|
|
self.types['API Constants'] = Unscoped('API Constants')
|
2017-09-06 12:11:20 +10:00
|
|
|
|
2019-01-05 12:04:31 +11:00
|
|
|
def _serialise(self, name: str, queued: Set[str]):
|
2018-06-11 14:55:49 +10:00
|
|
|
if name in queued:
|
|
|
|
return []
|
|
|
|
|
|
|
|
result = []
|
|
|
|
obj = self.types[name]
|
|
|
|
|
|
|
|
for d in obj.depends:
|
|
|
|
if d == name:
|
|
|
|
continue
|
|
|
|
result += self._serialise(d, queued)
|
|
|
|
|
|
|
|
assert name not in queued
|
|
|
|
queued.add(name)
|
|
|
|
result += [obj]
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
2019-01-05 12:04:31 +11:00
|
|
|
def serialise(self, platform: Set[str]):
|
2018-06-11 14:55:49 +10:00
|
|
|
required = []
|
|
|
|
|
2019-01-05 12:16:52 +11:00
|
|
|
for (_, f) in self.features.items():
|
|
|
|
required += f.apply(self)
|
2018-06-11 14:55:49 +10:00
|
|
|
required.append(f.name)
|
|
|
|
|
|
|
|
for e in self.extensions:
|
|
|
|
required += self.extensions[e].apply(self, platform)
|
|
|
|
|
|
|
|
queued = set()
|
|
|
|
result = []
|
|
|
|
|
|
|
|
for r in required:
|
|
|
|
result += self._serialise(r, queued)
|
|
|
|
|
|
|
|
return result
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
|
|
|
|
###############################################################################
|
2019-01-05 12:14:15 +11:00
|
|
|
class Type(object):
|
2018-06-11 14:55:49 +10:00
|
|
|
"""
|
|
|
|
The base class for all object defined in the Vulkan API.
|
|
|
|
|
|
|
|
This includes (but is not limited to) types, like structures; and values,
|
|
|
|
like constants.
|
|
|
|
"""
|
2019-01-05 12:04:31 +11:00
|
|
|
def __init__(self, name: str, depends: List[str] = None):
|
2018-09-08 12:32:20 +10:00
|
|
|
assert name
|
|
|
|
|
2017-09-04 15:08:36 +10:00
|
|
|
self.name = name
|
2018-09-08 12:32:20 +10:00
|
|
|
self.depends = depends or []
|
|
|
|
|
|
|
|
assert isinstance(self.depends, list)
|
|
|
|
for i in self.depends:
|
|
|
|
assert isinstance(i, str)
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
def depends(self):
|
2018-06-11 14:55:49 +10:00
|
|
|
return self.depends
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
def declare(self):
|
|
|
|
return ""
|
|
|
|
|
2019-01-05 12:16:52 +11:00
|
|
|
def define(self, reg: Registry):
|
2017-09-04 15:08:36 +10:00
|
|
|
return ""
|
|
|
|
|
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
###############################################################################
|
2019-01-05 12:14:15 +11:00
|
|
|
class AliasType(Type):
|
2018-09-08 12:32:20 +10:00
|
|
|
"""
|
|
|
|
A type that is an alias for another type.
|
|
|
|
|
|
|
|
May be serialised using an appropriate host language facility
|
|
|
|
(eg, a typedef)
|
|
|
|
"""
|
2019-01-05 15:04:36 +11:00
|
|
|
def __init__(self, name: str, target: str, depends: List[str] = None):
|
2018-09-08 12:32:20 +10:00
|
|
|
depends = depends or []
|
|
|
|
super().__init__(name, depends=depends+[target])
|
2018-06-11 14:55:49 +10:00
|
|
|
self.target = target
|
|
|
|
|
|
|
|
def declare(self):
|
2018-09-08 12:32:20 +10:00
|
|
|
return f"using {rename(self.name)} = {rename(self.target)};"
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
|
2019-01-05 12:19:03 +11:00
|
|
|
# -----------------------------------------------------------------------------
|
2019-01-05 12:14:15 +11:00
|
|
|
class AliasValue(Type):
|
2018-09-08 12:32:20 +10:00
|
|
|
"""
|
|
|
|
A value that is an alias for another value.
|
|
|
|
|
|
|
|
May be serialised using an appropriate host language facility.
|
|
|
|
"""
|
2019-01-05 12:04:31 +11:00
|
|
|
def __init__(self, name: str, target: str):
|
2018-06-11 14:55:49 +10:00
|
|
|
super().__init__(name, depends=[target])
|
|
|
|
self.target = target
|
|
|
|
self.value = target
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
def declare(self):
|
|
|
|
return "constexpr auto %(name)s = %(target)s;" % {
|
2018-09-08 12:32:20 +10:00
|
|
|
"name": rename(self.name),
|
|
|
|
"target": rename(self.target),
|
2017-09-06 12:11:20 +10:00
|
|
|
}
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
|
2019-01-05 12:19:03 +11:00
|
|
|
# -----------------------------------------------------------------------------
|
2019-01-05 12:14:15 +11:00
|
|
|
class Placeholder(Type):
|
2019-01-05 12:04:31 +11:00
|
|
|
def __init__(self, name: str):
|
2018-06-11 14:55:49 +10:00
|
|
|
super().__init__(name)
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
|
2019-01-05 12:19:03 +11:00
|
|
|
# -----------------------------------------------------------------------------
|
2019-01-05 12:14:15 +11:00
|
|
|
class Unscoped(Type):
|
2019-01-05 12:04:31 +11:00
|
|
|
def __init__(self, name: str):
|
2018-06-11 14:55:49 +10:00
|
|
|
super().__init__(name)
|
|
|
|
self.values = []
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
def declare(self):
|
2018-06-11 14:55:49 +10:00
|
|
|
return "\n".join(t.declare() for t in self.values)
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-01-05 15:04:36 +11:00
|
|
|
def define(self, reg):
|
2018-06-11 14:55:49 +10:00
|
|
|
return "\n".join(t.define(reg.types) for t in self.values)
|
|
|
|
|
|
|
|
|
|
|
|
###############################################################################
|
2019-01-05 12:14:15 +11:00
|
|
|
class Include(Type):
|
2018-06-11 14:55:49 +10:00
|
|
|
def __init__(self, node):
|
|
|
|
assert node.tag == 'type'
|
|
|
|
assert node.attrib['category'] == 'include'
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
super().__init__(node.attrib['name'])
|
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
self.directive = node.text
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
def declare(self):
|
|
|
|
return self.directive or "#include <%s>" % self.name
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
|
2019-01-05 12:31:33 +11:00
|
|
|
# -----------------------------------------------------------------------------
|
2019-01-05 12:14:15 +11:00
|
|
|
class Define(Type):
|
2017-09-04 15:08:36 +10:00
|
|
|
def __init__(self, node):
|
2018-06-11 14:55:49 +10:00
|
|
|
assert node.tag == 'type'
|
|
|
|
assert node.attrib['category'] == 'define'
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
name = node.attrib.get('name') or node.find('name').text
|
|
|
|
super().__init__(name)
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
self.directive = "".join(node.itertext())
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2017-09-06 12:11:20 +10:00
|
|
|
def declare(self):
|
2018-06-11 14:55:49 +10:00
|
|
|
return self.directive
|
2017-09-06 12:11:20 +10:00
|
|
|
|
|
|
|
|
2019-01-05 12:31:33 +11:00
|
|
|
###############################################################################
|
2019-01-05 12:14:15 +11:00
|
|
|
class Bitmask(Type):
|
2019-01-05 12:04:31 +11:00
|
|
|
def __init__(self, node):
|
2018-06-11 14:55:49 +10:00
|
|
|
assert node.tag == 'type'
|
|
|
|
assert node.attrib['category'] == 'bitmask'
|
2017-09-06 12:11:20 +10:00
|
|
|
|
2019-01-05 15:12:56 +11:00
|
|
|
n = node.find('name').text
|
|
|
|
t = node.find('type').text
|
2017-09-06 12:11:20 +10:00
|
|
|
|
2019-01-05 15:12:56 +11:00
|
|
|
super().__init__(n, depends=[t])
|
2018-06-11 14:55:49 +10:00
|
|
|
|
2019-01-05 15:12:56 +11:00
|
|
|
self.type = t
|
2018-06-11 14:55:49 +10:00
|
|
|
self.requires = node.attrib.get('requires')
|
|
|
|
if self.requires:
|
|
|
|
self.depends.append(self.requires)
|
|
|
|
|
|
|
|
def declare(self):
|
|
|
|
return "using %(name)s = %(type)s;" % {
|
|
|
|
"name": self.name,
|
|
|
|
"type": self.type
|
2017-09-06 12:11:20 +10:00
|
|
|
}
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-01-05 12:14:15 +11:00
|
|
|
def define(self, reg: Registry):
|
2019-01-05 12:04:31 +11:00
|
|
|
return self.declare()
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2017-09-05 17:19:51 +10:00
|
|
|
|
2019-01-05 12:31:33 +11:00
|
|
|
###############################################################################
|
2019-01-05 12:14:15 +11:00
|
|
|
class Handle(Type):
|
2018-09-12 14:10:39 +10:00
|
|
|
parents: List[str]
|
|
|
|
type: str
|
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
def __init__(self, node):
|
|
|
|
assert node.tag == 'type'
|
|
|
|
assert node.attrib['category'] == 'handle'
|
2017-09-06 12:11:20 +10:00
|
|
|
|
2019-01-05 15:12:56 +11:00
|
|
|
n = node.find('name').text
|
|
|
|
t = node.find('type').text
|
|
|
|
assert t
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-01-05 15:12:56 +11:00
|
|
|
super().__init__(n, depends=[t])
|
|
|
|
self.type = t
|
2018-09-12 14:10:39 +10:00
|
|
|
|
|
|
|
parents = node.attrib.get('parent', None)
|
|
|
|
self.parents = parents.split(',') if parents else []
|
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
def declare(self):
|
|
|
|
return "struct %(name)s_t; using %(name)s = %(name)s_t*;" % {
|
|
|
|
"name": self.name,
|
|
|
|
"type": self.type
|
2017-09-06 12:11:20 +10:00
|
|
|
}
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-01-05 12:14:15 +11:00
|
|
|
def has_parent(self, name: str, reg: Registry) -> bool:
|
2018-09-10 13:21:02 +10:00
|
|
|
"""
|
|
|
|
Recursively check if this type is derived from a given parent type.
|
|
|
|
"""
|
|
|
|
assert name
|
|
|
|
assert reg
|
|
|
|
|
2018-09-12 14:10:39 +10:00
|
|
|
if self.name == name:
|
|
|
|
return True
|
|
|
|
|
|
|
|
if not self.parents:
|
2018-09-10 13:21:02 +10:00
|
|
|
return False
|
2018-09-12 14:10:39 +10:00
|
|
|
if name in self.parents:
|
2018-09-10 13:21:02 +10:00
|
|
|
return True
|
|
|
|
|
2018-09-12 14:10:39 +10:00
|
|
|
for p in self.parents:
|
|
|
|
if reg.types[p].has_parent(name, reg):
|
|
|
|
return True
|
|
|
|
return False
|
2018-09-10 13:21:02 +10:00
|
|
|
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-01-05 12:31:33 +11:00
|
|
|
###############################################################################
|
2019-01-05 12:14:15 +11:00
|
|
|
class Enum(Type):
|
2019-01-05 12:04:31 +11:00
|
|
|
def __init__(self, node):
|
2018-06-11 14:55:49 +10:00
|
|
|
assert node.tag == 'type'
|
|
|
|
assert node.attrib['category'] == 'enum'
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
name = node.attrib['name']
|
2019-01-05 12:04:31 +11:00
|
|
|
super().__init__(name, depends=["VkEnum"])
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
self.values = {}
|
2017-09-05 17:19:51 +10:00
|
|
|
|
2019-01-05 12:04:31 +11:00
|
|
|
def __setitem__(self, key: str, value):
|
2019-01-05 12:14:15 +11:00
|
|
|
assert isinstance(value, Constant) or isinstance(value, AliasValue)
|
2018-06-11 14:55:49 +10:00
|
|
|
self.values[key] = value
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
def declare(self):
|
|
|
|
return ""
|
|
|
|
|
2019-01-05 12:14:15 +11:00
|
|
|
def define(self, reg: Registry):
|
2019-01-05 15:04:36 +11:00
|
|
|
values = ("%(name)s = %(value)s" % {"name": k, "value": v.value} for (k, v) in self.values.items())
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
return "enum %(name)s : int32_t { %(values)s };" % {
|
|
|
|
"name": self.name,
|
|
|
|
"values": ", ".join(values)
|
|
|
|
}
|
2017-09-06 12:11:20 +10:00
|
|
|
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-01-05 12:31:33 +11:00
|
|
|
###############################################################################
|
2019-01-05 12:14:15 +11:00
|
|
|
class BaseType(AliasType):
|
2018-06-11 14:55:49 +10:00
|
|
|
"""
|
|
|
|
Represents fundamental types that aliases of system provided types and used
|
|
|
|
extensively by the base API. eg, VkBool32
|
|
|
|
"""
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
def __init__(self, node):
|
2018-06-11 14:55:49 +10:00
|
|
|
assert node.tag == 'type'
|
|
|
|
assert node.attrib['category'] == 'basetype'
|
|
|
|
|
|
|
|
super().__init__(
|
|
|
|
node.find('name').text,
|
|
|
|
node.find('type').text
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2019-01-05 12:31:33 +11:00
|
|
|
###############################################################################
|
2019-01-05 12:14:15 +11:00
|
|
|
class FuncPointer(Type):
|
2019-01-05 12:04:31 +11:00
|
|
|
def __init__(self, node):
|
2018-06-11 14:55:49 +10:00
|
|
|
assert node.tag == 'type'
|
|
|
|
assert node.attrib['category'] == 'funcpointer'
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
name = node.find('name').text
|
2018-06-11 14:55:49 +10:00
|
|
|
|
2017-09-04 15:08:36 +10:00
|
|
|
self.params = list(map(lambda x: x.text, node.findall('./type')))
|
|
|
|
self.text = "".join(node.itertext())
|
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
super().__init__(name, depends=['VkBool32']+self.params)
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
def declare(self):
|
|
|
|
return self.text
|
|
|
|
|
|
|
|
|
2019-01-05 12:31:33 +11:00
|
|
|
###############################################################################
|
2019-01-05 12:14:15 +11:00
|
|
|
class POD(Type):
|
2019-01-05 12:04:31 +11:00
|
|
|
def __init__(self, node):
|
2018-06-11 14:55:49 +10:00
|
|
|
assert node.tag == 'type'
|
|
|
|
assert node.attrib['category'] in ['struct', 'union']
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
super().__init__(node.attrib['name'])
|
2017-09-06 12:11:20 +10:00
|
|
|
self._node = node
|
2017-09-04 15:08:36 +10:00
|
|
|
self._category = node.attrib['category']
|
|
|
|
|
|
|
|
# sometimes there are enums hiding in the member fields being used as array sizes
|
2018-06-11 14:55:49 +10:00
|
|
|
self.depends += list(e.text for e in node.findall('.//enum'))
|
|
|
|
self.depends += list(t.text for t in node.findall('.//type'))
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2017-09-07 15:46:07 +10:00
|
|
|
self._members = []
|
|
|
|
|
|
|
|
for member in node.findall('./member'):
|
2019-01-05 15:12:56 +11:00
|
|
|
t = member.find('type').text
|
|
|
|
n = member.find('name').text
|
2017-09-07 15:46:07 +10:00
|
|
|
|
|
|
|
comment = member.find('comment')
|
2019-01-05 15:14:17 +11:00
|
|
|
if comment is not None:
|
2017-09-07 15:46:07 +10:00
|
|
|
member.remove(comment)
|
|
|
|
code = " ".join(member.itertext())
|
|
|
|
|
2019-01-05 15:12:56 +11:00
|
|
|
self._members.append({'code': code, 'type': t, 'name': n})
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
def declare(self):
|
2017-09-06 12:11:20 +10:00
|
|
|
return "%(category)s %(name)s;" % {
|
|
|
|
'category': self._category,
|
|
|
|
'name': rename(self.name)
|
|
|
|
}
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-01-05 12:14:15 +11:00
|
|
|
def define(self, reg: Registry):
|
2017-09-06 00:14:18 +10:00
|
|
|
return "%(category)s %(name)s {\n%(members)s\n};" % {
|
2017-09-04 15:08:36 +10:00
|
|
|
'category': self._category,
|
2017-09-06 12:11:20 +10:00
|
|
|
'name': rename(self.name),
|
2017-09-04 15:08:36 +10:00
|
|
|
'members': "\n".join(m['code'] + ';' for m in self._members)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-01-05 12:31:33 +11:00
|
|
|
# -----------------------------------------------------------------------------
|
2019-01-05 12:14:15 +11:00
|
|
|
class Struct(POD):
|
2019-01-05 12:04:31 +11:00
|
|
|
def __init__(self, node):
|
2017-09-04 15:08:36 +10:00
|
|
|
super().__init__(node)
|
|
|
|
|
|
|
|
|
2019-01-05 12:31:33 +11:00
|
|
|
# -----------------------------------------------------------------------------
|
2019-01-05 12:14:15 +11:00
|
|
|
class Union(POD):
|
2019-01-05 12:04:31 +11:00
|
|
|
def __init__(self, node):
|
2017-09-04 15:08:36 +10:00
|
|
|
super().__init__(node)
|
|
|
|
|
|
|
|
|
2019-01-05 12:31:33 +11:00
|
|
|
###############################################################################
|
2019-01-05 12:14:15 +11:00
|
|
|
class Constant(Type):
|
2019-01-05 12:04:31 +11:00
|
|
|
def __init__(self, node, **kwargs):
|
2018-06-11 14:55:49 +10:00
|
|
|
assert node.tag == 'enum'
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
name = node.attrib['name']
|
|
|
|
super().__init__(name)
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
if 'offset' in node.attrib:
|
|
|
|
assert 'extends' in node.attrib
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
number = int(kwargs['extnumber'])
|
|
|
|
offset = int(node.attrib['offset'])
|
|
|
|
self.value = 1000000000 + 1000 * number + offset
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
if 'dir' in node.attrib:
|
|
|
|
self.value *= -1
|
|
|
|
elif 'value' in node.attrib:
|
|
|
|
self.value = node.attrib['value']
|
|
|
|
elif 'bitpos' in node.attrib:
|
2019-01-05 15:04:36 +11:00
|
|
|
self.value = "1 << %s" % node.attrib['bitpos']
|
2017-09-04 15:08:36 +10:00
|
|
|
else:
|
2019-01-05 12:29:35 +11:00
|
|
|
raise RuntimeError("Unknown constant value type")
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
def declare(self):
|
|
|
|
return "constexpr auto %(name)s = %(value)s;" % {
|
|
|
|
"name": self.name,
|
|
|
|
"value": self.value
|
|
|
|
}
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
|
2019-01-05 12:31:33 +11:00
|
|
|
###############################################################################
|
2019-01-05 12:14:15 +11:00
|
|
|
class Command(Type):
|
|
|
|
class Param(Type):
|
2019-01-07 11:09:06 +11:00
|
|
|
name: str
|
2019-03-02 22:47:03 +11:00
|
|
|
"""An string that can be used to refer to the parameter variable"""
|
2019-01-07 11:09:06 +11:00
|
|
|
type: str
|
|
|
|
"""The name of this parameter's dependant type"""
|
|
|
|
param: str
|
|
|
|
"""
|
|
|
|
The components of this type for a C definition (ie, includes
|
2019-03-02 22:47:03 +11:00
|
|
|
pointer, const, other decorations, _and_ the variable name (which must
|
|
|
|
be the same as self.name for a useful system).
|
2019-01-07 11:09:06 +11:00
|
|
|
"""
|
|
|
|
|
2019-03-13 14:49:21 +11:00
|
|
|
def __init__(self, name: str, klass: str, param: str, depends: List[str]):
|
2018-09-08 12:32:20 +10:00
|
|
|
super().__init__(
|
2019-03-02 22:47:03 +11:00
|
|
|
name=name,
|
|
|
|
depends=depends,
|
2018-09-08 12:32:20 +10:00
|
|
|
)
|
|
|
|
|
2019-03-13 14:49:21 +11:00
|
|
|
self.type = klass
|
2019-03-02 22:47:03 +11:00
|
|
|
self.param = param
|
2018-09-10 13:21:02 +10:00
|
|
|
|
2019-03-02 22:47:03 +11:00
|
|
|
def __repr__(self) -> str:
|
|
|
|
return f"{{ name: '{self.name}', type: '{self.type}', param: '{self.param}' }}"
|
2018-09-08 12:32:20 +10:00
|
|
|
|
2019-01-07 11:09:06 +11:00
|
|
|
def is_pointer(self):
|
|
|
|
return '*' in self.param
|
|
|
|
|
2019-03-02 13:54:48 +11:00
|
|
|
def __init__(self, name: str, result: str, params: List[Param], depends: List[str] = None):
|
2018-06-11 14:55:49 +10:00
|
|
|
super().__init__(name)
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-03-02 13:54:48 +11:00
|
|
|
self.result = result
|
|
|
|
self.params = params
|
|
|
|
self.depends += depends or []
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-03-02 22:47:03 +11:00
|
|
|
def __repr__(self) -> str:
|
|
|
|
return f"{{ name: '{self.name}', result: '{self.result}', param: {self.params} }}"
|
|
|
|
|
2017-09-04 15:08:36 +10:00
|
|
|
def declare(self):
|
2019-03-03 12:18:58 +11:00
|
|
|
return 'extern "C" %(result)s %(name)s [[gnu::visibility("default")]] (%(params)s) noexcept;' % {
|
2017-09-06 12:11:20 +10:00
|
|
|
'name': rename(self.name),
|
2018-06-11 14:55:49 +10:00
|
|
|
'result': self.result,
|
2018-09-10 13:21:02 +10:00
|
|
|
'params': ", ".join(p.param for p in self.params)
|
2017-09-04 15:08:36 +10:00
|
|
|
}
|
|
|
|
|
2019-01-05 12:14:15 +11:00
|
|
|
def is_instance(self, reg: Registry):
|
2018-09-10 13:21:02 +10:00
|
|
|
assert reg
|
|
|
|
|
|
|
|
if not self.params:
|
2019-01-05 23:30:52 +11:00
|
|
|
return False
|
2018-09-10 13:21:02 +10:00
|
|
|
|
2019-01-05 23:30:52 +11:00
|
|
|
first_name = self.params[0].type
|
|
|
|
first_obj = reg.types[first_name]
|
2018-09-10 13:21:02 +10:00
|
|
|
|
2019-01-05 12:14:15 +11:00
|
|
|
if not isinstance(first_obj, Handle):
|
2019-03-02 22:47:03 +11:00
|
|
|
return True
|
2018-09-10 13:21:02 +10:00
|
|
|
|
2019-03-02 22:47:03 +11:00
|
|
|
instance = first_obj.name == 'VkInstance'
|
|
|
|
physical = first_obj.name == 'VkPhysicalDevice'
|
|
|
|
return instance or physical
|
2018-09-10 13:21:02 +10:00
|
|
|
|
2019-01-05 23:30:52 +11:00
|
|
|
def is_device(self, reg: Registry):
|
|
|
|
if not self.params:
|
2018-09-10 13:21:02 +10:00
|
|
|
return False
|
2019-01-05 23:30:52 +11:00
|
|
|
|
|
|
|
first_name = self.params[0].type
|
|
|
|
first_obj = reg.types[first_name]
|
|
|
|
if not isinstance(first_obj, Handle):
|
2018-09-10 13:21:02 +10:00
|
|
|
return False
|
|
|
|
|
2019-03-02 22:47:03 +11:00
|
|
|
for i in ['VkDevice', 'VkQueue', 'VkCommandBuffer']:
|
|
|
|
if first_obj.has_parent(i, reg):
|
|
|
|
return True
|
|
|
|
|
|
|
|
return False
|
2018-09-10 13:21:02 +10:00
|
|
|
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-01-05 12:31:33 +11:00
|
|
|
###############################################################################
|
2019-01-05 12:14:15 +11:00
|
|
|
class Require(object):
|
2019-03-02 22:47:03 +11:00
|
|
|
def __init__(self, values, depends: List[str]):
|
|
|
|
self.values = values or []
|
|
|
|
self.depends = depends or []
|
2018-06-11 14:55:49 +10:00
|
|
|
|
2019-01-05 12:14:15 +11:00
|
|
|
def apply(self, reg: Registry, extnumber=None):
|
2018-06-11 14:55:49 +10:00
|
|
|
required = []
|
|
|
|
required += self.depends
|
|
|
|
|
|
|
|
for value in self.values:
|
|
|
|
name = value.attrib['name']
|
|
|
|
|
|
|
|
if len(value.attrib) == 1:
|
|
|
|
assert 'name' in value.attrib
|
|
|
|
required.append(name)
|
|
|
|
continue
|
|
|
|
|
2019-01-05 15:14:17 +11:00
|
|
|
if 'extends' not in value.attrib:
|
2019-01-05 12:14:15 +11:00
|
|
|
obj = Constant(value)
|
2018-06-11 14:55:49 +10:00
|
|
|
owner = reg.types['API Constants']
|
|
|
|
owner.values.append(obj)
|
|
|
|
continue
|
|
|
|
|
|
|
|
owner = reg.types[value.attrib['extends']]
|
|
|
|
|
|
|
|
if 'alias' in value.attrib:
|
2019-01-05 12:14:15 +11:00
|
|
|
owner[name] = AliasValue(name, value.attrib['alias'])
|
2018-06-11 14:55:49 +10:00
|
|
|
required.append(owner.name)
|
|
|
|
elif value.tag == 'enum':
|
2019-01-05 12:14:15 +11:00
|
|
|
owner[name] = Constant(value, extnumber=extnumber or int(value.attrib.get('extnumber', '0')))
|
2018-06-11 14:55:49 +10:00
|
|
|
required.append(owner.name)
|
|
|
|
elif value.tag == 'command':
|
|
|
|
required.append(name)
|
|
|
|
else:
|
2019-01-05 12:29:35 +11:00
|
|
|
raise RuntimeError("Unknown type")
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
return required
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
|
2019-01-05 12:31:33 +11:00
|
|
|
# -----------------------------------------------------------------------------
|
2019-01-05 12:14:15 +11:00
|
|
|
class Feature(Type):
|
2019-03-13 14:49:21 +11:00
|
|
|
def __init__(self, name: str, requires: List[Require] = None):
|
2018-06-11 14:55:49 +10:00
|
|
|
super().__init__(name)
|
2019-03-13 14:49:21 +11:00
|
|
|
self.requires = requires or []
|
2018-06-11 14:55:49 +10:00
|
|
|
|
2019-01-05 12:14:15 +11:00
|
|
|
def define(self, reg: Registry):
|
2018-06-11 14:55:49 +10:00
|
|
|
return "#define %s" % self.name
|
|
|
|
|
2019-01-05 12:14:15 +11:00
|
|
|
def apply(self, reg: Registry):
|
2018-06-11 14:55:49 +10:00
|
|
|
logging.info("Applying feature:", self.name, file=sys.stderr)
|
|
|
|
|
|
|
|
result = []
|
|
|
|
for r in self.requires:
|
|
|
|
result += r.apply(reg)
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
2019-01-05 12:31:33 +11:00
|
|
|
# -----------------------------------------------------------------------------
|
2019-01-05 12:14:15 +11:00
|
|
|
class Extension(Type):
|
2018-06-11 14:55:49 +10:00
|
|
|
def __init__(self, root):
|
|
|
|
assert root.tag == 'extension'
|
|
|
|
|
|
|
|
name = root.attrib['name']
|
|
|
|
super().__init__(name)
|
|
|
|
|
|
|
|
if 'requires' in root.attrib:
|
|
|
|
self.depends += root.attrib['requires'].split(',')
|
|
|
|
|
|
|
|
self.number = int(root.attrib['number'])
|
|
|
|
self.platform = root.attrib.get('platform')
|
|
|
|
|
|
|
|
self.requires = []
|
|
|
|
|
|
|
|
for node in root:
|
|
|
|
if node.tag == 'require':
|
2019-03-02 22:47:03 +11:00
|
|
|
self.requires.append(parse_require(node))
|
2018-06-11 14:55:49 +10:00
|
|
|
else:
|
2019-01-05 12:29:35 +11:00
|
|
|
raise RuntimeError("Unknown extension node")
|
2018-06-11 14:55:49 +10:00
|
|
|
|
2019-01-05 12:14:15 +11:00
|
|
|
def apply(self, reg: Registry, platform: Set[str]):
|
2018-06-11 14:55:49 +10:00
|
|
|
if self.name in reg.applied:
|
|
|
|
return []
|
|
|
|
reg.applied.add(self.name)
|
|
|
|
|
2018-08-23 13:56:12 +10:00
|
|
|
if self.platform and self.platform not in platform:
|
2018-06-11 14:55:49 +10:00
|
|
|
return []
|
|
|
|
|
|
|
|
required = []
|
|
|
|
|
|
|
|
for dep in self.depends:
|
2018-08-23 13:56:12 +10:00
|
|
|
required = reg.extensions[dep].apply(reg, platform)
|
2018-06-11 14:55:49 +10:00
|
|
|
|
|
|
|
logging.info("Applying extension:", self.name, file=sys.stderr)
|
|
|
|
for node in self.requires:
|
2019-01-05 15:04:36 +11:00
|
|
|
required += node.apply(reg, extnumber=self.number)
|
2018-06-11 14:55:49 +10:00
|
|
|
return required
|
|
|
|
|
2017-09-05 17:19:51 +10:00
|
|
|
|
2017-09-04 15:08:36 +10:00
|
|
|
###############################################################################
|
2019-01-05 15:04:36 +11:00
|
|
|
def ignore_node(types: Dict[str, Type], root):
|
2018-06-11 14:55:49 +10:00
|
|
|
pass
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-01-05 15:17:41 +11:00
|
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
2018-06-11 14:55:49 +10:00
|
|
|
parse_comment = ignore_node
|
|
|
|
parse_vendorids = ignore_node
|
|
|
|
parse_platforms = ignore_node
|
|
|
|
parse_tags = ignore_node
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
|
2019-01-05 15:17:41 +11:00
|
|
|
# -----------------------------------------------------------------------------
|
2019-01-05 12:14:15 +11:00
|
|
|
def parse_types(reg: Registry, root):
|
2018-06-11 14:55:49 +10:00
|
|
|
assert root.tag == 'types'
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
for t in root.findall('type'):
|
|
|
|
name = t.attrib.get ('name') or t.find('name').text
|
|
|
|
assert name not in reg.types
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
if 'alias' in t.attrib:
|
|
|
|
name = t.attrib['name']
|
|
|
|
target = t.attrib['alias']
|
2017-09-05 17:19:51 +10:00
|
|
|
|
2019-01-05 12:14:15 +11:00
|
|
|
reg.types[name] = AliasType(name, target)
|
2018-06-11 14:55:49 +10:00
|
|
|
continue
|
2017-09-05 17:19:51 +10:00
|
|
|
|
2019-03-13 14:49:21 +11:00
|
|
|
category = t.attrib.get('category')
|
2017-09-06 12:11:20 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
# if we don't have a category we should have a bare type that has a
|
|
|
|
# dependency on something like a header.
|
|
|
|
#
|
|
|
|
# eg, 'Display' depends on 'X11/Xlib.h'
|
|
|
|
if not category:
|
2019-01-05 15:04:36 +11:00
|
|
|
reg.types[name] = Placeholder(name)
|
2018-06-11 14:55:49 +10:00
|
|
|
else:
|
|
|
|
# Whitelist the known types so we don't accidentally instantiate
|
|
|
|
# something whacky
|
|
|
|
|
2019-01-05 12:14:15 +11:00
|
|
|
supported_categories = {
|
|
|
|
'include': Include,
|
|
|
|
'define': Define,
|
|
|
|
'bitmask': Bitmask,
|
|
|
|
'basetype': BaseType,
|
|
|
|
'handle': Handle,
|
|
|
|
'enum': Enum,
|
|
|
|
'funcpointer': FuncPointer,
|
|
|
|
'struct': Struct,
|
|
|
|
'union': Union,
|
|
|
|
}
|
|
|
|
|
|
|
|
concrete = supported_categories.get(category, None)
|
|
|
|
if concrete:
|
|
|
|
reg.types[name] = concrete(t)
|
2018-06-11 14:55:49 +10:00
|
|
|
else:
|
2019-01-05 12:29:35 +11:00
|
|
|
raise RuntimeError('unhandled type')
|
2017-09-06 12:11:20 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
if 'requires' in t.attrib:
|
|
|
|
reg.types[name].depends.append(t.attrib['requires'])
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
|
2019-01-05 12:19:03 +11:00
|
|
|
# -----------------------------------------------------------------------------
|
2019-01-05 12:14:15 +11:00
|
|
|
def parse_enums(reg: Registry, root):
|
2018-06-11 14:55:49 +10:00
|
|
|
assert root.tag == 'enums'
|
|
|
|
ownername = root.attrib['name']
|
|
|
|
owner = reg.types[ownername] if ownername != 'API Constants' else reg.types
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
for node in root.findall('./enum'):
|
|
|
|
valuename = node.attrib.get('name')
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
assert 'requires' not in node.attrib
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
if 'alias' in node.attrib:
|
2019-01-05 12:14:15 +11:00
|
|
|
owner[valuename] = AliasValue(valuename, node.attrib['alias'])
|
2018-06-11 14:55:49 +10:00
|
|
|
else:
|
2019-01-05 12:14:15 +11:00
|
|
|
owner[valuename] = Constant(node)
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-03-13 14:49:21 +11:00
|
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
2019-03-02 22:47:03 +11:00
|
|
|
def parse_param(root) -> Command.Param:
|
|
|
|
assert root.tag == 'param'
|
|
|
|
|
|
|
|
param = ""
|
|
|
|
for i in root.iter():
|
|
|
|
param += i.text or ""
|
|
|
|
param += i.tail or ""
|
|
|
|
# normalise whitespace
|
|
|
|
param = " ".join(param.split())
|
|
|
|
|
|
|
|
name = root.find('name').text
|
2019-03-13 14:49:21 +11:00
|
|
|
klass = root.find('type').text
|
2019-03-02 22:47:03 +11:00
|
|
|
depends = [root.find('type').text]
|
|
|
|
|
|
|
|
return Command.Param(
|
|
|
|
name=name,
|
2019-03-13 14:49:21 +11:00
|
|
|
klass=klass,
|
2019-03-02 22:47:03 +11:00
|
|
|
param=param,
|
|
|
|
depends=depends,
|
|
|
|
)
|
|
|
|
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-01-05 12:19:03 +11:00
|
|
|
# -----------------------------------------------------------------------------
|
2019-01-05 12:14:15 +11:00
|
|
|
def parse_commands(reg: Registry, root):
|
2018-06-11 14:55:49 +10:00
|
|
|
assert root.tag == 'commands'
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
for node in root.findall('./command'):
|
|
|
|
name = node.attrib.get('name') or node.find('./proto/name').text
|
|
|
|
assert name not in reg.types
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
if 'alias' in node.attrib:
|
2019-01-05 12:14:15 +11:00
|
|
|
reg.types[name] = AliasValue(name, node.attrib['alias'])
|
2018-06-11 14:55:49 +10:00
|
|
|
continue
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-03-02 13:54:48 +11:00
|
|
|
proto = node.find('proto')
|
|
|
|
name = proto.find('name').text
|
|
|
|
|
|
|
|
result = proto.find('type').text
|
2019-03-02 22:47:03 +11:00
|
|
|
params = [parse_param(p) for p in node.findall('./param')]
|
2019-03-02 13:54:48 +11:00
|
|
|
depends = [result]
|
|
|
|
for p in params:
|
|
|
|
depends += p.depends
|
|
|
|
|
|
|
|
reg.types[name] = Command(name, result, params, depends)
|
2017-09-06 12:11:20 +10:00
|
|
|
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-01-05 12:19:03 +11:00
|
|
|
# -----------------------------------------------------------------------------
|
2019-03-02 22:47:03 +11:00
|
|
|
def parse_require(root) -> Require:
|
|
|
|
assert root.tag == 'require'
|
|
|
|
|
|
|
|
values = []
|
|
|
|
depends = []
|
|
|
|
|
|
|
|
for node in root:
|
|
|
|
if node.tag == 'enum':
|
|
|
|
values.append(node)
|
|
|
|
elif node.tag in ['command', 'type']:
|
|
|
|
depends.append(node.attrib['name'])
|
|
|
|
elif node.tag in ['comment']:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
raise RuntimeError("Unknown requires node")
|
|
|
|
|
|
|
|
return Require(values=values, depends=depends)
|
|
|
|
|
2019-03-13 14:49:21 +11:00
|
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
2019-01-05 12:14:15 +11:00
|
|
|
def parse_feature(reg: Registry, root):
|
2018-06-11 14:55:49 +10:00
|
|
|
assert root.tag == 'feature'
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-01-05 12:16:52 +11:00
|
|
|
name = root.attrib['name']
|
2018-06-11 14:55:49 +10:00
|
|
|
assert name not in reg.features
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-03-02 16:01:04 +11:00
|
|
|
requires = []
|
|
|
|
for node in root:
|
|
|
|
if 'require' == node.tag:
|
2019-03-02 22:47:03 +11:00
|
|
|
requires.append(parse_require(node))
|
2019-03-02 16:01:04 +11:00
|
|
|
else:
|
|
|
|
raise RuntimeError("Unhandled feature node")
|
|
|
|
|
|
|
|
reg.features[name] = Feature(name, requires)
|
2018-06-11 14:55:49 +10:00
|
|
|
reg.types[name] = reg.features[name]
|
2017-09-05 17:19:51 +10:00
|
|
|
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2019-01-05 12:19:03 +11:00
|
|
|
# -----------------------------------------------------------------------------
|
2019-01-05 12:14:15 +11:00
|
|
|
def parse_extensions(reg: Registry, root):
|
2018-06-11 14:55:49 +10:00
|
|
|
assert root.tag == 'extensions'
|
2017-09-04 15:08:36 +10:00
|
|
|
|
2018-06-11 14:55:49 +10:00
|
|
|
for node in root.findall('./extension'):
|
|
|
|
name = node.attrib['name']
|
|
|
|
assert name not in reg.extensions
|
|
|
|
|
2019-01-05 12:14:15 +11:00
|
|
|
reg.extensions[name] = Extension(node)
|
2017-09-04 15:08:36 +10:00
|
|
|
|
|
|
|
|
|
|
|
###############################################################################
|
2019-01-05 15:29:13 +11:00
|
|
|
def write_header(dst: TextIO, q: List[Type], reg: Registry):
|
|
|
|
dst.write("#pragma once\n")
|
2019-01-05 11:53:03 +11:00
|
|
|
|
2019-01-05 15:29:13 +11:00
|
|
|
# Write the declarations and definitions for all types.
|
|
|
|
for obj in q:
|
|
|
|
dst.write(obj.declare())
|
|
|
|
dst.write('\n')
|
|
|
|
dst.write(obj.define(reg))
|
|
|
|
dst.write('\n')
|
2018-09-12 14:10:39 +10:00
|
|
|
|
2019-01-05 15:29:13 +11:00
|
|
|
# Define the default case for device and instance type traits.
|
|
|
|
dst.write("""
|
|
|
|
#include <type_traits>
|
2018-11-05 15:04:04 +11:00
|
|
|
|
2019-01-05 15:29:13 +11:00
|
|
|
/// A type trait that tests if a Vulkan type is an instance type
|
|
|
|
template <typename NativeT>
|
|
|
|
struct is_instance:
|
|
|
|
public std::false_type
|
|
|
|
{};
|
2018-11-05 15:04:04 +11:00
|
|
|
|
2019-01-05 11:53:03 +11:00
|
|
|
|
2019-01-05 15:29:13 +11:00
|
|
|
/// A type trait that tests if a Vulkan type is a device type
|
|
|
|
template <typename NativeT>
|
|
|
|
struct is_device:
|
|
|
|
public std::false_type
|
|
|
|
{};
|
2018-11-05 15:04:04 +11:00
|
|
|
|
|
|
|
|
2019-01-05 15:29:13 +11:00
|
|
|
template <typename T>
|
|
|
|
constexpr auto is_instance_v = is_instance<T>::value;
|
2018-11-05 15:04:04 +11:00
|
|
|
|
2019-01-05 15:29:13 +11:00
|
|
|
template <typename T>
|
|
|
|
constexpr auto is_device_v = is_device<T>::value;
|
|
|
|
""")
|
2018-09-12 14:10:39 +10:00
|
|
|
|
2019-01-05 15:29:13 +11:00
|
|
|
# Specialise traits for device and instance types.
|
|
|
|
for obj in q:
|
|
|
|
if not isinstance(obj, Handle):
|
|
|
|
continue
|
2018-09-12 14:10:39 +10:00
|
|
|
|
2019-01-05 15:29:13 +11:00
|
|
|
device_value = "true_type" if obj.has_parent("VkDevice", reg) else "false_type"
|
|
|
|
instance_value = "true_type" if obj.has_parent("VkInstance", reg) else "false_type"
|
2018-09-12 14:10:39 +10:00
|
|
|
|
2019-01-05 15:29:13 +11:00
|
|
|
dst.write(f"""
|
|
|
|
template <> struct is_instance<{obj.name}>: public std::{instance_value} {{ }};
|
|
|
|
template <> struct is_device<{obj.name}>: public std::{device_value} {{ }};
|
|
|
|
""")
|
2018-08-24 17:33:09 +10:00
|
|
|
|
2019-01-05 11:53:03 +11:00
|
|
|
|
2019-01-05 12:19:03 +11:00
|
|
|
# -----------------------------------------------------------------------------
|
2019-03-03 11:53:31 +11:00
|
|
|
def write_load(dst: TextIO, q: List[Type], reg: Registry):
|
2019-01-05 15:29:13 +11:00
|
|
|
commands = [i for i in q if isinstance(i, Command)]
|
2018-09-10 13:21:02 +10:00
|
|
|
|
2019-01-05 23:30:52 +11:00
|
|
|
collections = {
|
|
|
|
'instance': Command.is_instance,
|
|
|
|
'device': Command.is_device,
|
|
|
|
}
|
2018-09-10 13:21:02 +10:00
|
|
|
|
2019-01-05 15:29:13 +11:00
|
|
|
dst.write(f"""
|
2019-01-05 23:30:52 +11:00
|
|
|
#pragma once
|
2018-09-08 12:32:20 +10:00
|
|
|
|
2019-01-05 23:30:52 +11:00
|
|
|
#include <cruft/vk/vk.hpp>
|
2018-09-10 13:21:02 +10:00
|
|
|
|
2019-01-05 23:30:52 +11:00
|
|
|
#include <cruft/util/preprocessor.hpp>
|
2018-09-10 13:21:02 +10:00
|
|
|
|
2019-03-03 11:53:31 +11:00
|
|
|
namespace cruft::vk::load {{
|
2019-01-05 15:29:13 +11:00
|
|
|
class vendor;
|
2018-09-10 13:21:02 +10:00
|
|
|
|
2019-01-05 23:30:52 +11:00
|
|
|
#define MAP_COMMANDS(FUNC) MAP0(FUNC,{",".join(i.name for i in commands)})
|
2018-09-10 13:21:02 +10:00
|
|
|
|
2019-01-05 15:29:13 +11:00
|
|
|
""")
|
2018-09-08 12:32:20 +10:00
|
|
|
|
2019-01-05 23:30:52 +11:00
|
|
|
for name, test in collections.items():
|
2019-03-13 14:49:21 +11:00
|
|
|
curr = [i for i in commands if test(i, reg)]
|
2019-03-02 22:47:03 +11:00
|
|
|
next = [i for i in commands if not test(i, reg)]
|
|
|
|
commands = next
|
|
|
|
|
2019-01-05 23:30:52 +11:00
|
|
|
dst.write(f"""
|
|
|
|
#define MAP_{name.upper()}_COMMANDS(FUNC) \
|
2019-03-02 22:47:03 +11:00
|
|
|
MAP0(FUNC,{",".join([i.name for i in curr])})
|
2018-09-10 13:21:02 +10:00
|
|
|
|
2019-01-05 23:30:52 +11:00
|
|
|
struct {name}_table{{
|
|
|
|
""")
|
|
|
|
|
|
|
|
# Generate the vtable entries for instance methods
|
|
|
|
dst.writelines((
|
|
|
|
f"{obj.result} (*{obj.name}) ({','.join(p.param for p in obj.params)}) = nullptr;"
|
2019-03-02 22:47:03 +11:00
|
|
|
for obj in curr
|
2019-01-05 23:30:52 +11:00
|
|
|
))
|
2018-09-10 13:21:02 +10:00
|
|
|
|
2019-01-05 23:30:52 +11:00
|
|
|
dst.write("""
|
|
|
|
};
|
|
|
|
""")
|
2018-09-10 13:21:02 +10:00
|
|
|
|
2019-01-05 15:29:13 +11:00
|
|
|
dst.write("""
|
2019-03-02 22:47:03 +11:00
|
|
|
struct vendor_table;
|
2019-03-03 12:18:58 +11:00
|
|
|
extern cruft::vk::load::vendor_table const *v_table [[gnu::visibility("default")]];
|
2019-01-05 15:29:13 +11:00
|
|
|
}
|
|
|
|
""")
|
2018-09-08 12:32:20 +10:00
|
|
|
|
|
|
|
|
2019-01-05 12:19:03 +11:00
|
|
|
# -----------------------------------------------------------------------------
|
2019-01-05 15:29:13 +11:00
|
|
|
def write_dispatch(dst: TextIO, q: List[Type], reg: Registry):
|
|
|
|
dst.write("""
|
2019-01-05 23:30:52 +11:00
|
|
|
#include <cruft/vk/vk.hpp>
|
2019-03-03 11:53:31 +11:00
|
|
|
#include <cruft/vk/load/vtable.hpp>
|
|
|
|
#include <cruft/vk/load/dispatch.hpp>
|
2018-11-05 15:04:04 +11:00
|
|
|
|
2019-01-05 15:29:13 +11:00
|
|
|
#include <cruft/util/debug.hpp>
|
2019-03-03 22:12:46 +11:00
|
|
|
|
|
|
|
#include <cstring>
|
2019-03-04 10:50:25 +11:00
|
|
|
#include <iostream>
|
2018-08-24 17:33:09 +10:00
|
|
|
|
2019-01-05 15:29:13 +11:00
|
|
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
2018-09-08 12:32:20 +10:00
|
|
|
|
2019-03-02 22:47:03 +11:00
|
|
|
template <typename HandleT, typename TableT>
|
2019-01-05 23:30:52 +11:00
|
|
|
struct indirect {
|
2019-03-02 22:47:03 +11:00
|
|
|
HandleT handle;
|
|
|
|
TableT table;
|
2019-01-05 23:30:52 +11:00
|
|
|
};
|
2019-01-05 15:29:13 +11:00
|
|
|
""")
|
2018-09-08 12:32:20 +10:00
|
|
|
|
2019-03-02 22:47:03 +11:00
|
|
|
implementations = {
|
|
|
|
'vkCreateInstance': """
|
2019-03-03 11:53:31 +11:00
|
|
|
auto res = std::make_unique<indirect<VkInstance,cruft::vk::load::instance_table>> ();
|
2019-03-11 10:43:11 +11:00
|
|
|
auto ptr = reinterpret_cast<decltype(vkCreateInstance)*> (cruft::vk::load::v_table->vk_icdGetInstanceProcAddr (nullptr, "vkCreateInstance"));
|
2019-03-03 23:36:14 +11:00
|
|
|
auto err = (*ptr) (pCreateInfo, pAllocator, &res->handle);
|
2019-03-02 22:47:03 +11:00
|
|
|
if (err != VK_SUCCESS)
|
|
|
|
return err;
|
|
|
|
|
2019-03-13 18:16:49 +11:00
|
|
|
#define GET(NAME) res->table.NAME = reinterpret_cast<decltype(NAME)*> (cruft::vk::load::v_table->vk_icdGetInstanceProcAddr (res->handle, #NAME));
|
2019-03-02 22:47:03 +11:00
|
|
|
MAP_INSTANCE_COMMANDS(GET)
|
|
|
|
#undef GET
|
|
|
|
|
2019-03-13 18:16:49 +11:00
|
|
|
#define GET(NAME) if (!res->table.NAME) res->table.NAME = reinterpret_cast<decltype(NAME)*> (res->table.vkGetInstanceProcAddr (res->handle, #NAME));
|
2019-03-02 22:47:03 +11:00
|
|
|
MAP_INSTANCE_COMMANDS(GET)
|
|
|
|
#undef GET
|
|
|
|
|
2019-03-11 10:43:11 +11:00
|
|
|
*pInstance = reinterpret_cast<VkInstance> (res.release ());
|
2019-03-02 22:47:03 +11:00
|
|
|
return err;
|
|
|
|
""",
|
|
|
|
|
2019-03-03 16:49:31 +11:00
|
|
|
'vkGetInstanceProcAddr': """
|
2019-03-04 10:51:12 +11:00
|
|
|
std::clog << "InstanceProcAddr: " << pName << '\\n';
|
2019-03-03 22:12:46 +11:00
|
|
|
#define ATTEMPT(NAME) if (!strcmp (#NAME, pName)) return reinterpret_cast<PFN_vkVoidFunction> (&NAME);
|
|
|
|
MAP_INSTANCE_COMMANDS(ATTEMPT)
|
|
|
|
#undef ATTEMPT
|
|
|
|
return nullptr;
|
2019-03-03 16:49:31 +11:00
|
|
|
""",
|
|
|
|
|
2019-03-02 22:47:03 +11:00
|
|
|
'vkCreateDevice': """
|
|
|
|
(void)physicalDevice;
|
|
|
|
(void)pCreateInfo;
|
|
|
|
(void)pAllocator;
|
|
|
|
(void)pDevice;
|
|
|
|
unimplemented ();
|
|
|
|
""",
|
|
|
|
|
|
|
|
'vkEnumeratePhysicalDevices': """
|
|
|
|
auto const entry = reinterpret_cast<
|
2019-03-03 11:53:31 +11:00
|
|
|
indirect<VkInstance,cruft::vk::load::instance_table> const*
|
2019-03-02 22:47:03 +11:00
|
|
|
> (instance);
|
|
|
|
|
|
|
|
if (!pPhysicalDeviceCount || !pPhysicalDevices) {
|
|
|
|
return (entry->table.vkEnumeratePhysicalDevices)(
|
|
|
|
entry->handle, pPhysicalDeviceCount, pPhysicalDevices
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<VkPhysicalDevice> res (*pPhysicalDeviceCount);
|
|
|
|
auto err = (entry->table.vkEnumeratePhysicalDevices)(
|
|
|
|
entry->handle, pPhysicalDeviceCount, res.data ()
|
|
|
|
);
|
|
|
|
res.resize (*pPhysicalDeviceCount);
|
|
|
|
|
|
|
|
if (err != VK_SUCCESS)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < *pPhysicalDeviceCount; ++i) {
|
2019-03-03 11:53:31 +11:00
|
|
|
auto wrapped = std::make_unique<indirect<VkPhysicalDevice,cruft::vk::load::instance_table>> ();
|
2019-03-02 22:47:03 +11:00
|
|
|
wrapped->handle = res[i];
|
|
|
|
|
2019-03-04 10:51:12 +11:00
|
|
|
//#define GET(NAME) wrapped->table.NAME = wrapped->table.NAME ?: reinterpret_cast<decltype(&NAME)> (cruft::vk::load::v_table->vk_icdGetPhysicalDeviceProcAddr (entry->handle, #NAME));
|
|
|
|
//MAP_INSTANCE_COMMANDS(GET)
|
|
|
|
//#undef GET
|
2019-03-02 22:47:03 +11:00
|
|
|
|
2019-03-03 23:29:26 +11:00
|
|
|
#define GET(NAME) wrapped->table.NAME = wrapped->table.NAME ?: reinterpret_cast<decltype(&NAME)> (cruft::vk::load::v_table->vk_icdGetInstanceProcAddr (entry->handle, #NAME));
|
|
|
|
MAP_INSTANCE_COMMANDS(GET)
|
|
|
|
#undef GET
|
|
|
|
|
2019-03-04 10:51:12 +11:00
|
|
|
#define GET(NAME) wrapped->table.NAME = wrapped->table.NAME ?: reinterpret_cast<decltype(&NAME)> (entry->table.vkGetInstanceProcAddr (entry->handle, #NAME));
|
2019-03-03 23:29:26 +11:00
|
|
|
MAP_INSTANCE_COMMANDS(GET)
|
|
|
|
#undef GET
|
|
|
|
|
2019-03-11 10:43:11 +11:00
|
|
|
std::clog << "physical_device[" << i << "]; wrapped: " << wrapped.get() << ", native: " << reinterpret_cast<void const*> (wrapped->handle) << '\\n';
|
|
|
|
pPhysicalDevices[i] = reinterpret_cast<VkPhysicalDevice> (wrapped.release ());
|
2019-03-02 22:47:03 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
"""
|
|
|
|
}
|
|
|
|
|
2019-01-05 15:29:13 +11:00
|
|
|
for obj in (i for i in q if isinstance(i, Command)):
|
|
|
|
first_arg = reg.types[obj.params[0].type]
|
2018-09-12 14:10:39 +10:00
|
|
|
|
2019-03-02 22:47:03 +11:00
|
|
|
if obj.is_instance(reg):
|
2019-01-05 23:30:52 +11:00
|
|
|
table = 'instance'
|
|
|
|
elif obj.is_device(reg):
|
|
|
|
table = 'device'
|
2019-01-05 15:29:13 +11:00
|
|
|
else:
|
2019-03-02 22:47:03 +11:00
|
|
|
raise Exception(f"Unhandled command type for {obj}")
|
2019-01-05 15:29:13 +11:00
|
|
|
|
2019-01-07 11:09:06 +11:00
|
|
|
if obj.params:
|
|
|
|
last_param = obj.params[-1]
|
|
|
|
last_obj = reg.types[last_param.type]
|
|
|
|
is_creating = isinstance(last_obj, Handle) and last_param.is_pointer()
|
|
|
|
else:
|
|
|
|
is_creating = False
|
|
|
|
|
2019-03-02 22:47:03 +11:00
|
|
|
if obj.is_instance(reg) and obj.params[0].type not in ['VkInstance', 'VkPhysicalDevice']:
|
2019-03-03 22:12:46 +11:00
|
|
|
forwarding = f"""
|
2019-03-03 23:36:14 +11:00
|
|
|
auto ptr= cruft::vk::load::v_table->vk_icdGetInstanceProcAddr (nullptr, "{obj.name}");
|
2019-03-11 10:43:11 +11:00
|
|
|
return (reinterpret_cast<decltype({obj.name})*> (ptr)) ({', '.join (i.name for i in obj.params)});
|
2019-03-03 22:12:46 +11:00
|
|
|
"""
|
2019-03-02 22:47:03 +11:00
|
|
|
else:
|
|
|
|
forwarding = f"""
|
2019-03-03 22:12:46 +11:00
|
|
|
if constexpr (std::is_same_v<{obj.params[0].type}, VkInstance> ||
|
|
|
|
std::is_same_v<{obj.params[0].type}, VkPhysicalDevice>) {{
|
|
|
|
if ({obj.params[0].name} == VK_NULL_HANDLE) {{
|
|
|
|
auto ptr= cruft::vk::load::v_table->vk_icdGetInstanceProcAddr (nullptr, "{obj.name}");
|
2019-03-11 10:43:11 +11:00
|
|
|
return (reinterpret_cast<decltype({obj.name})*> (ptr)) ({', '.join (i.name for i in obj.params)});
|
2019-03-03 22:12:46 +11:00
|
|
|
}}
|
|
|
|
}}
|
|
|
|
|
2019-03-03 16:50:04 +11:00
|
|
|
auto const entry = reinterpret_cast<
|
|
|
|
indirect<{obj.params[0].type},cruft::vk::load::{table}_table> const*
|
|
|
|
> ({obj.params[0].name});
|
2019-03-02 22:47:03 +11:00
|
|
|
|
|
|
|
return (entry->table.{obj.name})(
|
|
|
|
{", ".join(['entry->handle'] + [p.name for p in obj.params[1:]])}
|
|
|
|
);
|
|
|
|
"""
|
|
|
|
|
2019-01-05 15:29:13 +11:00
|
|
|
dst.write(f"""
|
|
|
|
extern "C" {obj.result} {rename(obj.name)} ({", ".join(p.param for p in obj.params)}) noexcept {{
|
2019-03-03 23:29:44 +11:00
|
|
|
std::clog << "{obj.name}" << "\\n";
|
2019-03-02 22:47:03 +11:00
|
|
|
{implementations.get(obj.name, forwarding)}
|
2019-01-05 15:29:13 +11:00
|
|
|
}}
|
|
|
|
""")
|
2019-01-05 11:53:03 +11:00
|
|
|
|
2019-03-04 10:51:47 +11:00
|
|
|
def write_trace(dst: TextIO, q: List[Type], reg: Registry):
|
|
|
|
dst.write("""
|
|
|
|
#include <cruft/vk/vk.hpp>
|
|
|
|
#include <cruft/vk/load/vtable.hpp>
|
|
|
|
#include <cruft/vk/ostream.hpp>
|
|
|
|
#include <iostream>
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#include <cstring>
|
|
|
|
|
|
|
|
static void* g_lib;
|
|
|
|
|
|
|
|
void init [[gnu::constructor]] (void) {
|
|
|
|
g_lib = dlopen (getenv ("REAL_VULKAN"), RTLD_LAZY | RTLD_LOCAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void deinit [[gnu::destructor]] (void) {
|
|
|
|
dlclose (g_lib);
|
|
|
|
g_lib = nullptr;
|
|
|
|
}
|
|
|
|
#pragma GCC diagnostic ignored "-Wsign-promo"
|
|
|
|
""")
|
|
|
|
|
|
|
|
|
|
|
|
for obj in (i for i in q if isinstance(i, Command)):
|
|
|
|
inner = ""
|
2019-03-13 14:49:21 +11:00
|
|
|
if obj.name in ['vkGetInstanceProcAddr']:
|
2019-03-04 10:51:47 +11:00
|
|
|
inner = """
|
|
|
|
|
|
|
|
#define FWD(NAME) \\
|
|
|
|
if (!strcmp(#NAME,pName)) { \\
|
|
|
|
if (fn (nullptr, #NAME)) \\
|
2019-03-11 10:43:11 +11:00
|
|
|
return reinterpret_cast<PFN_vkVoidFunction> (NAME); \\
|
2019-03-04 10:51:47 +11:00
|
|
|
else \\
|
|
|
|
return nullptr;\\
|
|
|
|
}
|
2019-03-13 14:49:21 +11:00
|
|
|
#undef FWD
|
|
|
|
|
2019-04-01 15:22:39 +11:00
|
|
|
#define FWD(NAME) if (!strcmp(#NAME,pName)) { return reinterpret_cast<PFN_vkVoidFunction> (NAME); }
|
2019-03-04 10:51:47 +11:00
|
|
|
MAP_INSTANCE_COMMANDS(FWD)
|
|
|
|
#undef FWD
|
2019-03-13 14:49:21 +11:00
|
|
|
std::clog << "Unhooked: " << pName << '\\n';
|
2019-03-04 10:51:47 +11:00
|
|
|
"""
|
|
|
|
|
|
|
|
dst.write(f"""
|
|
|
|
extern "C" {obj.result} {rename(obj.name)} ({", ".join(p.param for p in obj.params)}) noexcept {{
|
|
|
|
std::clog << "{obj.name}" << " " << {'<< " " << '.join (p.name for p in obj.params)} << '\\n';
|
|
|
|
static auto fn = reinterpret_cast<decltype({obj.name})*> (dlsym (g_lib, "{obj.name}"));
|
|
|
|
{inner}
|
|
|
|
return (*fn) ({','.join (p.name for p in obj.params)});
|
|
|
|
}}""")
|
|
|
|
|
2019-01-05 11:53:03 +11:00
|
|
|
|
2019-01-05 12:00:31 +11:00
|
|
|
###############################################################################
|
2019-01-05 12:16:52 +11:00
|
|
|
def main():
|
2019-01-05 11:53:03 +11:00
|
|
|
logging.getLogger().setLevel(logging.WARNING)
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(description='Transform XML API specification into C++ headers')
|
|
|
|
parser.add_argument('--src', type=str, help='the path to the XML file to transform')
|
|
|
|
parser.add_argument('--dst', type=str, help='the output path for the result')
|
2019-03-03 11:53:31 +11:00
|
|
|
parser.add_argument('--load', type=str, help='the output path for the loading routines')
|
2019-01-05 11:53:03 +11:00
|
|
|
parser.add_argument('--dispatch', type=str, help="the output path for function dispatch")
|
2019-03-04 10:51:47 +11:00
|
|
|
parser.add_argument('--trace', type=str, help="the output path for the vulkan tracing library")
|
2019-01-05 11:53:03 +11:00
|
|
|
parser.add_argument(
|
|
|
|
'--platform',
|
|
|
|
type=str,
|
|
|
|
action='append',
|
|
|
|
help='a platform to generate output for. may be specific multiple times"'
|
|
|
|
)
|
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
2019-01-05 12:01:08 +11:00
|
|
|
# Get a copy of the specification XML
|
|
|
|
with open(args.src, 'r') as src:
|
2019-01-05 15:13:13 +11:00
|
|
|
tree = xml.etree.ElementTree.parse(src)
|
2019-01-05 11:53:03 +11:00
|
|
|
root = tree.getroot()
|
|
|
|
|
2019-01-05 12:01:08 +11:00
|
|
|
# Find a parser for each of the nodes in the XML
|
2019-01-05 12:14:15 +11:00
|
|
|
reg = Registry()
|
2019-01-05 11:53:03 +11:00
|
|
|
for node in root:
|
|
|
|
target = "parse_%s" % node.tag
|
|
|
|
globals()[target](reg, node)
|
|
|
|
|
2019-01-05 12:01:08 +11:00
|
|
|
# Override some requested system types so that they fit more naturally in
|
|
|
|
# our environment. eg, use appropriate C++ types, or cruft library
|
|
|
|
# wrappers.
|
2019-01-05 11:53:03 +11:00
|
|
|
reg.types['windows.h'].name = 'cruft/util/win32/windows.hpp'
|
2019-01-05 12:14:15 +11:00
|
|
|
reg.types['void*'] = Placeholder('void*')
|
|
|
|
reg.types['nullptr'] = Placeholder('nullptr')
|
|
|
|
reg.types['VkEnum'] = AliasType('VkEnum', 'int32_t')
|
|
|
|
reg.types['VK_DEFINE_NON_DISPATCHABLE_HANDLE'] = AliasType("VK_DEFINE_NON_DISPATCHABLE_HANDLE", "uint64_t")
|
|
|
|
reg.types['VK_DEFINE_HANDLE'] = AliasType("VK_DEFINE_HANDLE", "void*")
|
2019-01-05 15:04:57 +11:00
|
|
|
reg.types['VK_NULL_HANDLE'] = AliasValue("VK_NULL_HANDLE", "nullptr")
|
2019-01-05 11:53:03 +11:00
|
|
|
|
2019-01-05 12:01:08 +11:00
|
|
|
# Request a minimal set of extensions that will almost certainly be
|
|
|
|
# required for all applications.
|
2019-01-05 11:53:03 +11:00
|
|
|
extensions = ["VK_KHR_swapchain", "VK_EXT_debug_report", "VK_KHR_external_memory"]
|
|
|
|
q = reg.serialise(args.platform)
|
|
|
|
|
2019-03-03 11:53:31 +11:00
|
|
|
# Finally write out the header, vtables, and dispatch code.
|
2019-01-05 15:29:13 +11:00
|
|
|
with open(args.dst, 'w') as dst:
|
|
|
|
write_header(dst, q, reg)
|
|
|
|
|
2019-03-03 11:53:31 +11:00
|
|
|
with open(args.load, 'w') as dst:
|
|
|
|
write_load(dst, q, reg)
|
2019-01-05 15:29:13 +11:00
|
|
|
|
|
|
|
with open(args.dispatch, 'w') as dst:
|
|
|
|
write_dispatch(dst, q, reg)
|
2019-01-05 12:16:52 +11:00
|
|
|
|
2019-03-04 10:51:47 +11:00
|
|
|
with open(args.trace, 'w') as dst:
|
|
|
|
write_trace(dst, q, reg)
|
|
|
|
|
2019-01-05 12:19:03 +11:00
|
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
2019-01-05 12:16:52 +11:00
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|