X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Famd%2Fvulkan%2Fradv_entrypoints_gen.py;h=8eb18e64b640184af5ac4ab41894f9b8a5cf0aa9;hb=ed41e776d03d27d2874f9b02e00bbd1f2226528f;hp=c981c0be13a10bcc4931b07f42185fdb1f6f59c4;hpb=e344cd81783255eb5de762e5bd56bd4dfe8ae0c2;p=mesa.git diff --git a/src/amd/vulkan/radv_entrypoints_gen.py b/src/amd/vulkan/radv_entrypoints_gen.py index c981c0be13a..8eb18e64b64 100644 --- a/src/amd/vulkan/radv_entrypoints_gen.py +++ b/src/amd/vulkan/radv_entrypoints_gen.py @@ -24,9 +24,11 @@ import argparse import functools +import math import os import xml.etree.cElementTree as et +from collections import OrderedDict, namedtuple from mako.template import Template from radv_extensions import * @@ -36,6 +38,12 @@ from radv_extensions import * # function and a power-of-two size table. The prime numbers are determined # experimentally. +# We currently don't use layers in radv, but keeping the ability for anv +# anyways, so we can use it for device groups. +LAYERS = [ + 'radv' +] + TEMPLATE_H = Template("""\ /* This file generated from ${filename}, don't edit directly. */ @@ -43,28 +51,33 @@ struct radv_dispatch_table { union { void *entrypoints[${len(entrypoints)}]; struct { - % for _, name, _, _, _, guard in entrypoints: - % if guard is not None: -#ifdef ${guard} - PFN_vk${name} ${name}; + % for e in entrypoints: + % if e.guard is not None: +#ifdef ${e.guard} + PFN_${e.name} ${e.name}; #else - void *${name}; + void *${e.name}; # endif % else: - PFN_vk${name} ${name}; + PFN_${e.name} ${e.name}; % endif % endfor }; }; }; -% for type_, name, args, num, h, guard in entrypoints: - % if guard is not None: -#ifdef ${guard} +% for e in entrypoints: + % if e.alias: + <% continue %> + % endif + % if e.guard is not None: +#ifdef ${e.guard} % endif - ${type_} radv_${name}(${args}); - % if guard is not None: -#endif // ${guard} + % for layer in LAYERS: + ${e.return_type} ${e.prefixed_name(layer)}(${e.decl_params()}); + % endfor + % if e.guard is not None: +#endif // ${e.guard} % endif % endfor """, output_encoding='utf-8') @@ -97,9 +110,10 @@ TEMPLATE_C = Template(u"""\ #include "radv_private.h" -struct radv_entrypoint { +struct string_map_entry { uint32_t name; uint32_t hash; + uint32_t num; }; /* We use a big string constant to avoid lots of reloctions from the entry @@ -108,14 +122,29 @@ struct radv_entrypoint { */ static const char strings[] = -% for _, name, _, _, _, _ in entrypoints: - "vk${name}\\0" +% for s in strmap.sorted_strings: + "${s.string}\\0" % endfor ; -static const struct radv_entrypoint entrypoints[] = { -% for _, name, _, num, h, _ in entrypoints: - [${num}] = { ${offsets[num]}, ${'{:0=#8x}'.format(h)} }, /* vk${name} */ +static const struct string_map_entry string_map_entries[] = { +% for s in strmap.sorted_strings: + { ${s.offset}, ${'{:0=#8x}'.format(s.hash)}, ${s.num} }, /* ${s.string} */ +% endfor +}; + +/* Hash table stats: + * size ${len(strmap.sorted_strings)} entries + * collisions entries: +% for i in xrange(10): + * ${i}${'+' if i == 9 else ' '} ${strmap.collisions[i]} +% endfor + */ + +#define none 0xffff +static const uint16_t string_map[${strmap.hash_size}] = { +% for e in strmap.mapping: + ${ '{:0=#6x}'.format(e) if e >= 0 else 'none' }, % endfor }; @@ -124,25 +153,28 @@ static const struct radv_entrypoint entrypoints[] = { * either pick the correct entry point. */ -% for layer in ['radv']: - % for type_, name, args, _, _, guard in entrypoints: - % if guard is not None: -#ifdef ${guard} +% for layer in LAYERS: + % for e in entrypoints: + % if e.alias: + <% continue %> + % endif + % if e.guard is not None: +#ifdef ${e.guard} % endif - ${type_} ${layer}_${name}(${args}) __attribute__ ((weak)); - % if guard is not None: -#endif // ${guard} + ${e.return_type} ${e.prefixed_name(layer)}(${e.decl_params()}) __attribute__ ((weak)); + % if e.guard is not None: +#endif // ${e.guard} % endif % endfor const struct radv_dispatch_table ${layer}_layer = { - % for _, name, args, _, _, guard in entrypoints: - % if guard is not None: -#ifdef ${guard} + % for e in entrypoints: + % if e.guard is not None: +#ifdef ${e.guard} % endif - .${name} = ${layer}_${name}, - % if guard is not None: -#endif // ${guard} + .${e.name} = ${e.prefixed_name(layer)}, + % if e.guard is not None: +#endif // ${e.guard} % endif % endfor }; @@ -154,111 +186,254 @@ radv_resolve_entrypoint(uint32_t index) return radv_layer.entrypoints[index]; } -/* Hash table stats: - * size ${hash_size} entries - * collisions entries: -% for i in xrange(10): - * ${i}${'+' if i == 9 else ''} ${collisions[i]} -% endfor +/** Return true if the core version or extension in which the given entrypoint + * is defined is enabled. + * + * If instance is NULL, we only allow the 3 commands explicitly allowed by the vk + * spec. + * + * If device is NULL, all device extensions are considered enabled. */ - -#define none ${'{:#x}'.format(none)} -static const uint16_t map[] = { -% for i in xrange(0, hash_size, 8): - % for j in xrange(i, i + 8): - ## This is 6 because the 0x is counted in the length - % if mapping[j] & 0xffff == 0xffff: - none, - % else: - ${'{:0=#6x}'.format(mapping[j] & 0xffff)}, - % endif - % endfor +static bool +radv_entrypoint_is_enabled(int index, uint32_t core_version, + const struct radv_instance_extension_table *instance, + const struct radv_device_extension_table *device) +{ + switch (index) { +% for e in entrypoints: + case ${e.num}: + % if not e.device_command: + if (device) return false; + % endif + % if e.name == 'vkCreateInstance' or e.name == 'vkEnumerateInstanceExtensionProperties' or e.name == 'vkEnumerateInstanceLayerProperties': + return !device; + % elif e.core_version: + return instance && ${e.core_version.c_vk_version()} <= core_version; + % elif e.extensions: + % for ext in e.extensions: + % if ext.type == 'instance': + if (instance && instance->${ext.name[3:]}) return true; + % else: + if (instance && (!device || device->${ext.name[3:]})) return true; + % endif + %endfor + return false; + % else: + return instance; + % endif % endfor -}; + default: + return false; + } +} -void * +static int radv_lookup_entrypoint(const char *name) { - static const uint32_t prime_factor = ${prime_factor}; - static const uint32_t prime_step = ${prime_step}; - const struct radv_entrypoint *e; - uint32_t hash, h, i; + static const uint32_t prime_factor = ${strmap.prime_factor}; + static const uint32_t prime_step = ${strmap.prime_step}; + const struct string_map_entry *e; + uint32_t hash, h; + uint16_t i; const char *p; hash = 0; for (p = name; *p; p++) - hash = hash * prime_factor + *p; + hash = hash * prime_factor + *p; h = hash; - do { - i = map[h & ${hash_mask}]; - if (i == none) - return NULL; - e = &entrypoints[i]; - h += prime_step; - } while (e->hash != hash); - - if (strcmp(name, strings + e->name) != 0) + while (1) { + i = string_map[h & ${strmap.hash_mask}]; + if (i == none) + return -1; + e = &string_map_entries[i]; + if (e->hash == hash && strcmp(name, strings + e->name) == 0) + return e->num; + h += prime_step; + } + + return -1; +} + +void * +radv_lookup_entrypoint_unchecked(const char *name) +{ + int index = radv_lookup_entrypoint(name); + if (index < 0) return NULL; + return radv_resolve_entrypoint(index); +} - return radv_resolve_entrypoint(i); +void * +radv_lookup_entrypoint_checked(const char *name, + uint32_t core_version, + const struct radv_instance_extension_table *instance, + const struct radv_device_extension_table *device) +{ + int index = radv_lookup_entrypoint(name); + if (index < 0 || !radv_entrypoint_is_enabled(index, core_version, instance, device)) + return NULL; + return radv_resolve_entrypoint(index); }""", output_encoding='utf-8') -NONE = 0xffff -HASH_SIZE = 256 U32_MASK = 2**32 - 1 -HASH_MASK = HASH_SIZE - 1 PRIME_FACTOR = 5024183 PRIME_STEP = 19 - -def cal_hash(name): - """Calculate the same hash value that Mesa will calculate in C.""" - return functools.reduce( - lambda h, c: (h * PRIME_FACTOR + ord(c)) & U32_MASK, name, 0) - +def round_to_pow2(x): + return 2**int(math.ceil(math.log(x, 2))) + +class StringIntMapEntry(object): + def __init__(self, string, num): + self.string = string + self.num = num + + # Calculate the same hash value that we will calculate in C. + h = 0 + for c in string: + h = ((h * PRIME_FACTOR) + ord(c)) & U32_MASK + self.hash = h + + self.offset = None + +class StringIntMap(object): + def __init__(self): + self.baked = False + self.strings = dict() + + def add_string(self, string, num): + assert not self.baked + assert string not in self.strings + assert num >= 0 and num < 2**31 + self.strings[string] = StringIntMapEntry(string, num) + + def bake(self): + self.sorted_strings = \ + sorted(self.strings.values(), key=lambda x: x.string) + offset = 0 + for entry in self.sorted_strings: + entry.offset = offset + offset += len(entry.string) + 1 + + # Save off some values that we'll need in C + self.hash_size = round_to_pow2(len(self.strings) * 1.25) + self.hash_mask = self.hash_size - 1 + self.prime_factor = PRIME_FACTOR + self.prime_step = PRIME_STEP + + self.mapping = [-1] * self.hash_size + self.collisions = [0] * 10 + for idx, s in enumerate(self.sorted_strings): + level = 0 + h = s.hash + while self.mapping[h & self.hash_mask] >= 0: + h = h + PRIME_STEP + level = level + 1 + self.collisions[min(level, 9)] += 1 + self.mapping[h & self.hash_mask] = idx + +EntrypointParam = namedtuple('EntrypointParam', 'type name decl') + +class EntrypointBase(object): + def __init__(self, name): + self.name = name + self.alias = None + self.guard = None + self.enabled = False + self.num = None + # Extensions which require this entrypoint + self.core_version = None + self.extensions = [] + +class Entrypoint(EntrypointBase): + def __init__(self, name, return_type, params, guard = None): + super(Entrypoint, self).__init__(name) + self.return_type = return_type + self.params = params + self.guard = guard + self.device_command = len(params) > 0 and (params[0].type == 'VkDevice' or params[0].type == 'VkQueue' or params[0].type == 'VkCommandBuffer') + + def prefixed_name(self, prefix): + assert self.name.startswith('vk') + return prefix + '_' + self.name[2:] + + def decl_params(self): + return ', '.join(p.decl for p in self.params) + + def call_params(self): + return ', '.join(p.name for p in self.params) + +class EntrypointAlias(EntrypointBase): + def __init__(self, name, entrypoint): + super(EntrypointAlias, self).__init__(name) + self.alias = entrypoint + self.device_command = entrypoint.device_command + + def prefixed_name(self, prefix): + return self.alias.prefixed_name(prefix) def get_entrypoints(doc, entrypoints_to_defines, start_index): """Extract the entry points from the registry.""" - entrypoints = [] + entrypoints = OrderedDict() + + for command in doc.findall('./commands/command'): + if 'alias' in command.attrib: + alias = command.attrib['name'] + target = command.attrib['alias'] + entrypoints[alias] = EntrypointAlias(alias, entrypoints[target]) + else: + name = command.find('./proto/name').text + ret_type = command.find('./proto/type').text + params = [EntrypointParam( + type = p.find('./type').text, + name = p.find('./name').text, + decl = ''.join(p.itertext()) + ) for p in command.findall('./param')] + guard = entrypoints_to_defines.get(name) + # They really need to be unique + assert name not in entrypoints + entrypoints[name] = Entrypoint(name, ret_type, params, guard) - enabled_commands = set() for feature in doc.findall('./feature'): assert feature.attrib['api'] == 'vulkan' - if VkVersion(feature.attrib['number']) > MAX_API_VERSION: + version = VkVersion(feature.attrib['number']) + if version > MAX_API_VERSION: continue for command in feature.findall('./require/command'): - enabled_commands.add(command.attrib['name']) + e = entrypoints[command.attrib['name']] + e.enabled = True + assert e.core_version is None + e.core_version = version - supported = set(ext.name for ext in EXTENSIONS) + supported_exts = dict((ext.name, ext) for ext in EXTENSIONS) for extension in doc.findall('.extensions/extension'): - if extension.attrib['name'] not in supported: + ext_name = extension.attrib['name'] + if ext_name not in supported_exts: continue if extension.attrib['supported'] != 'vulkan': continue - for command in extension.findall('./require/command'): - enabled_commands.add(command.attrib['name']) - - index = start_index - for command in doc.findall('./commands/command'): - type = command.find('./proto/type').text - fullname = command.find('./proto/name').text + ext = supported_exts[ext_name] + ext.type = extension.attrib['type'] - if fullname not in enabled_commands: - continue + for command in extension.findall('./require/command'): + e = entrypoints[command.attrib['name']] + e.enabled = True + assert e.core_version is None + e.extensions.append(ext) - shortname = fullname[2:] - params = (''.join(p.itertext()) for p in command.findall('./param')) - params = ', '.join(params) - guard = entrypoints_to_defines.get(fullname) - entrypoints.append((type, shortname, params, index, cal_hash(fullname), guard)) - index += 1 + # if the base command is not supported by the driver yet, don't alias aliases + for e in entrypoints.values(): + if e.alias and not e.alias.enabled: + e_clone = copy.deepcopy(e.alias) + e_clone.enabled = True + e_clone.name = e.name + entrypoints[e.name] = e_clone - return entrypoints + return [e for e in entrypoints.itervalues() if e.enabled] def get_entrypoints_defines(doc): @@ -272,39 +447,27 @@ def get_entrypoints_defines(doc): fullname = entrypoint.attrib['name'] entrypoints_to_defines[fullname] = define + for extension in doc.findall('./extensions/extension[@platform]'): + platform = extension.attrib['platform'] + define = 'VK_USE_PLATFORM_' + platform.upper() + '_KHR' + + for entrypoint in extension.findall('./require/command'): + fullname = entrypoint.attrib['name'] + entrypoints_to_defines[fullname] = define + return entrypoints_to_defines def gen_code(entrypoints): """Generate the C code.""" - i = 0 - offsets = [] - for _, name, _, _, _, _ in entrypoints: - offsets.append(i) - i += 2 + len(name) + 1 - - mapping = [NONE] * HASH_SIZE - collisions = [0] * 10 - for _, name, _, num, h, _ in entrypoints: - level = 0 - while mapping[h & HASH_MASK] != NONE: - h = h + PRIME_STEP - level = level + 1 - if level > 9: - collisions[9] += 1 - else: - collisions[level] += 1 - mapping[h & HASH_MASK] = num + strmap = StringIntMap() + for e in entrypoints: + strmap.add_string(e.name, e.num) + strmap.bake() return TEMPLATE_C.render(entrypoints=entrypoints, - offsets=offsets, - collisions=collisions, - mapping=mapping, - hash_mask=HASH_MASK, - prime_step=PRIME_STEP, - prime_factor=PRIME_FACTOR, - none=NONE, - hash_size=HASH_SIZE, + LAYERS=LAYERS, + strmap=strmap, filename=os.path.basename(__file__)) @@ -326,10 +489,14 @@ def main(): entrypoints += get_entrypoints(doc, get_entrypoints_defines(doc), start_index=len(entrypoints)) + for num, e in enumerate(entrypoints): + e.num = num + # For outputting entrypoints.h we generate a radv_EntryPoint() prototype # per entry point. with open(os.path.join(args.outdir, 'radv_entrypoints.h'), 'wb') as f: f.write(TEMPLATE_H.render(entrypoints=entrypoints, + LAYERS=LAYERS, filename=os.path.basename(__file__))) with open(os.path.join(args.outdir, 'radv_entrypoints.c'), 'wb') as f: f.write(gen_code(entrypoints))