| #!/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 |
| } |
| |
| 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 = ('INSTANCE_TABLE', 'DEVICE_TABLE', 'PROTOTYPES_H', '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 |
| 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' |
| |
| 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 |
| elif is_descendant_type(types, type, 'VkInstance'): |
| blocks['LOAD_INSTANCE'] += load_fn |
| blocks['INSTANCE_TABLE'] += def_table |
| blocks['LOAD_INSTANCE_TABLE'] += load_table |
| elif type != '': |
| blocks['LOAD_LOADER'] += load_fn |
| |
| 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()) |