From 94c9096c833c7b21275f1b85f472afbf63a5ee7c Mon Sep 17 00:00:00 2001 From: Bas Nieuwenhuizen Date: Tue, 6 Mar 2018 01:08:43 +0100 Subject: [PATCH] radv: Add entrypoints generation with the new vk.xml A lot of it is based on intel again. Reviewed-by: Dave Airlie --- src/amd/vulkan/radv_entrypoints_gen.py | 271 +++++++++++++++---------- 1 file changed, 164 insertions(+), 107 deletions(-) diff --git a/src/amd/vulkan/radv_entrypoints_gen.py b/src/amd/vulkan/radv_entrypoints_gen.py index 08ca9ccca13..8eb18e64b64 100644 --- a/src/amd/vulkan/radv_entrypoints_gen.py +++ b/src/amd/vulkan/radv_entrypoints_gen.py @@ -24,6 +24,7 @@ import argparse import functools +import math import os import xml.etree.cElementTree as et @@ -66,6 +67,9 @@ struct radv_dispatch_table { }; % for e in entrypoints: + % if e.alias: + <% continue %> + % endif % if e.guard is not None: #ifdef ${e.guard} % endif @@ -106,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 @@ -117,14 +122,29 @@ struct radv_entrypoint { */ static const char strings[] = -% for e in entrypoints: - "${e.name}\\0" +% for s in strmap.sorted_strings: + "${s.string}\\0" % endfor ; -static const struct radv_entrypoint entrypoints[] = { -% for e in entrypoints: - [${e.num}] = { ${offsets[e.num]}, ${'{:0=#8x}'.format(e.get_c_hash())} }, /* ${e.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 }; @@ -135,6 +155,9 @@ static const struct radv_entrypoint entrypoints[] = { % for layer in LAYERS: % for e in entrypoints: + % if e.alias: + <% continue %> + % endif % if e.guard is not None: #ifdef ${e.guard} % endif @@ -163,28 +186,6 @@ 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 - */ - -#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 -% endfor -}; - /** Return true if the core version or extension in which the given entrypoint * is defined is enabled. * @@ -208,12 +209,15 @@ radv_entrypoint_is_enabled(int index, uint32_t core_version, return !device; % elif e.core_version: return instance && ${e.core_version.c_vk_version()} <= core_version; - % elif e.extension: - % if e.extension.type == 'instance': - return instance && instance->${e.extension.name[3:]}; - % else: - return instance && (!device || device->${e.extension.name[3:]}); - % endif + % 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 @@ -226,29 +230,29 @@ radv_entrypoint_is_enabled(int index, uint32_t core_version, 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 -1; - e = &entrypoints[i]; - h += prime_step; - } while (e->hash != hash); - - if (strcmp(name, strings + e->name) != 0) - return -1; - - return i; + 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 * @@ -272,32 +276,82 @@ radv_lookup_entrypoint_checked(const char *name, 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 Entrypoint(object): - def __init__(self, name, return_type, params, guard = None): +class EntrypointBase(object): + def __init__(self, name): self.name = name - self.return_type = return_type - self.params = params - self.guard = guard + self.alias = None + self.guard = None self.enabled = False self.num = None # Extensions which require this entrypoint self.core_version = None - self.extension = 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): @@ -310,27 +364,37 @@ class Entrypoint(object): def call_params(self): return ', '.join(p.name for p in self.params) - def get_c_hash(self): - return cal_hash(self.name) +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 = OrderedDict() for command in doc.findall('./commands/command'): - ret_type = command.find('./proto/type').text - fullname = command.find('./proto/name').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(fullname) - # They really need to be unique - assert fullname not in entrypoints - entrypoints[fullname] = Entrypoint(fullname, ret_type, params, guard) - - enabled_commands = set() + 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) + for feature in doc.findall('./feature'): assert feature.attrib['api'] == 'vulkan' version = VkVersion(feature.attrib['number']) @@ -359,8 +423,15 @@ def get_entrypoints(doc, entrypoints_to_defines, start_index): e = entrypoints[command.attrib['name']] e.enabled = True assert e.core_version is None - assert e.extension is None - e.extension = ext + e.extensions.append(ext) + + # 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 [e for e in entrypoints.itervalues() if e.enabled] @@ -376,41 +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 e in entrypoints: - offsets.append(i) - i += len(e.name) + 1 - - mapping = [NONE] * HASH_SIZE - collisions = [0] * 10 + strmap = StringIntMap() for e in entrypoints: - level = 0 - h = e.get_c_hash() - 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] = e.num + strmap.add_string(e.name, e.num) + strmap.bake() return TEMPLATE_C.render(entrypoints=entrypoints, LAYERS=LAYERS, - offsets=offsets, - collisions=collisions, - mapping=mapping, - hash_mask=HASH_MASK, - prime_step=PRIME_STEP, - prime_factor=PRIME_FACTOR, - none=NONE, - hash_size=HASH_SIZE, + strmap=strmap, filename=os.path.basename(__file__)) -- 2.30.2