radv: Add entrypoints generation with the new vk.xml
authorBas Nieuwenhuizen <bas@basnieuwenhuizen.nl>
Tue, 6 Mar 2018 00:08:43 +0000 (01:08 +0100)
committerBas Nieuwenhuizen <bas@basnieuwenhuizen.nl>
Wed, 7 Mar 2018 14:50:19 +0000 (15:50 +0100)
A lot of it is based on intel again.

Reviewed-by: Dave Airlie <airlied@redhat.com>
src/amd/vulkan/radv_entrypoints_gen.py

index 08ca9ccca13f81e7d52223644fd9deb68070c628..8eb18e64b640184af5ac4ab41894f9b8a5cf0aa9 100644 (file)
@@ -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__))