1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
|
#!/usr/bin/python3
# This file is part of volk library; see volk.h for version/license details
from collections import OrderedDict
import re
import sys
import urllib
import xml.etree.ElementTree as etree
import urllib.request
import zlib
cmdversions = {
"vkCmdSetDiscardRectangleEnableEXT": 2,
"vkCmdSetDiscardRectangleModeEXT": 2,
"vkCmdSetExclusiveScissorEnableNV": 2,
"vkGetImageViewAddressNVX": 2,
"vkGetImageViewHandle64NVX": 3,
"vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI": 2,
}
def parse_xml(path):
file = urllib.request.urlopen(path) if path.startswith("http") else open(path, 'r')
with file:
tree = etree.parse(file)
return tree
def patch_file(path, blocks):
result = []
block = None
with open(path, 'r') as file:
for line in file.readlines():
if block:
if line == block:
result.append(line)
block = None
else:
result.append(line)
# C comment marker
if line.strip().startswith('/* VOLK_GENERATE_'):
block = line
result.append(blocks[line.strip()[17:-3]])
# Shell/CMake comment marker
elif line.strip().startswith('# VOLK_GENERATE_'):
block = line
result.append(blocks[line.strip()[16:]])
with open(path, 'w', newline='\n') as file:
for line in result:
file.write(line)
def is_descendant_type(types, name, base):
if name == base:
return True
type = types.get(name)
if type is None:
return False
parents = type.get('parent')
if not parents:
return False
return any([is_descendant_type(types, parent, base) for parent in parents.split(',')])
def defined(key):
return 'defined(' + key + ')'
def cdepends(key):
return re.sub(r'[a-zA-Z0-9_]+', lambda m: defined(m.group(0)), key).replace(',', ' || ').replace('+', ' && ')
if __name__ == "__main__":
specpath = "https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/main/xml/vk.xml"
if len(sys.argv) > 1:
specpath = sys.argv[1]
spec = parse_xml(specpath)
block_keys = ('INSTANCE_TABLE', 'DEVICE_TABLE', 'PROTOTYPES_H', 'PROTOTYPES_H_DEVICE', 'PROTOTYPES_C', 'LOAD_LOADER', 'LOAD_INSTANCE', 'LOAD_INSTANCE_TABLE', 'LOAD_DEVICE', 'LOAD_DEVICE_TABLE')
blocks = {}
version = spec.find('types/type[name="VK_HEADER_VERSION"]')
blocks['VERSION'] = version.find('name').tail.strip() + '\n'
blocks['VERSION_DEFINE'] = '#define VOLK_HEADER_VERSION ' + version.find('name').tail.strip() + '\n'
command_groups = OrderedDict()
instance_commands = set()
for feature in spec.findall('feature'):
api = feature.get('api')
if 'vulkan' not in api.split(','):
continue
name = feature.get('name')
name = re.sub(r'VK_(BASE|COMPUTE|GRAPHICS)_VERSION_', 'VK_VERSION_', name) # strip Vulkan Base prefixes for compatibility
key = defined(name)
cmdrefs = feature.findall('require/command')
command_groups.setdefault(key, []).extend([cmdref.get('name') for cmdref in cmdrefs])
for ext in sorted(spec.findall('extensions/extension'), key=lambda ext: ext.get('name')):
supported = ext.get('supported')
if 'vulkan' not in supported.split(','):
continue
name = ext.get('name')
type = ext.get('type')
for req in ext.findall('require'):
key = defined(name)
if req.get('feature'): # old-style XML depends specification
for i in req.get('feature').split(','):
key += ' && ' + defined(i)
if req.get('extension'): # old-style XML depends specification
for i in req.get('extension').split(','):
key += ' && ' + defined(i)
if req.get('depends'): # new-style XML depends specification
dep = cdepends(req.get('depends'))
key += ' && ' + ('(' + dep + ')' if '||' in dep else dep)
cmdrefs = req.findall('command')
for cmdref in cmdrefs:
ver = cmdversions.get(cmdref.get('name'))
if ver:
command_groups.setdefault(key + ' && ' + name.upper() + '_SPEC_VERSION >= ' + str(ver), []).append(cmdref.get('name'))
else:
command_groups.setdefault(key, []).append(cmdref.get('name'))
if type == 'instance':
for cmdref in cmdrefs:
instance_commands.add(cmdref.get('name'))
commands_to_groups = OrderedDict()
for (group, cmdnames) in command_groups.items():
for name in cmdnames:
commands_to_groups.setdefault(name, []).append(group)
for (group, cmdnames) in command_groups.items():
command_groups[group] = [name for name in cmdnames if len(commands_to_groups[name]) == 1]
for (name, groups) in commands_to_groups.items():
if len(groups) == 1:
continue
key = ' || '.join(['(' + g + ')' for g in groups])
command_groups.setdefault(key, []).append(name)
commands = {}
for cmd in spec.findall('commands/command'):
if not cmd.get('alias'):
name = cmd.findtext('proto/name')
commands[name] = cmd
for cmd in spec.findall('commands/command'):
if cmd.get('alias'):
name = cmd.get('name')
commands[name] = commands[cmd.get('alias')]
types = {}
for type in spec.findall('types/type'):
name = type.findtext('name')
if name:
types[name] = type
for key in block_keys:
blocks[key] = ''
devp = {}
instp = {}
for (group, cmdnames) in command_groups.items():
ifdef = '#if ' + group + '\n'
for key in block_keys:
blocks[key] += ifdef
devt = 0
devo = len(blocks['DEVICE_TABLE'])
instt = 0
insto = len(blocks['INSTANCE_TABLE'])
for name in sorted(cmdnames):
cmd = commands[name]
type = cmd.findtext('param[1]/type')
if name == 'vkGetInstanceProcAddr':
type = ''
if name == 'vkGetDeviceProcAddr':
type = 'VkInstance'
extern_fn = 'extern PFN_' + name + ' ' + name + ';\n'
load_fn = '\t' + name + ' = (PFN_' + name + ')load(context, "' + name + '");\n'
def_table = '\tPFN_' + name + ' ' + name + ';\n'
load_table = '\ttable->' + name + ' = (PFN_' + name + ')load(context, "' + name + '");\n'
if is_descendant_type(types, type, 'VkDevice') and name not in instance_commands:
blocks['LOAD_DEVICE'] += load_fn
blocks['DEVICE_TABLE'] += def_table
blocks['LOAD_DEVICE_TABLE'] += load_table
blocks['PROTOTYPES_H_DEVICE'] += extern_fn
devt += 1
elif is_descendant_type(types, type, 'VkInstance'):
blocks['LOAD_INSTANCE'] += load_fn
blocks['PROTOTYPES_H'] += extern_fn
blocks['INSTANCE_TABLE'] += def_table
blocks['LOAD_INSTANCE_TABLE'] += load_table
instt += 1
elif type != '':
blocks['LOAD_LOADER'] += load_fn
blocks['PROTOTYPES_H'] += extern_fn
else:
blocks['PROTOTYPES_H'] += extern_fn
blocks['PROTOTYPES_C'] += 'PFN_' + name + ' ' + name + ';\n'
for key in block_keys:
if blocks[key].endswith(ifdef):
blocks[key] = blocks[key][:-len(ifdef)]
elif key == 'DEVICE_TABLE':
devh = zlib.crc32(blocks[key][devo:].encode())
assert(devh not in devp)
devp[devh] = True
blocks[key] += '#else\n'
blocks[key] += f'\tPFN_vkVoidFunction padding_{devh:x}[{devt}];\n'
blocks[key] += '#endif /* ' + group + ' */\n'
elif key == 'INSTANCE_TABLE':
insth = zlib.crc32(blocks[key][insto:].encode())
assert(insth not in instp)
instp[insth] = True
blocks[key] += '#else\n'
blocks[key] += f'\tPFN_vkVoidFunction padding_{insth:x}[{instt}];\n'
blocks[key] += '#endif /* ' + group + ' */\n'
else:
blocks[key] += '#endif /* ' + group + ' */\n'
patch_file('volk.h', blocks)
patch_file('volk.c', blocks)
patch_file('CMakeLists.txt', blocks)
print(version.find('name').tail.strip())
|