radv: Reword radv_entrypoints_gen.py
authorBas Nieuwenhuizen <bas@basnieuwenhuizen.nl>
Sun, 11 Feb 2018 12:12:33 +0000 (13:12 +0100)
committerBas Nieuwenhuizen <bas@basnieuwenhuizen.nl>
Thu, 22 Feb 2018 23:39:02 +0000 (00:39 +0100)
With a big inspiration from anv as always ...

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

index c981c0be13a10bcc4931b07f42185fdb1f6f59c4..f7a791967b96ea95ab2ef7b9c2dca70f78db0bfa 100644 (file)
@@ -27,6 +27,7 @@ import functools
 import os
 import xml.etree.cElementTree as et
 
+from collections import OrderedDict, namedtuple
 from mako.template import Template
 
 from radv_extensions import *
@@ -36,6 +37,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 +50,30 @@ 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.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')
@@ -108,14 +117,14 @@ struct radv_entrypoint {
  */
 
 static const char strings[] =
-% for _, name, _, _, _, _ in entrypoints:
-    "vk${name}\\0"
+% for e in entrypoints:
+    "${e.name}\\0"
 % endfor
 ;
 
 static const struct radv_entrypoint entrypoints[] = {
-% for _, name, _, num, h, _ in entrypoints:
-    [${num}] = { ${offsets[num]}, ${'{:0=#8x}'.format(h)} }, /* vk${name} */
+% for e in entrypoints:
+    [${e.num}] = { ${offsets[e.num]}, ${'{:0=#8x}'.format(e.get_c_hash())} }, /* ${e.name} */
 % endfor
 };
 
@@ -124,25 +133,25 @@ 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.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
   };
@@ -212,53 +221,88 @@ 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)
 
+EntrypointParam = namedtuple('EntrypointParam', 'type name decl')
+
+class Entrypoint(object):
+    def __init__(self, name, return_type, params, guard = None):
+        self.name = name
+        self.return_type = return_type
+        self.params = params
+        self.guard = guard
+        self.enabled = False
+        self.num = None
+        # Extensions which require this entrypoint
+        self.core_version = None
+        self.extension = None
+
+    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)
+
+    def get_c_hash(self):
+        return cal_hash(self.name)
 
 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'):
+        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()
     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
-
-        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
+        for command in extension.findall('./require/command'):
+            e = entrypoints[command.attrib['name']]
+            e.enabled = True
+            assert e.core_version is None
+            assert e.extension is None
+            e.extension = ext
 
-    return entrypoints
+    return [e for e in entrypoints.itervalues() if e.enabled]
 
 
 def get_entrypoints_defines(doc):
@@ -279,14 +323,15 @@ def gen_code(entrypoints):
     """Generate the C code."""
     i = 0
     offsets = []
-    for _, name, _, _, _, _ in entrypoints:
+    for e in entrypoints:
         offsets.append(i)
-        i += 2 + len(name) + 1
+        i += len(e.name) + 1
 
     mapping = [NONE] * HASH_SIZE
     collisions = [0] * 10
-    for _, name, _, num, h, _ in entrypoints:
+    for e in entrypoints:
         level = 0
+        h = e.get_c_hash()
         while mapping[h & HASH_MASK] != NONE:
             h = h + PRIME_STEP
             level = level + 1
@@ -294,9 +339,10 @@ def gen_code(entrypoints):
             collisions[9] += 1
         else:
             collisions[level] += 1
-        mapping[h & HASH_MASK] = num
+        mapping[h & HASH_MASK] = e.num
 
     return TEMPLATE_C.render(entrypoints=entrypoints,
+                             LAYERS=LAYERS,
                              offsets=offsets,
                              collisions=collisions,
                              mapping=mapping,
@@ -326,10 +372,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))