2024-11-05 07:10:41 -06:00

197 lines
6.0 KiB
Python
Executable File

#!/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
cmdversions = {
"vkCmdSetDiscardRectangleEnableEXT": 2,
"vkCmdSetDiscardRectangleModeEXT": 2,
"vkCmdSetExclusiveScissorEnableNV": 2,
"vkGetImageViewAddressNVX": 2,
"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 not type:
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 = ('DEVICE_TABLE', 'PROTOTYPES_H', 'PROTOTYPES_C', 'LOAD_LOADER', 'LOAD_INSTANCE', '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
key = defined(feature.get('name'))
cmdrefs = feature.findall('require/command')
command_groups[key] = [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] = ''
for (group, cmdnames) in command_groups.items():
ifdef = '#if ' + group + '\n'
for key in block_keys:
blocks[key] += ifdef
for name in sorted(cmdnames):
cmd = commands[name]
type = cmd.findtext('param[1]/type')
if name == 'vkGetInstanceProcAddr':
type = ''
if name == 'vkGetDeviceProcAddr':
type = 'VkInstance'
if is_descendant_type(types, type, 'VkDevice') and name not in instance_commands:
blocks['LOAD_DEVICE'] += '\t' + name + ' = (PFN_' + name + ')load(context, "' + name + '");\n'
blocks['DEVICE_TABLE'] += '\tPFN_' + name + ' ' + name + ';\n'
blocks['LOAD_DEVICE_TABLE'] += '\ttable->' + name + ' = (PFN_' + name + ')load(context, "' + name + '");\n'
elif is_descendant_type(types, type, 'VkInstance'):
blocks['LOAD_INSTANCE'] += '\t' + name + ' = (PFN_' + name + ')load(context, "' + name + '");\n'
elif type != '':
blocks['LOAD_LOADER'] += '\t' + name + ' = (PFN_' + name + ')load(context, "' + name + '");\n'
blocks['PROTOTYPES_H'] += 'extern PFN_' + name + ' ' + name + ';\n'
blocks['PROTOTYPES_C'] += 'PFN_' + name + ' ' + name + ';\n'
for key in block_keys:
if blocks[key].endswith(ifdef):
blocks[key] = blocks[key][:-len(ifdef)]
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())