#Copyright 2021 Google LLC #SPDX - License - Identifier : MIT import argparse import json import os SIMPLE_OBJECT_CREATE_DRIVER_HANDLE_TEMPL = ''' /* create a driver {vk_type} and update the vkr_{vkr_type} */ static inline VkResult vkr_{create_func_name}_create_driver_handle( UNUSED struct vkr_context *ctx, struct vn_command_{create_cmd} *args, struct vkr_{vkr_type} *obj) {{ struct vkr_device *dev = vkr_device_from_handle(args->device); struct vn_device_proc_table *vk = &dev->proc_table; /* handles in args are replaced */ vn_replace_{create_cmd}_args_handle(args); args->ret = vk->{proc_create}(args->device, args->{create_info}, NULL, &obj->base.handle.{vkr_type}); return args->ret; }} ''' POOL_OBJECT_CREATE_DRIVER_HANDLES_TEMPL = ''' /* create an array of driver {vk_type}s from a pool and update the * object_array */ static inline VkResult vkr_{create_func_name}_create_driver_handles( UNUSED struct vkr_context *ctx, struct vn_command_{create_cmd} *args, struct object_array *arr) {{ struct vkr_device *dev = vkr_device_from_handle(args->device); struct vn_device_proc_table *vk = &dev->proc_table; /* handles in args are replaced */ vn_replace_{create_cmd}_args_handle(args); args->ret = vk->{proc_create}(args->device, args->{create_info}, arr->handle_storage); return args->ret; }} ''' PIPELINE_OBJECT_CREATE_DRIVER_HANDLES_TEMPL = ''' /* create an array of driver {vk_type}s and update the object_array */ static inline VkResult vkr_{create_func_name}_create_driver_handles( UNUSED struct vkr_context *ctx, struct vn_command_{create_cmd} *args, struct object_array *arr) {{ struct vkr_device *dev = vkr_device_from_handle(args->device); struct vn_device_proc_table *vk = &dev->proc_table; /* handles in args are replaced */ vn_replace_{create_cmd}_args_handle(args); args->ret = vk->{proc_create}(args->device, args->{create_cache}, args->{create_count}, args->{create_info}, NULL, arr->handle_storage); return args->ret; }} ''' SIMPLE_OBJECT_DESTROY_DRIVER_HANDLE_TEMPL = ''' /* destroy a driver {vk_type} */ static inline void vkr_{destroy_func_name}_destroy_driver_handle( UNUSED struct vkr_context *ctx, struct vn_command_{destroy_cmd} *args) {{ struct vkr_device *dev = vkr_device_from_handle(args->device); struct vn_device_proc_table *vk = &dev->proc_table; /* handles in args are replaced */ vn_replace_{destroy_cmd}_args_handle(args); vk->{proc_destroy}(args->device, args->{destroy_obj}, NULL); }} ''' POOL_OBJECT_DESTROY_DRIVER_HANDLES_TEMPL = ''' /* destroy an array of driver {vk_type}s from a pool, remove them from the * vkr_{pool_type}, and return the list of affected vkr_{vkr_type}s */ static inline void vkr_{destroy_func_name}_destroy_driver_handles( UNUSED struct vkr_context *ctx, struct vn_command_{destroy_cmd} *args, struct list_head *free_list) {{ struct vkr_device *dev = vkr_device_from_handle(args->device); struct vn_device_proc_table *vk = &dev->proc_table; list_inithead(free_list); for (uint32_t i = 0; i < args->{destroy_count}; i++) {{ struct vkr_{vkr_type} *obj = vkr_{vkr_type}_from_handle(args->{destroy_objs}[i]); if (!obj) continue; list_del(&obj->base.track_head); list_addtail(&obj->base.track_head, free_list); }} /* handles in args are replaced */ vn_replace_{destroy_cmd}_args_handle(args); vk->{proc_destroy}(args->device, args->{destroy_pool}, args->{destroy_count}, args->{destroy_objs}); }} ''' PIPELINE_OBJECT_DESTROY_DRIVER_HANDLE_TEMPL = SIMPLE_OBJECT_DESTROY_DRIVER_HANDLE_TEMPL COMMON_OBJECT_INIT_ARRAY_TEMPL = ''' /* initialize an object_array for vkr_{vkr_type}s */ static inline VkResult vkr_{create_func_name}_init_array( struct vkr_context *ctx, struct vn_command_{create_cmd} *args, struct object_array *arr) {{ args->ret = object_array_init(ctx, arr, args->{create_count}, {vk_enum}, sizeof(struct vkr_{vkr_type}), sizeof(*args->{create_objs}), args->{create_objs}) ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY; return args->ret; }} ''' POOL_OBJECT_INIT_ARRAY_TEMPL = COMMON_OBJECT_INIT_ARRAY_TEMPL PIPELINE_OBJECT_INIT_ARRAY_TEMPL = COMMON_OBJECT_INIT_ARRAY_TEMPL SIMPLE_OBJECT_CREATE_TEMPL = ''' /* create a vkr_{vkr_type} */ static inline struct vkr_{vkr_type} * vkr_{create_func_name}_create( struct vkr_context *ctx, struct vn_command_{create_cmd} *args) {{ struct vkr_{vkr_type} *obj = vkr_context_alloc_object(ctx, sizeof(*obj), {vk_enum}, args->{create_obj}); if (!obj) {{ args->ret = VK_ERROR_OUT_OF_HOST_MEMORY; return NULL; }} /* handles in args are replaced */ if (vkr_{create_func_name}_create_driver_handle(ctx, args, obj) != VK_SUCCESS) {{ free(obj); return NULL; }} return obj; }} ''' COMMON_OBJECT_CREATE_ARRAY_TEMPL = ''' /* create an array of vkr_{vkr_type}s */ static inline VkResult vkr_{create_func_name}_create_array( struct vkr_context *ctx, struct vn_command_{create_cmd} *args, struct object_array *arr) {{ if (vkr_{create_func_name}_init_array(ctx, args, arr) != VK_SUCCESS) return args->ret; if (vkr_{create_func_name}_create_driver_handles(ctx, args, arr) < VK_SUCCESS) {{ /* In case the client expects a reply, clear all returned handles to * VK_NULL_HANDLE. */ memset(args->{create_objs}, 0, args->{create_count} * sizeof(args->{create_objs}[0])); object_array_fini(arr); return args->ret; }} return args->ret; }} ''' POOL_OBJECT_CREATE_ARRAY_TEMPL = COMMON_OBJECT_CREATE_ARRAY_TEMPL PIPELINE_OBJECT_CREATE_ARRAY_TEMPL = COMMON_OBJECT_CREATE_ARRAY_TEMPL SIMPLE_OBJECT_CREATE_AND_ADD_TEMPL = ''' /* create a vkr_{vkr_type} and add it to the vkr_device */ static inline struct vkr_{vkr_type} * vkr_{create_func_name}_create_and_add( struct vkr_context *ctx, struct vn_command_{create_cmd} *args) {{ struct vkr_device *dev = vkr_device_from_handle(args->device); struct vkr_{vkr_type} *obj = vkr_{create_func_name}_create(ctx, args); if (!obj) return NULL; vkr_device_add_object(ctx, dev, &obj->base); return obj; }} ''' POOL_OBJECT_ADD_ARRAY_TEMPL = ''' /* steal vkr_{vkr_type}s from an object_array and add them to the * vkr_{pool_type} and the context */ static inline void vkr_{create_func_name}_add_array( struct vkr_context *ctx, struct vkr_device *dev, struct vkr_{pool_type} *pool, struct object_array *arr) {{ for (uint32_t i = 0; i < arr->count; i++) {{ struct vkr_{vkr_type} *obj = arr->objects[i]; obj->base.handle.{vkr_type} = (({vk_type} *)arr->handle_storage)[i]; obj->device = dev; /* pool objects are tracked by the pool other than the device */ list_add(&obj->base.track_head, &pool->{vkr_type}s); vkr_context_add_object(ctx, &obj->base); }} arr->objects_stolen = true; object_array_fini(arr); }} ''' PIPELINE_OBJECT_ADD_ARRAY_TEMPL = ''' /* steal vkr_{vkr_type}s from an object_array and add them to the device */ static inline void vkr_{create_func_name}_add_array( struct vkr_context *ctx, struct vkr_device *dev, struct object_array *arr, {vk_type} *args_{create_objs}) {{ for (uint32_t i = 0; i < arr->count; i++) {{ struct vkr_{vkr_type} *obj = arr->objects[i]; obj->base.handle.{vkr_type} = (({vk_type} *)arr->handle_storage)[i]; /* Individual pipelines may fail creation. */ if (obj->base.handle.{vkr_type} == VK_NULL_HANDLE) {{ free(obj); arr->objects[i] = NULL; args_{create_objs}[i] = VK_NULL_HANDLE; }} else {{ vkr_device_add_object(ctx, dev, &obj->base); }} }} arr->objects_stolen = true; object_array_fini(arr); }} ''' SIMPLE_OBJECT_DESTROY_AND_REMOVE_TEMPL = ''' /* remove a vkr_{vkr_type} from the device and destroy it */ static inline void vkr_{destroy_func_name}_destroy_and_remove( struct vkr_context *ctx, struct vn_command_{destroy_cmd} *args) {{ struct vkr_device *dev = vkr_device_from_handle(args->device); struct vkr_{vkr_type} *obj = vkr_{vkr_type}_from_handle(args->{destroy_obj}); if (!obj) return; vkr_{destroy_func_name}_destroy_driver_handle(ctx, args); vkr_device_remove_object(ctx, dev, &obj->base); }} ''' PIPELINE_OBJECT_DESTROY_AND_REMOVE_TEMPL = SIMPLE_OBJECT_DESTROY_AND_REMOVE_TEMPL def apply_variant(json_obj, json_variant): tmp_obj = json_obj.copy() for key, val in json_variant.items(): tmp_obj[key] = val return tmp_obj def simple_object_generator(json_obj): '''Generate functions for a simple object. For most device objects, object creation can be broken down into 3 steps (1) allocate and initialize the object (2) create the driver handle (3) add the object to the device and the object table SIMPLE_OBJECT_CREATE_DRIVER_HANDLE_TEMPL defines a function for (2). SIMPLE_OBJECT_CREATE_TEMPL defines a function for (1) and (2). SIMPLE_OBJECT_CREATE_AND_ADD_TEMPL defines a function for all steps. Object destruction can be broken down into 2 steps (1) destroy the driver handle (2) remove the object from the device and the object table SIMPLE_OBJECT_DESTROY_DRIVER_HANDLE_TEMPL defines a function for (1). SIMPLE_OBJECT_DESTROY_AND_REMOVE_TEMPL defines a function for both steps. ''' contents = '' contents += SIMPLE_OBJECT_CREATE_DRIVER_HANDLE_TEMPL.format(**json_obj) contents += SIMPLE_OBJECT_CREATE_TEMPL.format(**json_obj) contents += SIMPLE_OBJECT_CREATE_AND_ADD_TEMPL.format(**json_obj) contents += SIMPLE_OBJECT_DESTROY_DRIVER_HANDLE_TEMPL.format(**json_obj) contents += SIMPLE_OBJECT_DESTROY_AND_REMOVE_TEMPL.format(**json_obj) for json_variant in json_obj['variants']: tmp_obj = apply_variant(json_obj, json_variant) contents += SIMPLE_OBJECT_CREATE_DRIVER_HANDLE_TEMPL.format(**tmp_obj) contents += SIMPLE_OBJECT_CREATE_TEMPL.format(**tmp_obj) contents += SIMPLE_OBJECT_CREATE_AND_ADD_TEMPL.format(**tmp_obj) return contents def pool_object_generator(json_obj): '''Generate functions for a pool object. For VkCommandBuffer and VkDescriptorSet, object creation can be broken down into 3 steps (1) allocate and initialize the object array (2) create the driver handles (3) add the object array to the device and the object table POOL_OBJECT_INIT_ARRAY_TEMPL defines a function for (1). POOL_OBJECT_CREATE_DRIVER_HANDLES_TEMPL defines a function for (2). POOL_OBJECT_CREATE_ARRAY_TEMPL defines a function for (1) and (2). POOL_OBJECT_ADD_ARRAY_TEMPL defines a function for step (3). Object destruction can be broken down into 2 steps (1) destroy the driver handles (2) remove the objects from the pool and the object table POOL_OBJECT_DESTROY_DRIVER_HANDLES_TEMPL defines a function for (1) and the first half of (2). ''' contents = '' contents += POOL_OBJECT_INIT_ARRAY_TEMPL.format(**json_obj) contents += POOL_OBJECT_CREATE_DRIVER_HANDLES_TEMPL.format(**json_obj) contents += POOL_OBJECT_CREATE_ARRAY_TEMPL.format(**json_obj) contents += POOL_OBJECT_ADD_ARRAY_TEMPL.format(**json_obj) contents += POOL_OBJECT_DESTROY_DRIVER_HANDLES_TEMPL.format(**json_obj) assert not json_obj['variants'] return contents def pipeline_object_generator(json_obj): '''Generate functions for a pipeline object. For VkPipeline, object creation can be broken down into 3 steps (1) allocate and initialize the object array (2) create the driver handles (3) add the object array to the device and the object table PIPELINE_OBJECT_INIT_ARRAY_TEMPL defines a function for (1). PIPELINE_OBJECT_CREATE_DRIVER_HANDLES_TEMPL defines a function for (2). PIPELINE_OBJECT_CREATE_ARRAY_TEMPL defines a function for (1) and (2). PIPELINE_OBJECT_ADD_ARRAY_TEMPL defines a function for step (3). Object destruction can be broken down into 2 steps (1) destroy the driver handle (2) remove the object from the device and the object table PIPELINE_OBJECT_DESTROY_DRIVER_HANDLE_TEMPL defines a function for (1). PIPELINE_OBJECT_DESTROY_AND_REMOVE_TEMPL defines a function for both steps. ''' contents = '' contents += PIPELINE_OBJECT_INIT_ARRAY_TEMPL.format(**json_obj) contents += PIPELINE_OBJECT_CREATE_DRIVER_HANDLES_TEMPL.format(**json_obj) contents += PIPELINE_OBJECT_CREATE_ARRAY_TEMPL.format(**json_obj) # shared by both graphics and compute tmp_obj = json_obj.copy() tmp_obj['create_func_name'] = tmp_obj['vkr_type'] contents += PIPELINE_OBJECT_ADD_ARRAY_TEMPL.format(**tmp_obj) contents += PIPELINE_OBJECT_DESTROY_DRIVER_HANDLE_TEMPL.format(**json_obj) contents += PIPELINE_OBJECT_DESTROY_AND_REMOVE_TEMPL.format(**json_obj) for json_variant in json_obj['variants']: tmp_obj = apply_variant(json_obj, json_variant) contents += PIPELINE_OBJECT_INIT_ARRAY_TEMPL.format(**tmp_obj) contents += PIPELINE_OBJECT_CREATE_DRIVER_HANDLES_TEMPL.format(**tmp_obj) contents += PIPELINE_OBJECT_CREATE_ARRAY_TEMPL.format(**tmp_obj) return contents object_generators = { 'simple-object': simple_object_generator, 'pool-object': pool_object_generator, 'pipeline-object': pipeline_object_generator, } FILE_HEADER_TEMPL = ''' /* This file is generated by {script}. */ #ifndef {guard} #define {guard} #include "vkr_common.h" {protocol_includes} #include "vkr_context.h" #include "vkr_device.h" ''' FILE_FOOTER_TEMPL = ''' #endif /* {guard} */ ''' def get_guard(filename): return filename.upper().translate(str.maketrans('.', '_')) def file_header_generator(json_file): script = os.path.basename(__file__) guard = get_guard(json_file['filename']) include_filenames = [] for json_obj in json_file['objects']: name = 'venus-protocol/vn_protocol_renderer_{}.h'.format( json_obj['vkr_type']) include_filenames.append(name) protocol_includes = '#include "' + '"\n#include "'.join(include_filenames) + '"' return FILE_HEADER_TEMPL.format(script=script, guard=guard, protocol_includes=protocol_includes).lstrip() def file_footer_generator(json_file): guard = get_guard(json_file['filename']) return FILE_FOOTER_TEMPL.format(guard=guard) def process_objects(json_objs): for json_obj in json_objs: json_obj.setdefault('create_func_name', json_obj['vkr_type']) json_obj.setdefault('destroy_func_name', json_obj['vkr_type']) json_obj.setdefault('variants', []) json_obj['proc_create'] = json_obj.get('create_cmd')[2:] json_obj['proc_destroy'] = json_obj.get('destroy_cmd')[2:] for variant in json_obj.get('variants'): if variant.get('create_cmd') != None: variant['proc_create'] = variant.get('create_cmd')[2:] if variant.get('destroy_cmd') != None: variant['proc_destroy'] = variant.get('create_cmd')[2:] def file_generator(json_file): contents = file_header_generator(json_file) for json_obj in json_file['objects']: contents += object_generators[json_obj['generator']](json_obj) contents += file_footer_generator(json_file) return contents def parse_args(): parser = argparse.ArgumentParser() parser.add_argument('json', help='specifies the input JSON file') parser.add_argument('-o', '--output-dir', required=True, help='specifies output directory') return parser.parse_args() def main(): args = parse_args() with open(args.json) as f: json_files = json.load(f) for json_file in json_files: process_objects(json_file['objects']) output = os.path.join(args.output_dir, json_file['filename']) with open(output, 'wb') as f: contents = file_generator(json_file) f.write(contents.encode()) if __name__ == '__main__': main()