#!/usr/bin/python3 -i # # Copyright (c) 2015-2022 The Khronos Group Inc. # Copyright (c) 2015-2022 Valve Corporation # Copyright (c) 2015-2022 LunarG, Inc. # Copyright (c) 2015-2017 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Author: Mark Young # Author: Mark Lobodzinski import os,re,sys import xml.etree.ElementTree as etree from generator import * from collections import namedtuple from common_codegen import * WSI_EXT_NAMES = ['VK_KHR_surface', 'VK_KHR_display', 'VK_KHR_xlib_surface', 'VK_KHR_xcb_surface', 'VK_KHR_wayland_surface', 'VK_EXT_directfb_surface', 'VK_KHR_win32_surface', 'VK_KHR_android_surface', 'VK_GGP_stream_descriptor_surface', 'VK_MVK_macos_surface', 'VK_MVK_ios_surface', 'VK_EXT_headless_surface', 'VK_EXT_metal_surface', 'VK_FUCHSIA_imagepipe_surface', 'VK_KHR_swapchain', 'VK_KHR_display_swapchain', 'VK_KHR_get_display_properties2', 'VK_KHR_get_surface_capabilities2', 'VK_QNX_screen_surface', 'VK_NN_vi_surface'] ADD_INST_CMDS = ['vkCreateInstance', 'vkEnumerateInstanceExtensionProperties', 'vkEnumerateInstanceLayerProperties', 'vkEnumerateInstanceVersion'] AVOID_EXT_NAMES = ['VK_EXT_debug_report'] NULL_CHECK_EXT_NAMES= ['VK_EXT_debug_utils'] AVOID_CMD_NAMES = ['vkCreateDebugUtilsMessengerEXT', 'vkDestroyDebugUtilsMessengerEXT', 'vkSubmitDebugUtilsMessageEXT'] DEVICE_CMDS_NEED_TERM = ['vkGetDeviceProcAddr', 'vkCreateSwapchainKHR', 'vkCreateSharedSwapchainsKHR', 'vkGetDeviceGroupSurfacePresentModesKHR', 'vkDebugMarkerSetObjectTagEXT', 'vkDebugMarkerSetObjectNameEXT', 'vkSetDebugUtilsObjectNameEXT', 'vkSetDebugUtilsObjectTagEXT', 'vkQueueBeginDebugUtilsLabelEXT', 'vkQueueEndDebugUtilsLabelEXT', 'vkQueueInsertDebugUtilsLabelEXT', 'vkCmdBeginDebugUtilsLabelEXT', 'vkCmdEndDebugUtilsLabelEXT', 'vkCmdInsertDebugUtilsLabelEXT', 'vkGetDeviceGroupSurfacePresentModes2EXT'] # These are the aliased functions that use the same terminator for both extension and core versions # Generally, this is only applies to physical device level functions in instance extensions SHARED_ALIASES = { # 1.1 aliases 'vkEnumeratePhysicalDeviceGroupsKHR': 'vkEnumeratePhysicalDeviceGroups', 'vkGetPhysicalDeviceFeatures2KHR': 'vkGetPhysicalDeviceFeatures2', 'vkGetPhysicalDeviceProperties2KHR': 'vkGetPhysicalDeviceProperties2', 'vkGetPhysicalDeviceFormatProperties2KHR': 'vkGetPhysicalDeviceFormatProperties2', 'vkGetPhysicalDeviceImageFormatProperties2KHR': 'vkGetPhysicalDeviceImageFormatProperties2', 'vkGetPhysicalDeviceQueueFamilyProperties2KHR': 'vkGetPhysicalDeviceQueueFamilyProperties2', 'vkGetPhysicalDeviceMemoryProperties2KHR': 'vkGetPhysicalDeviceMemoryProperties2', 'vkGetPhysicalDeviceSparseImageFormatProperties2KHR': 'vkGetPhysicalDeviceSparseImageFormatProperties2', 'vkGetPhysicalDeviceExternalBufferPropertiesKHR': 'vkGetPhysicalDeviceExternalBufferProperties', 'vkGetPhysicalDeviceExternalSemaphorePropertiesKHR': 'vkGetPhysicalDeviceExternalSemaphoreProperties', 'vkGetPhysicalDeviceExternalFencePropertiesKHR': 'vkGetPhysicalDeviceExternalFenceProperties', } PRE_INSTANCE_FUNCTIONS = ['vkEnumerateInstanceExtensionProperties', 'vkEnumerateInstanceLayerProperties', 'vkEnumerateInstanceVersion'] # # LoaderExtensionGeneratorOptions - subclass of GeneratorOptions. class LoaderExtensionGeneratorOptions(GeneratorOptions): def __init__(self, conventions = None, filename = None, directory = '.', genpath = None, apiname = None, profile = None, versions = '.*', emitversions = '.*', defaultExtensions = None, addExtensions = None, removeExtensions = None, emitExtensions = None, sortProcedure = regSortFeatures, prefixText = "", genFuncPointers = True, protectFile = True, protectFeature = True, apicall = '', apientry = '', apientryp = '', indentFuncProto = True, indentFuncPointer = False, alignFuncParam = 0, expandEnumerants = True): GeneratorOptions.__init__(self, conventions = conventions, filename = filename, directory = directory, genpath = genpath, apiname = apiname, profile = profile, versions = versions, emitversions = emitversions, defaultExtensions = defaultExtensions, addExtensions = addExtensions, removeExtensions = removeExtensions, emitExtensions = emitExtensions, sortProcedure = sortProcedure) self.prefixText = prefixText self.prefixText = None self.apicall = apicall self.apientry = apientry self.apientryp = apientryp self.alignFuncParam = alignFuncParam self.expandEnumerants = expandEnumerants # # LoaderExtensionOutputGenerator - subclass of OutputGenerator. # Generates dispatch table helper header files for LVL class LoaderExtensionOutputGenerator(OutputGenerator): """Generate dispatch table helper header based on XML element attributes""" def __init__(self, errFile = sys.stderr, warnFile = sys.stderr, diagFile = sys.stdout): OutputGenerator.__init__(self, errFile, warnFile, diagFile) # Internal state - accumulators for different inner block text self.ext_instance_dispatch_list = [] # List of extension entries for instance dispatch list self.ext_device_dispatch_list = [] # List of extension entries for device dispatch list self.core_commands = [] # List of CommandData records for core Vulkan commands self.ext_commands = [] # List of CommandData records for extension Vulkan commands self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'cdecl']) self.CommandData = namedtuple('CommandData', ['name', 'ext_name', 'ext_type', 'require', 'protect', 'return_type', 'handle_type', 'params', 'cdecl']) self.instanceExtensions = [] self.ExtensionData = namedtuple('ExtensionData', ['name', 'type', 'protect', 'define', 'num_commands']) # # Called once at the beginning of each run def beginFile(self, genOpts): OutputGenerator.beginFile(self, genOpts) # User-supplied prefix text, if any (list of strings) if (genOpts.prefixText): for s in genOpts.prefixText: write(s, file=self.outFile) # File Comment file_comment = '// *** THIS FILE IS GENERATED - DO NOT EDIT ***\n' file_comment += '// See loader_extension_generator.py for modifications\n' write(file_comment, file=self.outFile) # Copyright Notice copyright = '/*\n' copyright += ' * Copyright (c) 2015-2022 The Khronos Group Inc.\n' copyright += ' * Copyright (c) 2015-2022 Valve Corporation\n' copyright += ' * Copyright (c) 2015-2022 LunarG, Inc.\n' copyright += ' *\n' copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n' copyright += ' * you may not use this file except in compliance with the License.\n' copyright += ' * You may obtain a copy of the License at\n' copyright += ' *\n' copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n' copyright += ' *\n' copyright += ' * Unless required by applicable law or agreed to in writing, software\n' copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n' copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n' copyright += ' * See the License for the specific language governing permissions and\n' copyright += ' * limitations under the License.\n' copyright += ' *\n' copyright += ' * Author: Mark Lobodzinski \n' copyright += ' * Author: Mark Young \n' copyright += ' */\n' preamble = '' if self.genOpts.filename == 'vk_loader_extensions.h': preamble += '#pragma once\n' elif self.genOpts.filename == 'vk_loader_extensions.c': preamble += '#include \n' preamble += '#include \n' preamble += '#include \n' preamble += '#include "vk_loader_platform.h"\n' preamble += '#include "loader.h"\n' preamble += '#include "vk_loader_extensions.h"\n' preamble += '#include \n' preamble += '#include "wsi.h"\n' preamble += '#include "debug_utils.h"\n' preamble += '#include "extension_manual.h"\n' elif self.genOpts.filename == 'vk_layer_dispatch_table.h': preamble += '#pragma once\n' preamble += '\n' preamble += 'typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_GetPhysicalDeviceProcAddr)(VkInstance instance, const char* pName);\n' write(copyright, file=self.outFile) write(preamble, file=self.outFile) # # Write generate and write dispatch tables to output file def endFile(self): file_data = '' if self.genOpts.filename == 'vk_loader_extensions.h': file_data += self.OutputPrototypesInHeader() file_data += self.OutputLoaderTerminators() file_data += self.OutputIcdDispatchTable() file_data += self.OutputIcdExtensionEnableUnion() file_data += self.OutputDeviceFunctionTerminatorDispatchTable() elif self.genOpts.filename == 'vk_loader_extensions.c': file_data += self.OutputUtilitiesInSource() file_data += self.OutputIcdDispatchTableInit() file_data += self.OutputLoaderDispatchTables() file_data += self.InitDeviceFunctionTerminatorDispatchTable() file_data += self.OutputLoaderLookupFunc() file_data += self.CreateTrampTermFuncs() file_data += self.InstExtensionGPA() file_data += self.InstantExtensionCreate() file_data += self.DeviceExtensionGetTerminator() file_data += self.InitInstLoaderExtensionDispatchTable() file_data += self.OutputInstantExtensionWhitelistArray() elif self.genOpts.filename == 'vk_layer_dispatch_table.h': file_data += self.OutputLayerInstanceDispatchTable() file_data += self.OutputLayerDeviceDispatchTable() write(file_data, file=self.outFile); # Finish processing in superclass OutputGenerator.endFile(self) def beginFeature(self, interface, emit): # Start processing in superclass OutputGenerator.beginFeature(self, interface, emit) self.featureExtraProtect = GetFeatureProtect(interface) enums = interface[0].findall('enum') self.currentExtension = '' self.name_definition = '' for item in enums: name_definition = item.get('name') if 'EXTENSION_NAME' in name_definition: self.name_definition = name_definition self.type = interface.get('type') self.num_commands = 0 name = interface.get('name') self.currentExtension = name # # Process commands, adding to appropriate dispatch tables def genCmd(self, cmdinfo, name, alias): OutputGenerator.genCmd(self, cmdinfo, name, alias) # Get first param type params = cmdinfo.elem.findall('param') info = self.getTypeNameTuple(params[0]) self.num_commands += 1 if 'android' not in name: self.AddCommandToDispatchList(self.currentExtension, self.type, name, cmdinfo, info[0]) def endFeature(self): if 'android' not in self.currentExtension: self.instanceExtensions.append(self.ExtensionData(name=self.currentExtension, type=self.type, protect=self.featureExtraProtect, define=self.name_definition, num_commands=self.num_commands)) # Finish processing in superclass OutputGenerator.endFeature(self) # # Retrieve the value of the len tag def getLen(self, param): result = None len = param.attrib.get('len') if len and len != 'null-terminated': # For string arrays, 'len' can look like 'count,null-terminated', # indicating that we have a null terminated array of strings. We # strip the null-terminated from the 'len' field and only return # the parameter specifying the string count if 'null-terminated' in len: result = len.split(',')[0] else: result = len result = str(result).replace('::', '->') return result # # Determine if this API should be ignored or added to the instance or device dispatch table def AddCommandToDispatchList(self, extension_name, extension_type, name, cmdinfo, handle_type): handle = self.registry.tree.find("types/type/[name='" + handle_type + "'][@category='handle']") return_type = cmdinfo.elem.find('proto/type') if (return_type is not None and return_type.text == 'void'): return_type = None require = None if name == 'vkGetDeviceGroupSurfacePresentModes2EXT': require_node = self.registry.tree.find("./extensions/extension[@name='{}']/require/command[@name='{}']/..".format(extension_name, name)) if 'extension' in require_node.attrib: require = require_node.attrib['extension'] cmd_params = [] # Generate a list of commands for use in printing the necessary # core instance terminator prototypes params = cmdinfo.elem.findall('param') lens = set() for param in params: len = self.getLen(param) if len: lens.add(len) paramsInfo = [] for param in params: paramInfo = self.getTypeNameTuple(param) param_type = paramInfo[0] param_name = paramInfo[1] param_cdecl = self.makeCParamDecl(param, 0) cmd_params.append(self.CommandParam(type=param_type, name=param_name, cdecl=param_cdecl)) if handle is not None and handle_type != 'VkInstance' and handle_type != 'VkPhysicalDevice': # The Core Vulkan code will be wrapped in a feature called VK_VERSION_#_# # For example: VK_VERSION_1_0 wraps the core 1.0 Vulkan functionality if 'VK_VERSION_' in extension_name: self.core_commands.append( self.CommandData(name=name, ext_name=extension_name, ext_type='device', require=require, protect=self.featureExtraProtect, return_type = return_type, handle_type = handle_type, params = cmd_params, cdecl=self.makeCDecls(cmdinfo.elem)[0])) else: self.ext_device_dispatch_list.append((name, self.featureExtraProtect)) self.ext_commands.append( self.CommandData(name=name, ext_name=extension_name, ext_type=extension_type, require=require, protect=self.featureExtraProtect, return_type = return_type, handle_type = handle_type, params = cmd_params, cdecl=self.makeCDecls(cmdinfo.elem)[0])) else: # The Core Vulkan code will be wrapped in a feature called VK_VERSION_#_# # For example: VK_VERSION_1_0 wraps the core 1.0 Vulkan functionality if 'VK_VERSION_' in extension_name: self.core_commands.append( self.CommandData(name=name, ext_name=extension_name, ext_type='instance', require=require, protect=self.featureExtraProtect, return_type = return_type, handle_type = handle_type, params = cmd_params, cdecl=self.makeCDecls(cmdinfo.elem)[0])) else: self.ext_instance_dispatch_list.append((name, self.featureExtraProtect)) self.ext_commands.append( self.CommandData(name=name, ext_name=extension_name, ext_type=extension_type, require=require, protect=self.featureExtraProtect, return_type = return_type, handle_type = handle_type, params = cmd_params, cdecl=self.makeCDecls(cmdinfo.elem)[0])) # # Retrieve the type and name for a parameter def getTypeNameTuple(self, param): type = '' name = '' for elem in param: if elem.tag == 'type': type = noneStr(elem.text) elif elem.tag == 'name': name = noneStr(elem.text) return (type, name) def OutputPrototypesInHeader(self): protos = '' protos += '// Structures defined externally, but used here\n' protos += 'struct loader_instance;\n' protos += 'struct loader_device;\n' protos += 'struct loader_icd_term;\n' protos += 'struct loader_dev_dispatch_table;\n' protos += '\n' protos += '// Device extension error function\n' protos += 'VKAPI_ATTR VkResult VKAPI_CALL vkDevExtError(VkDevice dev);\n' protos += '\n' protos += '// Extension interception for vkGetInstanceProcAddr function, so we can return\n' protos += '// the appropriate information for any instance extensions we know about.\n' protos += 'bool extension_instance_gpa(struct loader_instance *ptr_instance, const char *name, void **addr);\n' protos += '\n' protos += '// Extension interception for vkCreateInstance function, so we can properly\n' protos += '// detect and enable any instance extension information for extensions we know\n' protos += '// about.\n' protos += 'void extensions_create_instance(struct loader_instance *ptr_instance, const VkInstanceCreateInfo *pCreateInfo);\n' protos += '\n' protos += '// Extension interception for vkGetDeviceProcAddr function, so we can return\n' protos += '// an appropriate terminator if this is one of those few device commands requiring\n' protos += '// a terminator.\n' protos += 'PFN_vkVoidFunction get_extension_device_proc_terminator(struct loader_device *dev, const char *name, bool* found_name);\n' protos += '\n' protos += '// Dispatch table properly filled in with appropriate terminators for the\n' protos += '// supported extensions.\n' protos += 'extern const VkLayerInstanceDispatchTable instance_disp;\n' protos += '\n' protos += '// Array of extension strings for instance extensions we support.\n' protos += 'extern const char *const LOADER_INSTANCE_EXTENSIONS[];\n' protos += '\n' protos += 'VKAPI_ATTR bool VKAPI_CALL loader_icd_init_entries(struct loader_icd_term *icd_term, VkInstance inst,\n' protos += ' const PFN_vkGetInstanceProcAddr fp_gipa);\n' protos += '\n' protos += '// Init Device function pointer dispatch table with core commands\n' protos += 'VKAPI_ATTR void VKAPI_CALL loader_init_device_dispatch_table(struct loader_dev_dispatch_table *dev_table, PFN_vkGetDeviceProcAddr gpa,\n' protos += ' VkDevice dev);\n' protos += '\n' protos += '// Init Device function pointer dispatch table with extension commands\n' protos += 'VKAPI_ATTR void VKAPI_CALL loader_init_device_extension_dispatch_table(struct loader_dev_dispatch_table *dev_table,\n' protos += ' PFN_vkGetInstanceProcAddr gipa,\n' protos += ' PFN_vkGetDeviceProcAddr gdpa,\n' protos += ' VkInstance inst,\n' protos += ' VkDevice dev);\n' protos += '\n' protos += '// Init Instance function pointer dispatch table with core commands\n' protos += 'VKAPI_ATTR void VKAPI_CALL loader_init_instance_core_dispatch_table(VkLayerInstanceDispatchTable *table, PFN_vkGetInstanceProcAddr gpa,\n' protos += ' VkInstance inst);\n' protos += '\n' protos += '// Init Instance function pointer dispatch table with core commands\n' protos += 'VKAPI_ATTR void VKAPI_CALL loader_init_instance_extension_dispatch_table(VkLayerInstanceDispatchTable *table, PFN_vkGetInstanceProcAddr gpa,\n' protos += ' VkInstance inst);\n' protos += '\n' protos += '// Device command lookup function\n' protos += 'VKAPI_ATTR void* VKAPI_CALL loader_lookup_device_dispatch_table(const VkLayerDispatchTable *table, const char *name);\n' protos += '\n' protos += '// Instance command lookup function\n' protos += 'VKAPI_ATTR void* VKAPI_CALL loader_lookup_instance_dispatch_table(const VkLayerInstanceDispatchTable *table, const char *name,\n' protos += ' bool *found_name);\n' protos += '\n' return protos def OutputUtilitiesInSource(self): protos = '' protos += '// Device extension error function\n' protos += 'VKAPI_ATTR VkResult VKAPI_CALL vkDevExtError(VkDevice dev) {\n' protos += ' struct loader_device *found_dev;\n' protos += ' // The device going in is a trampoline device\n' protos += ' struct loader_icd_term *icd_term = loader_get_icd_and_device(dev, &found_dev, NULL);\n' protos += '\n' protos += ' if (icd_term)\n' protos += ' loader_log(icd_term->this_instance, VULKAN_LOADER_ERROR_BIT, 0,\n' protos += ' "Bad destination in loader trampoline dispatch,"\n' protos += ' "Are layers and extensions that you are calling enabled?");\n' protos += ' return VK_ERROR_EXTENSION_NOT_PRESENT;\n' protos += '}\n\n' return protos # # Create a layer instance dispatch table from the appropriate list and return it as a string def OutputLayerInstanceDispatchTable(self): commands = [] table = '' cur_extension_name = '' table += '// Instance function pointer dispatch table\n' table += 'typedef struct VkLayerInstanceDispatchTable_ {\n' # First add in an entry for GetPhysicalDeviceProcAddr. This will not # ever show up in the XML or header, so we have to manually add it. table += ' // Manually add in GetPhysicalDeviceProcAddr entry\n' table += ' PFN_GetPhysicalDeviceProcAddr GetPhysicalDeviceProcAddr;\n' for x in range(0, 2): if x == 0: commands = self.core_commands else: commands = self.ext_commands for cur_cmd in commands: is_inst_handle_type = cur_cmd.name in ADD_INST_CMDS or cur_cmd.handle_type == 'VkInstance' or cur_cmd.handle_type == 'VkPhysicalDevice' if is_inst_handle_type: if cur_cmd.ext_name != cur_extension_name: if 'VK_VERSION_' in cur_cmd.ext_name: table += '\n // ---- Core %s commands\n' % cur_cmd.ext_name[11:] else: table += '\n // ---- %s extension commands\n' % cur_cmd.ext_name cur_extension_name = cur_cmd.ext_name # Remove 'vk' from proto name base_name = cur_cmd.name[2:] if cur_cmd.protect is not None: table += '#ifdef %s\n' % cur_cmd.protect table += ' PFN_%s %s;\n' % (cur_cmd.name, base_name) if cur_cmd.protect is not None: table += '#endif // %s\n' % cur_cmd.protect table += '} VkLayerInstanceDispatchTable;\n\n' return table # # Create a layer device dispatch table from the appropriate list and return it as a string def OutputLayerDeviceDispatchTable(self): commands = [] table = '' cur_extension_name = '' table += '// Device function pointer dispatch table\n' table += '#define DEVICE_DISP_TABLE_MAGIC_NUMBER 0x10ADED040410ADEDUL\n' table += 'typedef struct VkLayerDispatchTable_ {\n' table += ' uint64_t magic; // Should be DEVICE_DISP_TABLE_MAGIC_NUMBER\n' for x in range(0, 2): if x == 0: commands = self.core_commands else: commands = self.ext_commands for cur_cmd in commands: is_inst_handle_type = cur_cmd.name in ADD_INST_CMDS or cur_cmd.handle_type == 'VkInstance' or cur_cmd.handle_type == 'VkPhysicalDevice' if not is_inst_handle_type: if cur_cmd.ext_name != cur_extension_name: if 'VK_VERSION_' in cur_cmd.ext_name: table += '\n // ---- Core %s commands\n' % cur_cmd.ext_name[11:] else: table += '\n // ---- %s extension commands\n' % cur_cmd.ext_name cur_extension_name = cur_cmd.ext_name # Remove 'vk' from proto name base_name = cur_cmd.name[2:] if cur_cmd.protect is not None: table += '#ifdef %s\n' % cur_cmd.protect table += ' PFN_%s %s;\n' % (cur_cmd.name, base_name) if cur_cmd.protect is not None: table += '#endif // %s\n' % cur_cmd.protect table += '} VkLayerDispatchTable;\n\n' return table # # Common code between the dispatch table struct and the function filling out said struct def ShouldPrintInIcdDispatchTable(self, cur_cmd, skip_list): return cur_cmd.name == 'vkGetDeviceProcAddr' or \ (cur_cmd.handle_type not in ['VkDevice', 'VkCommandBuffer', 'VkQueue'] and cur_cmd.name not in skip_list) # # Create a dispatch table from the appropriate list and return it as a string def OutputIcdDispatchTable(self): commands = [] table = '' cur_extension_name = '' skip_commands = ['vkGetInstanceProcAddr', 'vkEnumerateDeviceLayerProperties', ] table += '// ICD function pointer dispatch table\n' table += 'struct loader_icd_term_dispatch {\n' for x in range(0, 2): if x == 0: commands = self.core_commands else: commands = self.ext_commands for cur_cmd in commands: if (self.ShouldPrintInIcdDispatchTable(cur_cmd, skip_commands)): if cur_cmd.ext_name != cur_extension_name: if 'VK_VERSION_' in cur_cmd.ext_name: table += '\n // ---- Core %s commands\n' % cur_cmd.ext_name[11:] else: table += '\n // ---- %s extension commands\n' % cur_cmd.ext_name cur_extension_name = cur_cmd.ext_name # Remove 'vk' from proto name base_name = cur_cmd.name[2:] if cur_cmd.protect is not None: table += '#ifdef %s\n' % cur_cmd.protect table += ' PFN_%s %s;\n' % (cur_cmd.name, base_name) if cur_cmd.protect is not None: table += '#endif // %s\n' % cur_cmd.protect table += '};\n\n' return table # # Init a dispatch table from the appropriate list and return it as a string def OutputIcdDispatchTableInit(self): commands = [] cur_extension_name = '' table = '' table += 'VKAPI_ATTR bool VKAPI_CALL loader_icd_init_entries(struct loader_icd_term *icd_term, VkInstance inst,\n' table += ' const PFN_vkGetInstanceProcAddr fp_gipa) {\n' table += '\n' table += '#define LOOKUP_GIPA(func, required) \\\n' table += ' do { \\\n' table += ' icd_term->dispatch.func = (PFN_vk##func)fp_gipa(inst, "vk" #func); \\\n' table += ' if (!icd_term->dispatch.func && required) { \\\n' table += ' loader_log((struct loader_instance *)inst, VULKAN_LOADER_WARN_BIT, 0, \\\n' table += ' loader_platform_get_proc_address_error("vk" #func)); \\\n' table += ' return false; \\\n' table += ' } \\\n' table += ' } while (0)\n' table += '\n' skip_gipa_commands = ['vkGetInstanceProcAddr', 'vkEnumerateDeviceLayerProperties', 'vkCreateInstance', 'vkEnumerateInstanceExtensionProperties', 'vkEnumerateInstanceLayerProperties', 'vkEnumerateInstanceVersion', ] for x in range(0, 2): if x == 0: commands = self.core_commands else: commands = self.ext_commands required = False for cur_cmd in commands: if (self.ShouldPrintInIcdDispatchTable(cur_cmd, skip_gipa_commands)): if cur_cmd.ext_name != cur_extension_name: if 'VK_VERSION_' in cur_cmd.ext_name: table += '\n // ---- Core %s\n' % cur_cmd.ext_name[11:] required = cur_cmd.ext_name == 'VK_VERSION_1_0' else: table += '\n // ---- %s extension commands\n' % cur_cmd.ext_name required = False cur_extension_name = cur_cmd.ext_name # Remove 'vk' from proto name base_name = cur_cmd.name[2:] if cur_cmd.protect is not None: table += '#ifdef %s\n' % cur_cmd.protect # The Core Vulkan code will be wrapped in a feature called VK_VERSION_#_# # For example: VK_VERSION_1_0 wraps the core 1.0 Vulkan functionality table += ' LOOKUP_GIPA(%s, %s);\n' % (base_name, 'true' if required else 'false') if cur_cmd.protect is not None: table += '#endif // %s\n' % cur_cmd.protect table += '\n' table += '#undef LOOKUP_GIPA\n' table += '\n' table += ' return true;\n' table += '};\n\n' return table # # Create the extension enable union def OutputIcdExtensionEnableUnion(self): extensions = self.instanceExtensions union = '' union += 'struct loader_instance_extension_enables {\n' for ext in extensions: if ('VK_VERSION_' in ext.name or ext.name in WSI_EXT_NAMES or ext.type == 'device' or ext.num_commands == 0): continue union += ' uint8_t %s;\n' % ext.name[3:].lower() union += '};\n\n' return union # # Creates the prototypes for the loader's core instance command terminators def OutputLoaderTerminators(self): terminators = '' terminators += '// Loader core instance terminators\n' for cur_cmd in self.core_commands: is_inst_handle_type = cur_cmd.name in ADD_INST_CMDS or cur_cmd.handle_type == 'VkInstance' or cur_cmd.handle_type == 'VkPhysicalDevice' if is_inst_handle_type: mod_string = '' new_terminator = cur_cmd.cdecl mod_string = new_terminator.replace("VKAPI_CALL vk", "VKAPI_CALL terminator_") if cur_cmd.name in PRE_INSTANCE_FUNCTIONS: mod_string = mod_string.replace(cur_cmd.name[2:] + '(\n', cur_cmd.name[2:] + '(\n const Vk' + cur_cmd.name[2:] + 'Chain* chain,\n') if (cur_cmd.protect is not None): terminators += '#ifdef %s\n' % cur_cmd.protect terminators += mod_string terminators += '\n' if (cur_cmd.protect is not None): terminators += '#endif // %s\n' % cur_cmd.protect terminators += '\n' return terminators # # Creates code to initialize the various dispatch tables def OutputLoaderDispatchTables(self): commands = [] tables = '' gpa_param = '' cur_type = '' cur_extension_name = '' for x in range(0, 4): if x == 0: cur_type = 'device' gpa_param = 'dev' commands = self.core_commands tables += '// Init Device function pointer dispatch table with core commands\n' tables += 'VKAPI_ATTR void VKAPI_CALL loader_init_device_dispatch_table(struct loader_dev_dispatch_table *dev_table, PFN_vkGetDeviceProcAddr gpa,\n' tables += ' VkDevice dev) {\n' tables += ' VkLayerDispatchTable *table = &dev_table->core_dispatch;\n' tables += ' table->magic = DEVICE_DISP_TABLE_MAGIC_NUMBER;\n' tables += ' for (uint32_t i = 0; i < MAX_NUM_UNKNOWN_EXTS; i++) dev_table->ext_dispatch[i] = (PFN_vkDevExt)vkDevExtError;\n' elif x == 1: cur_type = 'device' gpa_param = 'dev' commands = self.ext_commands tables += '// Init Device function pointer dispatch table with extension commands\n' tables += 'VKAPI_ATTR void VKAPI_CALL loader_init_device_extension_dispatch_table(struct loader_dev_dispatch_table *dev_table,\n' tables += ' PFN_vkGetInstanceProcAddr gipa,\n' tables += ' PFN_vkGetDeviceProcAddr gdpa,\n' tables += ' VkInstance inst,\n' tables += ' VkDevice dev) {\n' tables += ' VkLayerDispatchTable *table = &dev_table->core_dispatch;\n' tables += ' table->magic = DEVICE_DISP_TABLE_MAGIC_NUMBER;\n' elif x == 2: cur_type = 'instance' gpa_param = 'inst' commands = self.core_commands tables += '// Init Instance function pointer dispatch table with core commands\n' tables += 'VKAPI_ATTR void VKAPI_CALL loader_init_instance_core_dispatch_table(VkLayerInstanceDispatchTable *table, PFN_vkGetInstanceProcAddr gpa,\n' tables += ' VkInstance inst) {\n' else: cur_type = 'instance' gpa_param = 'inst' commands = self.ext_commands tables += '// Init Instance function pointer dispatch table with core commands\n' tables += 'VKAPI_ATTR void VKAPI_CALL loader_init_instance_extension_dispatch_table(VkLayerInstanceDispatchTable *table, PFN_vkGetInstanceProcAddr gpa,\n' tables += ' VkInstance inst) {\n' for cur_cmd in commands: is_inst_handle_type = cur_cmd.handle_type == 'VkInstance' or cur_cmd.handle_type == 'VkPhysicalDevice' if ((cur_type == 'instance' and is_inst_handle_type) or (cur_type == 'device' and not is_inst_handle_type)): if cur_cmd.ext_name != cur_extension_name: if 'VK_VERSION_' in cur_cmd.ext_name: tables += '\n // ---- Core %s commands\n' % cur_cmd.ext_name[11:] else: tables += '\n // ---- %s extension commands\n' % cur_cmd.ext_name cur_extension_name = cur_cmd.ext_name # Remove 'vk' from proto name base_name = cur_cmd.name[2:] # Names to skip if (base_name == 'CreateInstance' or base_name == 'CreateDevice' or base_name == 'EnumerateInstanceExtensionProperties' or base_name == 'EnumerateInstanceLayerProperties' or base_name == 'EnumerateInstanceVersion'): continue if cur_cmd.protect is not None: tables += '#ifdef %s\n' % cur_cmd.protect # If we're looking for the proc we are passing in, just point the table to it. This fixes the issue where # a layer overrides the function name for the loader. if x == 1: if base_name == 'GetDeviceProcAddr': tables += ' table->GetDeviceProcAddr = gdpa;\n' elif cur_cmd.ext_type == 'instance': tables += ' table->%s = (PFN_%s)gipa(inst, "%s");\n' % (base_name, cur_cmd.name, cur_cmd.name) else: tables += ' table->%s = (PFN_%s)gdpa(dev, "%s");\n' % (base_name, cur_cmd.name, cur_cmd.name) elif (x < 1 and base_name == 'GetDeviceProcAddr'): tables += ' table->GetDeviceProcAddr = gpa;\n' elif (x > 1 and base_name == 'GetInstanceProcAddr'): tables += ' table->GetInstanceProcAddr = gpa;\n' else: tables += ' table->%s = (PFN_%s)gpa(%s, "%s");\n' % (base_name, cur_cmd.name, gpa_param, cur_cmd.name) if cur_cmd.protect is not None: tables += '#endif // %s\n' % cur_cmd.protect tables += '}\n\n' return tables # # Create a lookup table function from the appropriate list of entrypoints and # return it as a string def OutputLoaderLookupFunc(self): commands = [] tables = '' cur_type = '' cur_extension_name = '' for x in range(0, 2): if x == 0: cur_type = 'device' tables += '// Device command lookup function\n' tables += 'VKAPI_ATTR void* VKAPI_CALL loader_lookup_device_dispatch_table(const VkLayerDispatchTable *table, const char *name) {\n' tables += ' if (!name || name[0] != \'v\' || name[1] != \'k\') return NULL;\n' tables += '\n' tables += ' name += 2;\n' else: cur_type = 'instance' tables += '// Instance command lookup function\n' tables += 'VKAPI_ATTR void* VKAPI_CALL loader_lookup_instance_dispatch_table(const VkLayerInstanceDispatchTable *table, const char *name,\n' tables += ' bool *found_name) {\n' tables += ' if (!name || name[0] != \'v\' || name[1] != \'k\') {\n' tables += ' *found_name = false;\n' tables += ' return NULL;\n' tables += ' }\n' tables += '\n' tables += ' *found_name = true;\n' tables += ' name += 2;\n' for y in range(0, 2): if y == 0: commands = self.core_commands else: commands = self.ext_commands for cur_cmd in commands: is_inst_handle_type = cur_cmd.handle_type == 'VkInstance' or cur_cmd.handle_type == 'VkPhysicalDevice' if ((cur_type == 'instance' and is_inst_handle_type) or (cur_type == 'device' and not is_inst_handle_type)): if cur_cmd.ext_name != cur_extension_name: if 'VK_VERSION_' in cur_cmd.ext_name: tables += '\n // ---- Core %s commands\n' % cur_cmd.ext_name[11:] else: tables += '\n // ---- %s extension commands\n' % cur_cmd.ext_name cur_extension_name = cur_cmd.ext_name # Remove 'vk' from proto name base_name = cur_cmd.name[2:] if (base_name == 'CreateInstance' or base_name == 'CreateDevice' or base_name == 'EnumerateInstanceExtensionProperties' or base_name == 'EnumerateInstanceLayerProperties' or base_name == 'EnumerateInstanceVersion'): continue if cur_cmd.protect is not None: tables += '#ifdef %s\n' % cur_cmd.protect tables += ' if (!strcmp(name, "%s")) return (void *)table->%s;\n' % (base_name, base_name) if cur_cmd.protect is not None: tables += '#endif // %s\n' % cur_cmd.protect tables += '\n' if x == 1: tables += ' *found_name = false;\n' tables += ' return NULL;\n' tables += '}\n\n' return tables # # Create the appropriate trampoline (and possibly terminator) functions def CreateTrampTermFuncs(self): entries = [] funcs = '' cur_extension_name = '' # Some extensions have to be manually added. Skip those in the automatic # generation. They will be manually added later. manual_ext_commands = ['vkEnumeratePhysicalDeviceGroupsKHR', 'vkGetPhysicalDeviceExternalImageFormatPropertiesNV', 'vkGetPhysicalDeviceFeatures2KHR', 'vkGetPhysicalDeviceProperties2KHR', 'vkGetPhysicalDeviceFormatProperties2KHR', 'vkGetPhysicalDeviceImageFormatProperties2KHR', 'vkGetPhysicalDeviceQueueFamilyProperties2KHR', 'vkGetPhysicalDeviceMemoryProperties2KHR', 'vkGetPhysicalDeviceSparseImageFormatProperties2KHR', 'vkGetPhysicalDeviceSurfaceCapabilities2KHR', 'vkGetPhysicalDeviceSurfaceFormats2KHR', 'vkGetPhysicalDeviceSurfaceCapabilities2EXT', 'vkReleaseDisplayEXT', 'vkAcquireXlibDisplayEXT', 'vkGetRandROutputDisplayEXT', 'vkGetPhysicalDeviceExternalBufferPropertiesKHR', 'vkGetPhysicalDeviceExternalSemaphorePropertiesKHR', 'vkGetPhysicalDeviceExternalFencePropertiesKHR', 'vkGetPhysicalDeviceDisplayProperties2KHR', 'vkGetPhysicalDeviceDisplayPlaneProperties2KHR', 'vkGetDisplayModeProperties2KHR', 'vkGetDisplayPlaneCapabilities2KHR', 'vkGetPhysicalDeviceSurfacePresentModes2EXT', 'vkGetDeviceGroupSurfacePresentModes2EXT', 'vkGetPhysicalDeviceToolPropertiesEXT'] for ext_cmd in self.ext_commands: if (ext_cmd.ext_name in WSI_EXT_NAMES or ext_cmd.ext_name in AVOID_EXT_NAMES or ext_cmd.name in AVOID_CMD_NAMES or ext_cmd.name in manual_ext_commands): continue if ext_cmd.ext_name != cur_extension_name: if 'VK_VERSION_' in ext_cmd.ext_name: funcs += '\n// ---- Core %s trampoline/terminators\n\n' % ext_cmd.ext_name[11:] else: funcs += '\n// ---- %s extension trampoline/terminators\n\n' % ext_cmd.ext_name cur_extension_name = ext_cmd.ext_name if ext_cmd.protect is not None: funcs += '#ifdef %s\n' % ext_cmd.protect func_header = ext_cmd.cdecl.replace(";", " {\n") tramp_header = func_header.replace("VKAPI_CALL vk", "VKAPI_CALL ") return_prefix = ' ' base_name = ext_cmd.name[2:] has_surface = 0 update_structure_surface = 0 update_structure_string = '' requires_terminator = 0 surface_var_name = '' phys_dev_var_name = '' instance_var_name = '' has_return_type = False always_use_param_name = True surface_type_to_replace = '' surface_name_replacement = '' physdev_type_to_replace = '' physdev_name_replacement = '' for param in ext_cmd.params: if param.type == 'VkSurfaceKHR': has_surface = 1 surface_var_name = param.name requires_terminator = 1 always_use_param_name = False surface_type_to_replace = 'VkSurfaceKHR' surface_name_replacement = 'icd_surface->real_icd_surfaces[icd_index]' if param.type == 'VkPhysicalDeviceSurfaceInfo2KHR': has_surface = 1 surface_var_name = param.name + '->surface' requires_terminator = 1 update_structure_surface = 1 update_structure_string = ' VkPhysicalDeviceSurfaceInfo2KHR info_copy = *pSurfaceInfo;\n' update_structure_string += ' info_copy.surface = icd_surface->real_icd_surfaces[icd_index];\n' always_use_param_name = False surface_type_to_replace = 'VkPhysicalDeviceSurfaceInfo2KHR' surface_name_replacement = '&info_copy' if param.type == 'VkPhysicalDevice': requires_terminator = 1 phys_dev_var_name = param.name always_use_param_name = False physdev_type_to_replace = 'VkPhysicalDevice' physdev_name_replacement = 'phys_dev_term->phys_dev' if param.type == 'VkInstance': requires_terminator = 1 instance_var_name = param.name if (ext_cmd.return_type is not None): return_prefix += 'return ' has_return_type = True if (ext_cmd.handle_type == 'VkInstance' or ext_cmd.handle_type == 'VkPhysicalDevice' or 'DebugMarkerSetObject' in ext_cmd.name or 'SetDebugUtilsObject' in ext_cmd.name or ext_cmd.name in DEVICE_CMDS_NEED_TERM): requires_terminator = 1 if requires_terminator == 1: term_header = tramp_header.replace("VKAPI_CALL ", "VKAPI_CALL terminator_") funcs += tramp_header if ext_cmd.handle_type == 'VkPhysicalDevice': funcs += ' const VkLayerInstanceDispatchTable *disp;\n' funcs += ' VkPhysicalDevice unwrapped_phys_dev = loader_unwrap_physical_device(%s);\n' % (phys_dev_var_name) funcs += ' if (VK_NULL_HANDLE == unwrapped_phys_dev) {\n' funcs += ' loader_log(NULL, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,\n' funcs += ' "%s: Invalid %s "\n' % (ext_cmd.name, phys_dev_var_name) funcs += ' "[VUID-%s-%s-parameter]");\n' % (ext_cmd.name, phys_dev_var_name) funcs += ' abort(); /* Intentionally fail so user can correct issue. */\n' funcs += ' }\n' funcs += ' disp = loader_get_instance_layer_dispatch(%s);\n' % (phys_dev_var_name) elif ext_cmd.handle_type == 'VkInstance': funcs += ' struct loader_instance *inst = loader_get_instance(%s);\n' % (instance_var_name) funcs += ' if (NULL == inst) {\n' funcs += ' loader_log(\n' funcs += ' NULL, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,\n' funcs += ' "%s: Invalid instance [VUID-%s-%s-parameter]");\n' % (ext_cmd.name, ext_cmd.name, instance_var_name) funcs += ' abort(); /* Intentionally fail so user can correct issue. */\n' funcs += ' }\n' funcs += '#error("Not implemented. Likely needs to be manually generated!");\n' else: funcs += ' const VkLayerDispatchTable *disp = loader_get_dispatch(' funcs += ext_cmd.params[0].name funcs += ');\n' funcs += ' if (NULL == disp) {\n' funcs += ' loader_log(NULL, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,\n' funcs += ' "%s: Invalid %s "\n' % (ext_cmd.name, ext_cmd.params[0].name) funcs += ' "[VUID-%s-%s-parameter]");\n' % (ext_cmd.name, ext_cmd.params[0].name) funcs += ' abort(); /* Intentionally fail so user can correct issue. */\n' funcs += ' }\n' if 'DebugMarkerSetObjectName' in ext_cmd.name: funcs += ' VkDebugMarkerObjectNameInfoEXT local_name_info;\n' funcs += ' memcpy(&local_name_info, pNameInfo, sizeof(VkDebugMarkerObjectNameInfoEXT));\n' funcs += ' // If this is a physical device, we have to replace it with the proper one for the next call.\n' funcs += ' if (pNameInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT) {\n' funcs += ' struct loader_physical_device_tramp *phys_dev_tramp = (struct loader_physical_device_tramp *)(uintptr_t)pNameInfo->object;\n' funcs += ' local_name_info.object = (uint64_t)(uintptr_t)phys_dev_tramp->phys_dev;\n' funcs += ' }\n' elif 'DebugMarkerSetObjectTag' in ext_cmd.name: funcs += ' VkDebugMarkerObjectTagInfoEXT local_tag_info;\n' funcs += ' memcpy(&local_tag_info, pTagInfo, sizeof(VkDebugMarkerObjectTagInfoEXT));\n' funcs += ' // If this is a physical device, we have to replace it with the proper one for the next call.\n' funcs += ' if (pTagInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT) {\n' funcs += ' struct loader_physical_device_tramp *phys_dev_tramp = (struct loader_physical_device_tramp *)(uintptr_t)pTagInfo->object;\n' funcs += ' local_tag_info.object = (uint64_t)(uintptr_t)phys_dev_tramp->phys_dev;\n' funcs += ' }\n' elif 'SetDebugUtilsObjectName' in ext_cmd.name: funcs += ' VkDebugUtilsObjectNameInfoEXT local_name_info;\n' funcs += ' memcpy(&local_name_info, pNameInfo, sizeof(VkDebugUtilsObjectNameInfoEXT));\n' funcs += ' // If this is a physical device, we have to replace it with the proper one for the next call.\n' funcs += ' if (pNameInfo->objectType == VK_OBJECT_TYPE_PHYSICAL_DEVICE) {\n' funcs += ' struct loader_physical_device_tramp *phys_dev_tramp = (struct loader_physical_device_tramp *)(uintptr_t)pNameInfo->objectHandle;\n' funcs += ' local_name_info.objectHandle = (uint64_t)(uintptr_t)phys_dev_tramp->phys_dev;\n' funcs += ' }\n' elif 'SetDebugUtilsObjectTag' in ext_cmd.name: funcs += ' VkDebugUtilsObjectTagInfoEXT local_tag_info;\n' funcs += ' memcpy(&local_tag_info, pTagInfo, sizeof(VkDebugUtilsObjectTagInfoEXT));\n' funcs += ' // If this is a physical device, we have to replace it with the proper one for the next call.\n' funcs += ' if (pTagInfo->objectType == VK_OBJECT_TYPE_PHYSICAL_DEVICE) {\n' funcs += ' struct loader_physical_device_tramp *phys_dev_tramp = (struct loader_physical_device_tramp *)(uintptr_t)pTagInfo->objectHandle;\n' funcs += ' local_tag_info.objectHandle = (uint64_t)(uintptr_t)phys_dev_tramp->phys_dev;\n' funcs += ' }\n' if ext_cmd.ext_name in NULL_CHECK_EXT_NAMES: funcs += ' if (disp->' + base_name + ' != NULL) {\n' funcs += ' ' funcs += return_prefix if ext_cmd.handle_type == 'VkInstance': funcs += 'inst->' funcs += 'disp->' funcs += base_name funcs += '(' count = 0 for param in ext_cmd.params: if count != 0: funcs += ', ' if param.type == 'VkPhysicalDevice': funcs += 'unwrapped_phys_dev' elif ('DebugMarkerSetObject' in ext_cmd.name or 'SetDebugUtilsObject' in ext_cmd.name) and param.name == 'pNameInfo': funcs += '&local_name_info' elif ('DebugMarkerSetObject' in ext_cmd.name or 'SetDebugUtilsObject' in ext_cmd.name) and param.name == 'pTagInfo': funcs += '&local_tag_info' else: funcs += param.name count += 1 funcs += ');\n' if ext_cmd.ext_name in NULL_CHECK_EXT_NAMES: if ext_cmd.return_type is not None: funcs += ' } else {\n' funcs += ' return VK_SUCCESS;\n' funcs += ' }\n' funcs += '}\n\n' funcs += term_header if ext_cmd.handle_type == 'VkPhysicalDevice': funcs += ' struct loader_physical_device_term *phys_dev_term = (struct loader_physical_device_term *)%s;\n' % (phys_dev_var_name) funcs += ' struct loader_icd_term *icd_term = phys_dev_term->this_icd_term;\n' funcs += ' if (NULL == icd_term->dispatch.' funcs += base_name funcs += ') {\n' funcs += ' loader_log(icd_term->this_instance, VULKAN_LOADER_ERROR_BIT, 0,\n' funcs += ' "ICD associated with VkPhysicalDevice does not support ' funcs += base_name funcs += '");\n' # If this is an instance function taking a physical device (i.e. pre Vulkan 1.1), we need to behave and not crash so return an # error here. if ext_cmd.ext_type =='instance' and has_return_type: funcs += ' return VK_ERROR_INITIALIZATION_FAILED;\n' else: funcs += ' abort(); /* Intentionally fail so user can correct issue. */\n' funcs += ' }\n' if has_surface == 1: funcs += ' VkIcdSurface *icd_surface = (VkIcdSurface *)(%s);\n' % (surface_var_name) funcs += ' uint8_t icd_index = phys_dev_term->icd_index;\n' funcs += ' if (NULL != icd_surface->real_icd_surfaces && NULL != (void *)icd_surface->real_icd_surfaces[icd_index]) {\n' # If there's a structure with a surface, we need to update its internals with the correct surface for the ICD if update_structure_surface == 1: funcs += update_structure_string funcs += ' ' + return_prefix + 'icd_term->dispatch.' funcs += base_name funcs += '(' count = 0 for param in ext_cmd.params: if count != 0: funcs += ', ' if not always_use_param_name: if surface_type_to_replace and surface_type_to_replace == param.type: funcs += surface_name_replacement elif physdev_type_to_replace and physdev_type_to_replace == param.type: funcs += physdev_name_replacement else: funcs += param.name else: funcs += param.name count += 1 funcs += ');\n' if not has_return_type: funcs += ' return;\n' funcs += ' }\n' funcs += return_prefix funcs += 'icd_term->dispatch.' funcs += base_name funcs += '(' count = 0 for param in ext_cmd.params: if count != 0: funcs += ', ' if param.type == 'VkPhysicalDevice': funcs += 'phys_dev_term->phys_dev' else: funcs += param.name count += 1 funcs += ');\n' elif ext_cmd.handle_type == 'VkInstance': funcs += ' struct loader_instance *inst = loader_get_instance(%s);\n' % (instance_var_name) funcs += ' if (NULL == inst) {\n' funcs += ' loader_log(\n' funcs += ' NULL, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,\n' funcs += ' "%s: Invalid instance [VUID-%s-%s-parameter]");\n' % (ext_cmd.name, ext_cmd.name, instance_var_name) funcs += ' abort(); /* Intentionally fail so user can correct issue. */\n' funcs += ' }\n' funcs += '#error("Not implemented. Likely needs to be manually generated!");\n' elif ext_cmd.ext_name in ['VK_EXT_debug_utils', 'VK_EXT_debug_marker']: if ext_cmd.name in ['vkDebugMarkerSetObjectNameEXT', 'vkDebugMarkerSetObjectTagEXT', 'vkSetDebugUtilsObjectNameEXT' , 'vkSetDebugUtilsObjectTagEXT']: is_debug_utils = ext_cmd.ext_name == "VK_EXT_debug_utils" debug_struct_name = ext_cmd.params[1].name local_struct = 'local_name_info' if 'ObjectName' in ext_cmd.name else 'local_tag_info' member_name = 'objectHandle' if is_debug_utils else 'object' phys_dev_check = 'VK_OBJECT_TYPE_PHYSICAL_DEVICE' if is_debug_utils else 'VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT' surf_check = 'VK_OBJECT_TYPE_SURFACE_KHR' if is_debug_utils else 'VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT' funcs += ' uint32_t icd_index = 0;\n' funcs += ' struct loader_device *dev;\n' funcs += f' struct loader_icd_term *icd_term = loader_get_icd_and_device({ ext_cmd.params[0].name}, &dev, &icd_index);\n' funcs += f' if (NULL == icd_term || NULL == dev) {{\n' funcs += f' loader_log(NULL, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, "{ext_cmd.name[2:]}: Invalid device handle");\n' funcs += ' abort(); /* Intentionally fail so user can correct issue. */\n' funcs += ' }\n' funcs += f' { ext_cmd.params[1].type} {local_struct};\n' funcs += f' memcpy(&{local_struct}, {debug_struct_name}, sizeof({ ext_cmd.params[1].type}));\n' funcs += ' // If this is a physical device, we have to replace it with the proper one for the next call.\n' funcs += f' if ({debug_struct_name}->objectType == {phys_dev_check}) {{\n' funcs += f' struct loader_physical_device_term *phys_dev_term = (struct loader_physical_device_term *)(uintptr_t){debug_struct_name}->{member_name};\n' funcs += f' {local_struct}.{member_name} = (uint64_t)(uintptr_t)phys_dev_term->phys_dev;\n' funcs += ' // If this is a KHR_surface, and the ICD has created its own, we have to replace it with the proper one for the next call.\n' funcs += f' }} else if ({debug_struct_name}->objectType == {surf_check}) {{\n' funcs += ' if (NULL != dev && NULL != dev->loader_dispatch.core_dispatch.CreateSwapchainKHR) {\n' funcs += f' VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t){debug_struct_name}->{member_name};\n' funcs += ' if (NULL != icd_surface->real_icd_surfaces) {\n' funcs += f' {local_struct}.{member_name} = (uint64_t)icd_surface->real_icd_surfaces[icd_index];\n' funcs += ' }\n' funcs += ' }\n' funcs += ' }\n' funcs += ' // Exit early if the driver does not support the function - this can happen as a layer or the loader itself supports\n' funcs += ' // debug utils but the driver does not.\n' funcs += f' if (NULL == dev->loader_dispatch.extension_terminator_dispatch.{ext_cmd.name[2:]})\n return VK_SUCCESS;\n' dispatch = 'dev->loader_dispatch.' else: funcs += f' struct loader_dev_dispatch_table *dispatch_table = loader_get_dev_dispatch({ext_cmd.params[0].name});\n' funcs += f' if (NULL == dispatch_table) {{\n' funcs += f' loader_log(NULL, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, "{ext_cmd.ext_name}: Invalid device handle");\n' funcs += ' abort(); /* Intentionally fail so user can correct issue. */\n' funcs += ' }\n' funcs += ' // Only call down if the device supports the function\n' funcs += f' if (NULL != dispatch_table->extension_terminator_dispatch.{base_name})\n ' dispatch = 'dispatch_table->' funcs += ' ' if has_return_type: funcs += 'return ' funcs += f'{dispatch}extension_terminator_dispatch.{base_name}(' count = 0 for param in ext_cmd.params: if count != 0: funcs += ', ' if param.type == 'VkPhysicalDevice': funcs += 'phys_dev_term->phys_dev' elif param.type == 'VkSurfaceKHR': funcs += 'icd_surface->real_icd_surfaces[icd_index]' elif ('DebugMarkerSetObject' in ext_cmd.name or 'SetDebugUtilsObject' in ext_cmd.name) and param.name == 'pNameInfo': funcs += '&local_name_info' elif ('DebugMarkerSetObject' in ext_cmd.name or 'SetDebugUtilsObject' in ext_cmd.name) and param.name == 'pTagInfo': funcs += '&local_tag_info' else: funcs += param.name count += 1 funcs += ');\n' else: funcs += '#error("Unknown error path!");\n' funcs += '}\n\n' else: funcs += tramp_header funcs += ' const VkLayerDispatchTable *disp = loader_get_dispatch(' funcs += ext_cmd.params[0].name funcs += ');\n' funcs += ' if (NULL == disp) {\n' funcs += ' loader_log(NULL, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,\n' funcs += ' "%s: Invalid %s "\n' % (ext_cmd.name, ext_cmd.params[0].name) funcs += ' "[VUID-%s-%s-parameter]");\n' % (ext_cmd.name, ext_cmd.params[0].name) funcs += ' abort(); /* Intentionally fail so user can correct issue. */\n' funcs += ' }\n' if ext_cmd.ext_name in NULL_CHECK_EXT_NAMES: funcs += ' if (disp->' + base_name + ' != NULL) {\n' funcs += ' ' funcs += return_prefix funcs += 'disp->' funcs += base_name funcs += '(' count = 0 for param in ext_cmd.params: if count != 0: funcs += ', ' funcs += param.name count += 1 funcs += ');\n' if ext_cmd.ext_name in NULL_CHECK_EXT_NAMES: if ext_cmd.return_type is not None: funcs += ' } else {\n' funcs += ' return VK_SUCCESS;\n' funcs += ' }\n' funcs += '}\n\n' if ext_cmd.protect is not None: funcs += '#endif // %s\n' % ext_cmd.protect return funcs # # Create a function for the extension GPA call def InstExtensionGPA(self): entries = [] gpa_func = '' cur_extension_name = '' gpa_func += '// GPA helpers for extensions\n' gpa_func += 'bool extension_instance_gpa(struct loader_instance *ptr_instance, const char *name, void **addr) {\n' gpa_func += ' *addr = NULL;\n\n' for cur_cmd in self.ext_commands: if ('VK_VERSION_' in cur_cmd.ext_name or cur_cmd.ext_name in WSI_EXT_NAMES or cur_cmd.ext_name in AVOID_EXT_NAMES or cur_cmd.name in AVOID_CMD_NAMES ): continue if cur_cmd.ext_name != cur_extension_name: gpa_func += '\n // ---- %s extension commands\n' % cur_cmd.ext_name cur_extension_name = cur_cmd.ext_name if cur_cmd.protect is not None: gpa_func += '#ifdef %s\n' % cur_cmd.protect #base_name = cur_cmd.name[2:] base_name = SHARED_ALIASES[cur_cmd.name] if cur_cmd.name in SHARED_ALIASES else cur_cmd.name[2:] if (cur_cmd.ext_type == 'instance'): gpa_func += ' if (!strcmp("%s", name)) {\n' % (cur_cmd.name) gpa_func += ' *addr = (ptr_instance->enabled_known_extensions.' gpa_func += cur_cmd.ext_name[3:].lower() gpa_func += ' == 1)\n' gpa_func += ' ? (void *)%s\n' % (base_name) gpa_func += ' : NULL;\n' gpa_func += ' return true;\n' gpa_func += ' }\n' else: gpa_func += ' if (!strcmp("%s", name)) {\n' % (cur_cmd.name) gpa_func += ' *addr = (void *)%s;\n' % (base_name) gpa_func += ' return true;\n' gpa_func += ' }\n' if cur_cmd.protect is not None: gpa_func += '#endif // %s\n' % cur_cmd.protect gpa_func += ' return false;\n' gpa_func += '}\n\n' return gpa_func # # Create the extension name init function def InstantExtensionCreate(self): entries = [] entries = self.instanceExtensions count = 0 cur_extension_name = '' create_func = '' create_func += '// A function that can be used to query enabled extensions during a vkCreateInstance call\n' create_func += 'void extensions_create_instance(struct loader_instance *ptr_instance, const VkInstanceCreateInfo *pCreateInfo) {\n' create_func += ' for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {\n' for ext in entries: if ('VK_VERSION_' in ext.name or ext.name in WSI_EXT_NAMES or ext.name in AVOID_EXT_NAMES or ext.name in AVOID_CMD_NAMES or ext.type == 'device' or ext.num_commands == 0): continue if ext.name != cur_extension_name: create_func += '\n // ---- %s extension commands\n' % ext.name cur_extension_name = ext.name if ext.protect is not None: create_func += '#ifdef %s\n' % ext.protect if count == 0: create_func += ' if (0 == strcmp(pCreateInfo->ppEnabledExtensionNames[i], ' else: create_func += ' } else if (0 == strcmp(pCreateInfo->ppEnabledExtensionNames[i], ' create_func += ext.define + ')) {\n' create_func += ' ptr_instance->enabled_known_extensions.' create_func += ext.name[3:].lower() create_func += ' = 1;\n' if ext.protect is not None: create_func += '#endif // %s\n' % ext.protect count += 1 create_func += ' }\n' create_func += ' }\n' create_func += '}\n\n' return create_func # # Create code to initialize a dispatch table from the appropriate list of # extension entrypoints and return it as a string def DeviceExtensionGetTerminator(self): term_func = '' term_func += '// Some device commands still need a terminator because the loader needs to unwrap something about them.\n' term_func += '// In many cases, the item needing unwrapping is a VkPhysicalDevice or VkSurfaceKHR object. But there may be other items\n' term_func += '// in the future.\n' term_func += 'PFN_vkVoidFunction get_extension_device_proc_terminator(struct loader_device *dev, const char *name, bool* found_name) {\n' term_func += ''' *found_name = false; if (!name || name[0] != 'v' || name[1] != 'k') { return NULL; } name += 2; ''' last_protect = None last_ext = None for ext_cmd in self.ext_commands: if ext_cmd.name in DEVICE_CMDS_NEED_TERM: if 'VK_VERSION_' in ext_cmd.ext_name: term_func += f' // ---- Core {ext_cmd.ext_name[11:]} commands\n' else: last_protect = ext_cmd.protect if ext_cmd.protect is not None: term_func += f'#ifdef {ext_cmd.protect}\n' if (last_ext != ext_cmd.ext_name): term_func += f' // ---- {ext_cmd.ext_name} extension commands\n' last_ext = ext_cmd.ext_name term_func += f' if (!strcmp(name, "{ext_cmd.name[2:]}")) {{\n' term_func += f' *found_name = true;\n' if ext_cmd.require: term_func += f' return dev->extensions.{ext_cmd.ext_name[3:].lower()}_enabled && dev->extensions.{ext_cmd.require[3:].lower()}_enabled ?\n' else: term_func += f' return dev->extensions.{ext_cmd.ext_name[3:].lower()}_enabled ?\n' term_func += f' (PFN_vkVoidFunction)terminator_{(ext_cmd.name[2:])} : NULL;\n' term_func += f' }}\n' if last_protect is not None: term_func += '#endif // %s\n' % ext_cmd.protect term_func += ' return NULL;\n' term_func += '}\n\n' return term_func # # Create a dispatch table solely for device functions which have custom terminators def OutputDeviceFunctionTerminatorDispatchTable(self): term_func = '' term_func += '// Functions that required a terminator need to have a separate dispatch table which contains their corresponding\n' term_func += '// device function. This is used in the terminators themselves.\n' term_func += 'struct loader_device_terminator_dispatch {\n' last_protect = None last_ext = None for ext_cmd in self.ext_commands: if ext_cmd.name in DEVICE_CMDS_NEED_TERM: if 'VK_VERSION_' in ext_cmd.ext_name: term_func += f' // ---- Core {ext_cmd.ext_name[11:]} commands\n' else: last_protect = ext_cmd.protect if ext_cmd.protect is not None: term_func += f'#ifdef {ext_cmd.protect}\n' if (last_ext != ext_cmd.ext_name): term_func += f' // ---- {ext_cmd.ext_name} extension commands\n' last_ext = ext_cmd.ext_name term_func += f' PFN_{ext_cmd.name} {ext_cmd.name[2:]};\n' if last_protect is not None: term_func += '#endif // %s\n' % ext_cmd.protect term_func += '}; \n\n' return term_func # # Create code to initialize a dispatch table from the appropriate list of # extension entrypoints and return it as a string def InitDeviceFunctionTerminatorDispatchTable(self): term_func = '' term_func += '// Functions that required a terminator need to have a separate dispatch table which contains their corresponding\n' term_func += '// device function. This is used in the terminators themselves.\n' term_func += 'void init_extension_device_proc_terminator_dispatch(struct loader_device *dev) {\n' term_func += ' struct loader_device_terminator_dispatch* dispatch = &dev->loader_dispatch.extension_terminator_dispatch;\n' term_func += ' PFN_vkGetDeviceProcAddr gpda = (PFN_vkGetDeviceProcAddr)dev->phys_dev_term->this_icd_term->dispatch.GetDeviceProcAddr;\n' last_protect = None last_ext = None for ext_cmd in self.ext_commands: if ext_cmd.name in DEVICE_CMDS_NEED_TERM: if 'VK_VERSION_' in ext_cmd.ext_name: term_func += f' // ---- Core {ext_cmd.ext_name[11:]} commands\n' else: last_protect = ext_cmd.protect if ext_cmd.protect is not None: term_func += f'#ifdef {ext_cmd.protect}\n' if (last_ext != ext_cmd.ext_name): term_func += f' // ---- {ext_cmd.ext_name} extension commands\n' last_ext = ext_cmd.ext_name if ext_cmd.require: term_func += f' if (dev->extensions.{ext_cmd.ext_name[3:].lower()}_enabled && dev->extensions.{ext_cmd.require[3:].lower()}_enabled)\n' term_func += f' dispatch->{ext_cmd.name[2:]} = (PFN_{(ext_cmd.name)})gpda(dev->icd_device, "{(ext_cmd.name)}");\n' else: term_func += f' if (dev->extensions.{ext_cmd.ext_name[3:].lower()}_enabled)\n' term_func += f' dispatch->{ext_cmd.name[2:]} = (PFN_{(ext_cmd.name)})gpda(dev->icd_device, "{(ext_cmd.name)}");\n' if last_protect is not None: term_func += '#endif // %s\n' % ext_cmd.protect term_func += '}\n\n' return term_func # # Create code to initialize a dispatch table from the appropriate list of # core and extension entrypoints and return it as a string def InitInstLoaderExtensionDispatchTable(self): commands = [] table = '' cur_extension_name = '' table += '// This table contains the loader\'s instance dispatch table, which contains\n' table += '// default functions if no instance layers are activated. This contains\n' table += '// pointers to "terminator functions".\n' table += 'const VkLayerInstanceDispatchTable instance_disp = {\n' for x in range(0, 2): if x == 0: commands = self.core_commands else: commands = self.ext_commands for cur_cmd in commands: if cur_cmd.handle_type == 'VkInstance' or cur_cmd.handle_type == 'VkPhysicalDevice': if cur_cmd.ext_name != cur_extension_name: if 'VK_VERSION_' in cur_cmd.ext_name: table += '\n // ---- Core %s commands\n' % cur_cmd.ext_name[11:] else: table += '\n // ---- %s extension commands\n' % cur_cmd.ext_name cur_extension_name = cur_cmd.ext_name # Remove 'vk' from proto name base_name = cur_cmd.name[2:] aliased_name = SHARED_ALIASES[cur_cmd.name][2:] if cur_cmd.name in SHARED_ALIASES else base_name if (base_name == 'CreateInstance' or base_name == 'CreateDevice' or base_name == 'EnumerateInstanceExtensionProperties' or base_name == 'EnumerateInstanceLayerProperties' or base_name == 'EnumerateInstanceVersion'): continue if cur_cmd.protect is not None: table += '#ifdef %s\n' % cur_cmd.protect if base_name == 'GetInstanceProcAddr': table += ' .%s = %s,\n' % (base_name, cur_cmd.name) else: table += ' .%s = terminator_%s,\n' % (base_name, aliased_name) if cur_cmd.protect is not None: table += '#endif // %s\n' % cur_cmd.protect table += '};\n\n' return table # # Create the extension name whitelist array def OutputInstantExtensionWhitelistArray(self): extensions = self.instanceExtensions table = '' table += '// A null-terminated list of all of the instance extensions supported by the loader.\n' table += '// If an instance extension name is not in this list, but it is exported by one or more of the\n' table += '// ICDs detected by the loader, then the extension name not in the list will be filtered out\n' table += '// before passing the list of extensions to the application.\n' table += 'const char *const LOADER_INSTANCE_EXTENSIONS[] = {\n' for ext in extensions: if ext.type == 'device' or 'VK_VERSION_' in ext.name: continue if ext.protect is not None: table += '#ifdef %s\n' % ext.protect table += ' ' table += ext.define + ',\n' if ext.protect is not None: table += '#endif // %s\n' % ext.protect table += ' NULL };\n' return table