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