flycast/core/deps/volk/generate.py

170 lines
5.1 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 sys
import urllib
import xml.etree.ElementTree as etree
import urllib.request
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') 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 + ')'
if __name__ == "__main__":
specpath = "https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/master/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'):
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 supported == 'disabled':
continue
name = ext.get('name')
type = ext.get('type')
for req in ext.findall('require'):
key = defined(name)
if req.get('feature'):
key += ' && ' + defined(req.get('feature'))
if req.get('extension'):
key += ' && ' + defined(req.get('extension'))
cmdrefs = req.findall('command')
command_groups.setdefault(key, []).extend([cmdref.get('name') for cmdref in cmdrefs])
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)