nvk: move to new command stream generator.
authorDave Airlie <airlied@redhat.com>
Thu, 9 Jun 2022 20:51:16 +0000 (06:51 +1000)
committerMarge Bot <emma+marge@anholt.net>
Fri, 4 Aug 2023 21:31:53 +0000 (21:31 +0000)
This auto generates macros/inlines/structs from the nvidia
class headers using a python for noobs script

v2: use argparse.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/24326>

src/nouveau/nvidia-headers/class_parser.py [new file with mode: 0644]
src/nouveau/nvidia-headers/meson.build
src/nouveau/vulkan/meson.build
src/nouveau/vulkan/nvk_private.h
src/nouveau/winsys/nouveau_push.h

diff --git a/src/nouveau/nvidia-headers/class_parser.py b/src/nouveau/nvidia-headers/class_parser.py
new file mode 100644 (file)
index 0000000..fa6636d
--- /dev/null
@@ -0,0 +1,151 @@
+#! /usr/bin/env python3
+
+# script to parse nvidia CL headers and generate inlines to be used in pushbuffer encoding.
+# probably needs python3.9
+
+import argparse
+import sys
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--out_h', required=True, help='Output C header.')
+parser.add_argument('--in_h',
+                    help='Input class header file.',
+                    required=True)
+args = parser.parse_args()
+
+filein = args.in_h
+fileout = args.out_h
+
+if (filein.strip == ""):
+    print("class_parser.py class.h output.h")
+    sys.exit()
+
+nvcl = filein.split("/")[-1]
+clheader = nvcl
+nvcl = nvcl.removeprefix("cl")
+nvcl = nvcl.removesuffix(".h")
+nvcl = nvcl.upper()
+
+nvcl = "NV" + nvcl
+
+f = open(filein)
+
+fout = open(fileout, 'w')
+
+class method(object):
+    pass
+
+# Simple state machine
+# state 0 looking for a new method define
+# state 1 looking for new fields in a method
+# state 2 looking for enums for a fields in a method
+# blank lines reset the state machine to 0
+
+state = 0
+mthddict = {}
+for line in f:
+
+    if line.strip() == "":
+        state = 0
+        curmthd = {}
+        continue
+
+    if line.startswith("#define"):
+        list = line.split();
+        if "_cl_" in list[1]:
+            continue
+
+        if not list[1].startswith(nvcl):
+            continue
+
+        if state == 2:
+            teststr = nvcl + "_" + curmthd.name + "_" + curfield + "_"
+            if ":" in list[2]:
+                state = 1
+            elif teststr in list[1]:
+                curmthd.field_defs[curfield][list[1].removeprefix(teststr)] = list[2]
+            else:
+                state = 1
+
+        if state == 1:
+            teststr = nvcl + "_" + curmthd.name + "_"
+            if teststr in list[1]:
+                if ("0x" in list[2]):
+                    state = 1
+                else:
+                    field = list[1].removeprefix(teststr)
+                    bitfield = list[2].split(":")
+                    curmthd.field_name_start[field] = bitfield[1]
+                    curmthd.field_name_end[field] = bitfield[0]
+                    curmthd.field_defs[field] = {}
+                    curfield = field
+                    state = 2
+            else:
+                state = 0
+
+        if state == 0:
+            teststr = nvcl + "_"
+            is_array = 0
+            name = list[1].removeprefix(teststr)
+            if name.endswith("(i)"):
+                is_array = 1
+                name = name.removesuffix("(i)")
+            if name.endswith("(j)"):
+                is_array = 1
+                name = name.removesuffix("(j)")
+            x = method()
+            x.name = name
+            x.addr = list[2]
+            x.is_array = is_array
+            x.field_name_start = {}
+            x.field_name_end = {}
+            x.field_defs = {}
+            mthddict[x.name] = x
+
+            curmthd = x
+            state = 1
+
+sys.stdout = fout
+print("/* parsed class " + nvcl + " */")
+
+print("#include \"" + clheader + "\"")
+for mthd in mthddict:
+    structname = "nv_" + nvcl.lower() + "_" + mthd
+    print("struct " + structname + " {")
+    for field_name in mthddict[mthd].field_name_start:
+        print("\tuint32_t " + field_name.lower() + ";")
+    print("};")
+    print("static inline void __" + nvcl.strip() + "_" + mthd + "(uint32_t *val_out, struct " + structname + " st" + ") {")
+    print("\tuint32_t val = 0;")
+    for field_name in mthddict[mthd].field_name_start:
+        field_width = int(mthddict[mthd].field_name_end[field_name]) - int(mthddict[mthd].field_name_start[field_name]) + 1
+        if (field_width == 32):
+            print("\tval |= st." + field_name.lower() + ";")
+        else:
+            print("\tassert(st." + field_name.lower() + " < (1ULL << " + str(field_width) + "));")
+            print("\tval |= (st." + field_name.lower() + " & ((1ULL << " + str(field_width) + ") - 1)) << " + mthddict[mthd].field_name_start[field_name] + ";");
+    print("\t*val_out = val;");
+    print("}")
+    print("#define V_" + nvcl + "_" + mthd + "(val, args...) { \\")
+    for field_name in mthddict[mthd].field_name_start:
+        if len(mthddict[mthd].field_defs[field_name]):
+            for d in mthddict[mthd].field_defs[field_name]:
+                print("UNUSED uint32_t " + field_name + "_" + d + " = " + nvcl + "_" + mthd + "_" + field_name + "_" + d+"; \\")
+    if len(mthddict[mthd].field_name_start) > 1:
+        print("\tstruct " + structname + " __data = args; \\")
+    else:
+        print("\tstruct " + structname + " __data = { ." + next(iter(mthddict[mthd].field_name_start)).lower() + " = (args) }; \\")
+    print("\t__" + nvcl.strip() + "_" + mthd + "(&val, __data); \\")
+    print("\t}")
+    print("#define P_" + nvcl + "_" + mthd + "(push, " + ("idx, " if mthddict[mthd].is_array else "") + "args...) do { \\")
+    for field_name in mthddict[mthd].field_name_start:
+        if len(mthddict[mthd].field_defs[field_name]):
+            for d in mthddict[mthd].field_defs[field_name]:
+                print("UNUSED uint32_t " + field_name + "_" + d + " = " + nvcl + "_" + mthd + "_" + field_name + "_" + d+"; \\")
+    print("\tuint32_t nvk_p_ret;\\")
+    print("\tV_" + nvcl + "_" + mthd + "(nvk_p_ret, args)\\");
+    print("\tnvk_push_val(push, " + nvcl + "_" + mthd + ("(idx), " if mthddict[mthd].is_array else ",") + " nvk_p_ret);\\");
+    print("\t} while(0)")
+
+fout.close()
+f.close()
index 8575043..d0febf2 100644 (file)
@@ -1,3 +1,25 @@
 idep_nvidia_headers = declare_dependency(
-  include_directories : include_directories('.')
+  include_directories : include_directories('.', 'classes')
 )
