1# coding=utf-8 2COPYRIGHT=u""" 3/* Copyright © 2015-2021 Intel Corporation 4 * Copyright © 2021 Collabora, Ltd. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the next 14 * paragraph) shall be included in all copies or substantial portions of the 15 * Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 * IN THE SOFTWARE. 24 */ 25""" 26 27import argparse 28import os 29import re 30from collections import namedtuple 31import xml.etree.ElementTree as et 32 33from mako.template import Template 34 35# Mesa-local imports must be declared in meson variable 36# '{file_without_suffix}_depend_files'. 37from vk_dispatch_table_gen import get_entrypoints_from_xml, EntrypointParam 38 39MANUAL_COMMANDS = ['CmdPushDescriptorSetKHR', # This script doesn't know how to copy arrays in structs in arrays 40 'CmdPushDescriptorSetWithTemplateKHR', # pData's size cannot be calculated from the xml 41 'CmdDrawMultiEXT', # The size of the elements is specified in a stride param 42 'CmdDrawMultiIndexedEXT', # The size of the elements is specified in a stride param 43 'CmdBindDescriptorSets', # The VkPipelineLayout object could be released before the command is executed 44 ] 45 46TEMPLATE_H = Template(COPYRIGHT + """\ 47/* This file generated from ${filename}, don't edit directly. */ 48 49#pragma once 50 51#include "util/list.h" 52 53#define VK_PROTOTYPES 54#include <vulkan/vulkan.h> 55 56#ifdef __cplusplus 57extern "C" { 58#endif 59 60struct vk_cmd_queue { 61 VkAllocationCallbacks *alloc; 62 struct list_head cmds; 63}; 64 65enum vk_cmd_type { 66% for c in commands: 67% if c.guard is not None: 68#ifdef ${c.guard} 69% endif 70 ${to_enum_name(c.name)}, 71% if c.guard is not None: 72#endif // ${c.guard} 73% endif 74% endfor 75}; 76 77extern const char *vk_cmd_queue_type_names[]; 78 79% for c in commands: 80% if len(c.params) <= 1: # Avoid "error C2016: C requires that a struct or union have at least one member" 81<% continue %> 82% endif 83% if c.guard is not None: 84#ifdef ${c.guard} 85% endif 86struct ${to_struct_name(c.name)} { 87% for p in c.params[1:]: 88 ${to_field_decl(p.decl)}; 89% endfor 90}; 91% if c.guard is not None: 92#endif // ${c.guard} 93% endif 94% endfor 95 96struct vk_cmd_queue_entry { 97 struct list_head cmd_link; 98 enum vk_cmd_type type; 99 union { 100% for c in commands: 101% if len(c.params) <= 1: 102<% continue %> 103% endif 104% if c.guard is not None: 105#ifdef ${c.guard} 106% endif 107 struct ${to_struct_name(c.name)} ${to_struct_field_name(c.name)}; 108% if c.guard is not None: 109#endif // ${c.guard} 110% endif 111% endfor 112 } u; 113 void *driver_data; 114}; 115 116% for c in commands: 117% if c.name in manual_commands: 118<% continue %> 119% endif 120% if c.guard is not None: 121#ifdef ${c.guard} 122% endif 123 void vk_enqueue_${to_underscore(c.name)}(struct vk_cmd_queue *queue 124% for p in c.params[1:]: 125 , ${p.decl} 126% endfor 127 ); 128% if c.guard is not None: 129#endif // ${c.guard} 130% endif 131 132% endfor 133 134void vk_free_queue(struct vk_cmd_queue *queue); 135 136#ifdef __cplusplus 137} 138#endif 139""", output_encoding='utf-8') 140 141TEMPLATE_C = Template(COPYRIGHT + """ 142/* This file generated from ${filename}, don't edit directly. */ 143 144#include "${header}" 145 146#define VK_PROTOTYPES 147#include <vulkan/vulkan.h> 148 149#include "vk_alloc.h" 150 151const char *vk_cmd_queue_type_names[] = { 152% for c in commands: 153% if c.guard is not None: 154#ifdef ${c.guard} 155% endif 156 "${to_enum_name(c.name)}", 157% if c.guard is not None: 158#endif // ${c.guard} 159% endif 160% endfor 161}; 162 163% for c in commands: 164% if c.name in manual_commands: 165<% continue %> 166% endif 167% if c.guard is not None: 168#ifdef ${c.guard} 169% endif 170void vk_enqueue_${to_underscore(c.name)}(struct vk_cmd_queue *queue 171% for p in c.params[1:]: 172, ${p.decl} 173% endfor 174) 175{ 176 struct vk_cmd_queue_entry *cmd = vk_zalloc(queue->alloc, 177 sizeof(*cmd), 8, 178 VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); 179 if (!cmd) 180 return; 181 182 cmd->type = ${to_enum_name(c.name)}; 183 list_addtail(&cmd->cmd_link, &queue->cmds); 184 185% for p in c.params[1:]: 186% if p.len: 187 if (${p.name}) { 188 ${get_array_copy(c, p)} 189 } 190% elif '[' in p.decl: 191 memcpy(cmd->u.${to_struct_field_name(c.name)}.${to_field_name(p.name)}, ${p.name}, 192 sizeof(*${p.name}) * ${get_array_len(p)}); 193% elif p.type == "void": 194 cmd->u.${to_struct_field_name(c.name)}.${to_field_name(p.name)} = (${remove_suffix(p.decl.replace("const", ""), p.name)}) ${p.name}; 195% elif '*' in p.decl: 196 ${get_struct_copy("cmd->u.%s.%s" % (to_struct_field_name(c.name), to_field_name(p.name)), p.name, p.type, 'sizeof(%s)' % p.type, types)} 197% else: 198 cmd->u.${to_struct_field_name(c.name)}.${to_field_name(p.name)} = ${p.name}; 199% endif 200% endfor 201} 202% if c.guard is not None: 203#endif // ${c.guard} 204% endif 205 206% endfor 207 208void 209vk_free_queue(struct vk_cmd_queue *queue) 210{ 211 struct vk_cmd_queue_entry *tmp, *cmd; 212 LIST_FOR_EACH_ENTRY_SAFE(cmd, tmp, &queue->cmds, cmd_link) { 213 switch(cmd->type) { 214% for c in commands: 215% if c.guard is not None: 216#ifdef ${c.guard} 217% endif 218 case ${to_enum_name(c.name)}: 219% for p in c.params[1:]: 220% if p.len: 221 vk_free(queue->alloc, (${remove_suffix(p.decl.replace("const", ""), p.name)})cmd->u.${to_struct_field_name(c.name)}.${to_field_name(p.name)}); 222% elif '*' in p.decl: 223 ${get_struct_free(c, p, types)} 224% endif 225% endfor 226 break; 227% if c.guard is not None: 228#endif // ${c.guard} 229% endif 230% endfor 231 } 232 vk_free(queue->alloc, cmd); 233 } 234} 235 236""", output_encoding='utf-8') 237 238def remove_prefix(text, prefix): 239 if text.startswith(prefix): 240 return text[len(prefix):] 241 return text 242 243def remove_suffix(text, suffix): 244 if text.endswith(suffix): 245 return text[:-len(suffix)] 246 return text 247 248def to_underscore(name): 249 return remove_prefix(re.sub('([A-Z]+)', r'_\1', name).lower(), '_') 250 251def to_struct_field_name(name): 252 return to_underscore(name).replace('cmd_', '') 253 254def to_field_name(name): 255 return remove_prefix(to_underscore(name).replace('cmd_', ''), 'p_') 256 257def to_field_decl(decl): 258 decl = decl.replace('const ', '') 259 [decl, name] = decl.rsplit(' ', 1) 260 return decl + ' ' + to_field_name(name) 261 262def to_enum_name(name): 263 return "VK_%s" % to_underscore(name).upper() 264 265def to_struct_name(name): 266 return "vk_%s" % to_underscore(name) 267 268def get_array_len(param): 269 return param.decl[param.decl.find("[") + 1:param.decl.find("]")] 270 271def get_array_copy(command, param): 272 field_name = "cmd->u.%s.%s" % (to_struct_field_name(command.name), to_field_name(param.name)) 273 if param.type == "void": 274 field_size = "1" 275 else: 276 field_size = "sizeof(*%s)" % field_name 277 allocation = "%s = vk_zalloc(queue->alloc, %s * %s, 8, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);" % (field_name, field_size, param.len) 278 const_cast = remove_suffix(param.decl.replace("const", ""), param.name) 279 copy = "memcpy((%s)%s, %s, %s * %s);" % (const_cast, field_name, param.name, field_size, param.len) 280 return "%s\n %s" % (allocation, copy) 281 282def get_array_member_copy(struct, src_name, member): 283 field_name = "%s->%s" % (struct, member.name) 284 len_field_name = "%s->%s" % (struct, member.len) 285 allocation = "%s = vk_zalloc(queue->alloc, sizeof(*%s) * %s, 8, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);" % (field_name, field_name, len_field_name) 286 const_cast = remove_suffix(member.decl.replace("const", ""), member.name) 287 copy = "memcpy((%s)%s, %s->%s, sizeof(*%s) * %s);" % (const_cast, field_name, src_name, member.name, field_name, len_field_name) 288 return "%s\n %s\n" % (allocation, copy) 289 290def get_pnext_member_copy(struct, src_type, member, types, level): 291 if not types[src_type].extended_by: 292 return "" 293 field_name = "%s->%s" % (struct, member.name) 294 pnext_decl = "const VkBaseInStructure *pnext = %s;" % field_name 295 case_stmts = "" 296 for type in types[src_type].extended_by: 297 case_stmts += """ 298 case %s: 299 %s 300 break; 301 """ % (type.enum, get_struct_copy(field_name, "pnext", type.name, "sizeof(%s)" % type.name, types, level)) 302 return """ 303 %s 304 if (pnext) { 305 switch ((int32_t)pnext->sType) { 306 %s 307 } 308 } 309 """ % (pnext_decl, case_stmts) 310 311def get_struct_copy(dst, src_name, src_type, size, types, level=0): 312 global tmp_dst_idx 313 global tmp_src_idx 314 315 allocation = "%s = vk_zalloc(queue->alloc, %s, 8, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);" % (dst, size) 316 copy = "memcpy((void*)%s, %s, %s);" % (dst, src_name, size) 317 318 level += 1 319 tmp_dst = "%s *tmp_dst%d = (void *) %s; (void) tmp_dst%d;" % (src_type, level, dst, level) 320 tmp_src = "%s *tmp_src%d = (void *) %s; (void) tmp_src%d;" % (src_type, level, src_name, level) 321 322 member_copies = "" 323 if src_type in types: 324 for member in types[src_type].members: 325 if member.len and member.len != 'null-terminated': 326 member_copies += get_array_member_copy("tmp_dst%d" % level, "tmp_src%d" % level, member) 327 elif member.name == 'pNext': 328 member_copies += get_pnext_member_copy("tmp_dst%d" % level, src_type, member, types, level) 329 330 null_assignment = "%s = NULL;" % dst 331 if_stmt = "if (%s) {" % src_name 332 return "%s\n %s\n %s\n %s\n %s \n %s } else {\n %s\n }" % (if_stmt, allocation, copy, tmp_dst, tmp_src, member_copies, null_assignment) 333 334def get_struct_free(command, param, types): 335 field_name = "cmd->u.%s.%s" % (to_struct_field_name(command.name), to_field_name(param.name)) 336 const_cast = remove_suffix(param.decl.replace("const", ""), param.name) 337 driver_data_free = "vk_free(queue->alloc, cmd->driver_data);\n" 338 struct_free = "vk_free(queue->alloc, (%s)%s);" % (const_cast, field_name) 339 member_frees = "" 340 if (param.type in types): 341 for member in types[param.type].members: 342 if member.len and member.len != 'null-terminated': 343 member_name = "cmd->u.%s.%s->%s" % (to_struct_field_name(command.name), to_field_name(param.name), member.name) 344 const_cast = remove_suffix(member.decl.replace("const", ""), member.name) 345 member_frees += "vk_free(queue->alloc, (%s)%s);\n" % (const_cast, member_name) 346 return "%s %s %s\n" % (member_frees, driver_data_free, struct_free) 347 348EntrypointType = namedtuple('EntrypointType', 'name enum members extended_by') 349 350def get_types(doc): 351 """Extract the types from the registry.""" 352 types = {} 353 354 for _type in doc.findall('./types/type'): 355 if _type.attrib.get('category') != 'struct': 356 continue 357 members = [] 358 type_enum = None 359 for p in _type.findall('./member'): 360 member = EntrypointParam(type=p.find('./type').text, 361 name=p.find('./name').text, 362 decl=''.join(p.itertext()), 363 len=p.attrib.get('len', None)) 364 members.append(member) 365 366 if p.find('./name').text == 'sType': 367 type_enum = p.attrib.get('values') 368 types[_type.attrib['name']] = EntrypointType(name=_type.attrib['name'], enum=type_enum, members=members, extended_by=[]) 369 370 for _type in doc.findall('./types/type'): 371 if _type.attrib.get('category') != 'struct': 372 continue 373 if _type.attrib.get('structextends') is None: 374 continue 375 for extended in _type.attrib.get('structextends').split(','): 376 types[extended].extended_by.append(types[_type.attrib['name']]) 377 378 return types 379 380def get_types_from_xml(xml_files): 381 types = {} 382 383 for filename in xml_files: 384 doc = et.parse(filename) 385 types.update(get_types(doc)) 386 387 return types 388 389def main(): 390 parser = argparse.ArgumentParser() 391 parser.add_argument('--out-c', required=True, help='Output C file.') 392 parser.add_argument('--out-h', required=True, help='Output H file.') 393 parser.add_argument('--xml', 394 help='Vulkan API XML file.', 395 required=True, action='append', dest='xml_files') 396 args = parser.parse_args() 397 398 commands = [] 399 for e in get_entrypoints_from_xml(args.xml_files): 400 if e.name.startswith('Cmd') and \ 401 not e.alias: 402 commands.append(e) 403 404 types = get_types_from_xml(args.xml_files) 405 406 assert os.path.dirname(args.out_c) == os.path.dirname(args.out_h) 407 408 environment = { 409 'header': os.path.basename(args.out_h), 410 'commands': commands, 411 'filename': os.path.basename(__file__), 412 'to_underscore': to_underscore, 413 'get_array_len': get_array_len, 414 'to_struct_field_name': to_struct_field_name, 415 'to_field_name': to_field_name, 416 'to_field_decl': to_field_decl, 417 'to_enum_name': to_enum_name, 418 'to_struct_name': to_struct_name, 419 'get_array_copy': get_array_copy, 420 'get_struct_copy': get_struct_copy, 421 'get_struct_free': get_struct_free, 422 'types': types, 423 'manual_commands': MANUAL_COMMANDS, 424 'remove_suffix': remove_suffix, 425 } 426 427 try: 428 with open(args.out_h, 'wb') as f: 429 guard = os.path.basename(args.out_h).replace('.', '_').upper() 430 f.write(TEMPLATE_H.render(guard=guard, **environment)) 431 with open(args.out_c, 'wb') as f: 432 f.write(TEMPLATE_C.render(**environment)) 433 except Exception: 434 # In the event there's an error, this imports some helpers from mako 435 # to print a useful stack trace and prints it, then exits with 436 # status 1, if python is run with debug; otherwise it just raises 437 # the exception 438 if __debug__: 439 import sys 440 from mako import exceptions 441 sys.stderr.write(exceptions.text_error_template().render() + '\n') 442 sys.exit(1) 443 raise 444 445if __name__ == '__main__': 446 main() 447