• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#! /usr/bin/env python3
2
3# script to parse nvidia CL headers and generate inlines to be used in pushbuffer encoding.
4# probably needs python3.9
5
6import argparse
7import os.path
8import sys
9
10from mako.template import Template
11
12METHOD_ARRAY_SIZES = {
13    'BIND_GROUP_CONSTANT_BUFFER'                        : 16,
14    'CALL_MME_DATA'                                     : 256,
15    'CALL_MME_MACRO'                                    : 256,
16    'LOAD_CONSTANT_BUFFER'                              : 16,
17    'LOAD_INLINE_QMD_DATA'                              : 64,
18    'SET_ANTI_ALIAS_SAMPLE_POSITIONS'                   : 4,
19    'SET_BLEND'                                         : 8,
20    'SET_BLEND_PER_TARGET_*'                            : 8,
21    'SET_COLOR_TARGET_*'                                : 8,
22    'SET_COLOR_COMPRESSION'                             : 8,
23    'SET_COLOR_CLEAR_VALUE'                             : 4,
24    'SET_CT_WRITE'                                      : 8,
25    'SET_MME_SHADOW_SCRATCH'                            : 256,
26    'SET_PIPELINE_*'                                    : 6,
27    'SET_SCG_COMPUTE_SCHEDULING_PARAMETERS'             : 16,
28    'SET_SCISSOR_*'                                     : 16,
29    'SET_SHADER_PERFORMANCE_SNAPSHOT_COUNTER_VALUE*'    : 8,
30    'SET_SHADER_PERFORMANCE_COUNTER_VALUE*'             : 8,
31    'SET_SHADER_PERFORMANCE_COUNTER_EVENT'              : 8,
32    'SET_SHADER_PERFORMANCE_COUNTER_CONTROL_A'          : 8,
33    'SET_SHADER_PERFORMANCE_COUNTER_CONTROL_B'          : 8,
34    'SET_STREAM_OUT_BUFFER_*'                           : 4,
35    'SET_STREAM_OUT_CONTROL_*'                          : 4,
36    'SET_VIEWPORT_*'                                    : 16,
37    'SET_VERTEX_ATTRIBUTE_*'                            : 16,
38    'SET_VERTEX_STREAM_*'                               : 16,
39}
40
41METHOD_IS_FLOAT = [
42    'SET_BLEND_CONST_*',
43    'SET_DEPTH_BIAS',
44    'SET_SLOPE_SCALE_DEPTH_BIAS',
45    'SET_DEPTH_BIAS_CLAMP',
46    'SET_DEPTH_BOUNDS_M*',
47    'SET_LINE_WIDTH_FLOAT',
48    'SET_ALIASED_LINE_WIDTH_FLOAT',
49    'SET_VIEWPORT_SCALE_*',
50    'SET_VIEWPORT_OFFSET_*',
51    'SET_VIEWPORT_CLIP_MIN_Z',
52    'SET_VIEWPORT_CLIP_MAX_Z',
53    'SET_Z_CLEAR_VALUE',
54]
55
56TEMPLATE_H = Template("""\
57/* parsed class ${nvcl} */
58
59#include "nvtypes.h"
60#include "${clheader}"
61
62#include <assert.h>
63#include <stdio.h>
64#include "util/u_math.h"
65
66%for mthd in mthddict:
67struct nv_${nvcl.lower()}_${mthd} {
68  %for field_name in mthddict[mthd].field_name_start:
69    uint32_t ${field_name.lower()};
70  %endfor
71};
72
73static inline void
74__${nvcl}_${mthd}(uint32_t *val_out, struct nv_${nvcl.lower()}_${mthd} st)
75{
76    uint32_t val = 0;
77  %for field_name in mthddict[mthd].field_name_start:
78    <%
79        field_start = int(mthddict[mthd].field_name_start[field_name])
80        field_end = int(mthddict[mthd].field_name_end[field_name])
81        field_width = field_end - field_start + 1
82    %>
83    %if field_width == 32:
84    val |= st.${field_name.lower()};
85    %else:
86    assert(st.${field_name.lower()} < (1ULL << ${field_width}));
87    val |= st.${field_name.lower()} << ${field_start};
88    %endif
89  %endfor
90    *val_out = val;
91}
92
93#define V_${nvcl}_${mthd}(val, args...) { ${bs}
94  %for field_name in mthddict[mthd].field_name_start:
95    %for d in mthddict[mthd].field_defs[field_name]:
96    UNUSED uint32_t ${field_name}_${d} = ${nvcl}_${mthd}_${field_name}_${d}; ${bs}
97    %endfor
98  %endfor
99  %if len(mthddict[mthd].field_name_start) > 1:
100    struct nv_${nvcl.lower()}_${mthd} __data = args; ${bs}
101  %else:
102<% field_name = next(iter(mthddict[mthd].field_name_start)).lower() %>\
103    struct nv_${nvcl.lower()}_${mthd} __data = { .${field_name} = (args) }; ${bs}
104  %endif
105    __${nvcl}_${mthd}(&val, __data); ${bs}
106}
107
108%if mthddict[mthd].is_array:
109#define VA_${nvcl}_${mthd}(i) V_${nvcl}_${mthd}
110%else:
111#define VA_${nvcl}_${mthd} V_${nvcl}_${mthd}
112%endif
113
114%if mthddict[mthd].is_array:
115#define P_${nvcl}_${mthd}(push, idx, args...) do { ${bs}
116%else:
117#define P_${nvcl}_${mthd}(push, args...) do { ${bs}
118%endif
119  %for field_name in mthddict[mthd].field_name_start:
120    %for d in mthddict[mthd].field_defs[field_name]:
121    UNUSED uint32_t ${field_name}_${d} = ${nvcl}_${mthd}_${field_name}_${d}; ${bs}
122    %endfor
123  %endfor
124    uint32_t nvk_p_ret; ${bs}
125    V_${nvcl}_${mthd}(nvk_p_ret, args); ${bs}
126    %if mthddict[mthd].is_array:
127    nv_push_val(push, ${nvcl}_${mthd}(idx), nvk_p_ret); ${bs}
128    %else:
129    nv_push_val(push, ${nvcl}_${mthd}, nvk_p_ret); ${bs}
130    %endif
131} while(0)
132
133%endfor
134
135const char *P_PARSE_${nvcl}_MTHD(uint16_t idx);
136void P_DUMP_${nvcl}_MTHD_DATA(FILE *fp, uint16_t idx, uint32_t data,
137                              const char *prefix);
138""")
139
140TEMPLATE_C = Template("""\
141#include "${header}"
142
143#include <stdio.h>
144
145const char*
146P_PARSE_${nvcl}_MTHD(uint16_t idx)
147{
148    switch (idx) {
149%for mthd in mthddict:
150  %if mthddict[mthd].is_array and mthddict[mthd].array_size == 0:
151    <% continue %>
152  %endif
153  %if mthddict[mthd].is_array:
154    %for i in range(mthddict[mthd].array_size):
155    case ${nvcl}_${mthd}(${i}):
156        return "${nvcl}_${mthd}(${i})";
157    %endfor
158  % else:
159    case ${nvcl}_${mthd}:
160        return "${nvcl}_${mthd}";
161  %endif
162%endfor
163    default:
164        return "unknown method";
165    }
166}
167
168void
169P_DUMP_${nvcl}_MTHD_DATA(FILE *fp, uint16_t idx, uint32_t data,
170                         const char *prefix)
171{
172    uint32_t parsed;
173    switch (idx) {
174%for mthd in mthddict:
175  %if mthddict[mthd].is_array and mthddict[mthd].array_size == 0:
176    <% continue %>
177  %endif
178  %if mthddict[mthd].is_array:
179    %for i in range(mthddict[mthd].array_size):
180    case ${nvcl}_${mthd}(${i}):
181    %endfor
182  % else:
183    case ${nvcl}_${mthd}:
184  %endif
185  %for field_name in mthddict[mthd].field_name_start:
186    <%
187        field_start = int(mthddict[mthd].field_name_start[field_name])
188        field_end = int(mthddict[mthd].field_name_end[field_name])
189        field_width = field_end - field_start + 1
190    %>
191    %if field_width == 32:
192        parsed = data;
193    %else:
194        parsed = (data >> ${field_start}) & ((1u << ${field_width}) - 1);
195    %endif
196        fprintf(fp, "%s.${field_name} = ", prefix);
197    %if len(mthddict[mthd].field_defs[field_name]):
198        switch (parsed) {
199      %for d in mthddict[mthd].field_defs[field_name]:
200        case ${nvcl}_${mthd}_${field_name}_${d}:
201            fprintf(fp, "${d}${bs}n");
202            break;
203      %endfor
204        default:
205            fprintf(fp, "0x%x${bs}n", parsed);
206            break;
207        }
208    %else:
209      %if mthddict[mthd].is_float:
210        fprintf(fp, "%ff (0x%x)${bs}n", uif(parsed), parsed);
211      %else:
212        fprintf(fp, "(0x%x)${bs}n", parsed);
213      %endif
214    %endif
215  %endfor
216        break;
217%endfor
218    default:
219        fprintf(fp, "%s.VALUE = 0x%x${bs}n", prefix, data);
220        break;
221    }
222}
223""")
224
225def glob_match(glob, name):
226    if glob.endswith('*'):
227        return name.startswith(glob[:-1])
228    else:
229        assert '*' not in glob
230        return name == glob
231
232class method(object):
233    @property
234    def array_size(self):
235        for (glob, value) in METHOD_ARRAY_SIZES.items():
236            if glob_match(glob, self.name):
237                return value
238        return 0
239
240    @property
241    def is_float(self):
242        for glob in METHOD_IS_FLOAT:
243            if glob_match(glob, self.name):
244                assert len(self.field_defs) == 1
245                return True
246        return False
247
248def parse_header(nvcl, f):
249    # Simple state machine
250    # state 0 looking for a new method define
251    # state 1 looking for new fields in a method
252    # state 2 looking for enums for a fields in a method
253    # blank lines reset the state machine to 0
254
255    state = 0
256    mthddict = {}
257    curmthd = {}
258    for line in f:
259
260        if line.strip() == "":
261            state = 0
262            if (curmthd):
263                if not len(curmthd.field_name_start):
264                    del mthddict[curmthd.name]
265            curmthd = {}
266            continue
267
268        if line.startswith("#define"):
269            list = line.split();
270            if "_cl_" in list[1]:
271                continue
272
273            if not list[1].startswith(nvcl):
274                continue
275
276            if list[1].endswith("TYPEDEF"):
277                continue
278
279            if state == 2:
280                teststr = nvcl + "_" + curmthd.name + "_" + curfield + "_"
281                if ":" in list[2]:
282                    state = 1
283                elif teststr in list[1]:
284                    curmthd.field_defs[curfield][list[1].removeprefix(teststr)] = list[2]
285                else:
286                    state = 1
287
288            if state == 1:
289                teststr = nvcl + "_" + curmthd.name + "_"
290                if teststr in list[1]:
291                    if ("0x" in list[2]):
292                        state = 1
293                    else:
294                        field = list[1].removeprefix(teststr)
295                        bitfield = list[2].split(":")
296                        curmthd.field_name_start[field] = bitfield[1]
297                        curmthd.field_name_end[field] = bitfield[0]
298                        curmthd.field_defs[field] = {}
299                        curfield = field
300                        state = 2
301                else:
302                    if not len(curmthd.field_name_start):
303                        del mthddict[curmthd.name]
304                        curmthd = {}
305                    state = 0
306
307            if state == 0:
308                if (curmthd):
309                    if not len(curmthd.field_name_start):
310                        del mthddict[curmthd.name]
311                teststr = nvcl + "_"
312                is_array = 0
313                if (':' in list[2]):
314                    continue
315                name = list[1].removeprefix(teststr)
316                if name.endswith("(i)"):
317                    is_array = 1
318                    name = name.removesuffix("(i)")
319                if name.endswith("(j)"):
320                    is_array = 1
321                    name = name.removesuffix("(j)")
322                x = method()
323                x.name = name
324                x.addr = list[2]
325                x.is_array = is_array
326                x.field_name_start = {}
327                x.field_name_end = {}
328                x.field_defs = {}
329                mthddict[x.name] = x
330
331                curmthd = x
332                state = 1
333
334    return mthddict
335
336def main():
337    parser = argparse.ArgumentParser()
338    parser.add_argument('--out_h', required=True, help='Output C header.')
339    parser.add_argument('--out_c', required=True, help='Output C file.')
340    parser.add_argument('--in_h',
341                        help='Input class header file.',
342                        required=True)
343    args = parser.parse_args()
344
345    clheader = os.path.basename(args.in_h)
346    nvcl = clheader
347    nvcl = nvcl.removeprefix("cl")
348    nvcl = nvcl.removesuffix(".h")
349    nvcl = nvcl.upper()
350    nvcl = "NV" + nvcl
351
352    with open(args.in_h, 'r', encoding='utf-8') as f:
353        mthddict = parse_header(nvcl, f)
354
355    environment = {
356        'clheader': clheader,
357        'header': os.path.basename(args.out_h),
358        'nvcl': nvcl,
359        'mthddict': mthddict,
360        'bs': '\\'
361    }
362
363    try:
364        with open(args.out_h, 'w', encoding='utf-8') as f:
365            f.write(TEMPLATE_H.render(**environment))
366        with open(args.out_c, 'w', encoding='utf-8') as f:
367            f.write(TEMPLATE_C.render(**environment))
368
369    except Exception:
370        # In the event there's an error, this imports some helpers from mako
371        # to print a useful stack trace and prints it, then exits with
372        # status 1, if python is run with debug; otherwise it just raises
373        # the exception
374        import sys
375        from mako import exceptions
376        print(exceptions.text_error_template().render(), file=sys.stderr)
377        sys.exit(1)
378
379if __name__ == '__main__':
380    main()
381