+
+nvk_classes = [
+  'cl902d',
+  'cl90b5',
+  'cla0b5',
+  'clc1b5',
+]
+
+nvk_cl_header_depend_files = [
+  files('class_parser.py')
+]
+
+cl_generated = []
+foreach cl : nvk_classes
+  cl_generated += custom_target(
+    cl + '.h',
+    input : ['class_parser.py', 'classes/'+cl+'.h'],
+    output : 'nvk_'+cl+'.h',
+    command : [prog_python, '@INPUT0@', '--in_h', '@INPUT1@', '--out_h', '@OUTPUT0@'],
+    depend_files: nvk_cl_header_depend_files,
+  )
+endforeach
index 9c26a11..7b0d6aa 100644 (file)
@@ -79,6 +79,7 @@ libvulkan_nouveau = shared_library(
   'vulkan_nouveau',
   [
     nvk_entrypoints,
+    cl_generated,
     nvk_files,
   ],
   include_directories : [
index 6224192..ff005d2 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef NVK_PRIVATE_H
 #define NVK_PRIVATE_H 1
 
+#include <assert.h>
+
 #include "nvk_entrypoints.h"
 
 #include "util/log.h"
index dbf9f82..c06b084 100644 (file)
@@ -21,6 +21,8 @@ struct nouveau_ws_push {
    uint32_t *orig_map;
    uint32_t *map;
    uint32_t *end;
+
+   uint32_t *last_size;
 };
 
 struct nouveau_ws_push *nouveau_ws_push_new(struct nouveau_ws_device *, uint64_t size);
@@ -325,4 +327,141 @@ void nouveau_ws_push_reset(struct nouveau_ws_push *);
 #define PUSH_1INC(A...) PUSH_NV(NV1I, ##A)
 #define PUSH_NINC(A...) PUSH_NV(NVNI, ##A)
 
+#define SUBC_NV902D 3
+
+#define SUBC_NV90B5 4
+
+static inline uint32_t
+NVC0_FIFO_PKHDR_SQ(int subc, int mthd, unsigned size)
+{
+   return 0x20000000 | (size << 16) | (subc << 13) | (mthd >> 2);
+}
+
+static inline void
+__push_mthd(struct nouveau_ws_push *push, int subc, uint32_t mthd)
+{
+   push->last_size = push->map;
+   *push->map = NVC0_FIFO_PKHDR_SQ(subc, mthd, 0);
+   push->map++;
+}
+
+#define P_MTHD(push, class, mthd) __push_mthd(push, SUBC_##class, class##_##mthd)
+
+static inline uint32_t
+NVC0_FIFO_PKHDR_IL(int subc, int mthd, uint16_t data)
+{
+   assert(data < 0x2000);
+   return 0x80000000 | (data << 16) | (subc << 13) | (mthd >> 2);
+}
+
+static inline void
+__push_immd(struct nouveau_ws_push *push, int subc, uint32_t mthd, uint32_t val)
+{
+   push->last_size = push->map;
+   *push->map = NVC0_FIFO_PKHDR_IL(subc, mthd, val);
+   push->map++;
+}
+
+#define P_IMMD(push, class, mthd, args...) do {\
+   uint32_t __val; \
+   V_##class##_##mthd(__val, args);         \
+   __push_immd(push, SUBC_##class, class##_##mthd, __val); \
+   } while(0)
+
+static inline uint32_t
+NVC0_FIFO_PKHDR_1I(int subc, int mthd, unsigned size)
+{
+   return 0xa0000000 | (size << 16) | (subc << 13) | (mthd >> 2);
+}
+
+static inline void
+__push_1inc(struct nouveau_ws_push *push, int subc, uint32_t mthd)
+{
+   push->last_size = push->map;
+   *push->map = NVC0_FIFO_PKHDR_1I(subc, mthd, 0);
+   push->map++;
+}
+
+#define P_1INC(push, class, mthd) __push_1inc(push, SUBC_##class, class##_##mthd)
+
+static inline uint32_t
+NVC0_FIFO_PKHDR_0I(int subc, int mthd, unsigned size)
+{
+   return 0x60000000 | (size << 16) | (subc << 13) | (mthd >> 2);
+}
+
+static inline void
+__push_0inc(struct nouveau_ws_push *push, int subc, uint32_t mthd)
+{
+   push->last_size = push->map;
+   *push->map = NVC0_FIFO_PKHDR_0I(subc, mthd, 0);
+   push->map++;
+}
+
+#define P_0INC(push, class, mthd) __push_0inc(push, SUBC_##class, class##_##mthd)
+
+static inline bool
+nvk_push_update_count(struct nouveau_ws_push *push, uint16_t count)
+{
+   uint32_t last_hdr_val = *push->last_size;
+
+   assert(count <= 0x1fff);
+   if (count > 0x1fff)
+      return false;
+
+   /* size is encoded at 28:16 */
+   uint32_t new_count = (count + (last_hdr_val >> 16)) & 0x1fff;
+   bool overflow = new_count < count;
+   /* if we would overflow, don't change anything and just let it be */
+   assert(!overflow);
+   if (overflow)
+      return false;
+
+   last_hdr_val &= ~0x1fff0000;
+   last_hdr_val |= new_count << 16;
+   *push->last_size = last_hdr_val;
+   return true;
+}
+
+static inline void
+P_INLINE_DATA(struct nouveau_ws_push *push, uint32_t value)
+{
+   if (nvk_push_update_count(push, 1)) {
+      /* push new value */
+      *push->map = value;
+      push->map++;
+   }
+}
+
+static inline void
+P_INLINE_ARRAY(struct nouveau_ws_push *push, const uint32_t *data, int num_dw)
+{
+   if (nvk_push_update_count(push, num_dw)) {
+      /* push new value */
+      memcpy(push->map, data, num_dw * 4);
+      push->map += num_dw;
+   }
+}
+
+/* internally used by generated inlines. */
+static inline void
+nvk_push_val(struct nouveau_ws_push *push, uint32_t idx, uint32_t val)
+{
+   UNUSED uint32_t last_hdr_val = *push->last_size;
+   UNUSED bool is_1inc = (last_hdr_val & 0xe0000000) == 0xa0000000;
+   UNUSED bool is_immd = (last_hdr_val & 0xe0000000) == 0x80000000;
+   UNUSED uint16_t last_method = (last_hdr_val & 0x1fff) << 2;
+
+   uint16_t distance = push->map - push->last_size - 1;
+   if (is_1inc)
+      distance = MIN2(1, distance);
+   last_method += distance * 4;
+
+   /* can't have empty headers ever */
+   assert(last_hdr_val);
+   assert(!is_immd);
+   assert(last_method == idx);
+
+   P_INLINE_DATA(push, val);
+}
 #endif