blob: 6523795c874bd1e5cf87e25a0de2561c35f304f6 [file]
#!/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.
# Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# Copyright (c) 2023-2023 RasterGrid Kft.
#
# 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 <marky@lunarg.com>
# Author: Mark Lobodzinski <mark@lunarg.com>
# Author: Charles Giessen <charles@lunarg.com>
import re
import os
from base_generator import BaseGenerator
from vulkan_object import Version
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']
DEVICE_CMDS_MUST_USE_TRAMP = ['vkSetDebugUtilsObjectNameEXT',
'vkSetDebugUtilsObjectTagEXT',
'vkDebugMarkerSetObjectNameEXT',
'vkDebugMarkerSetObjectTagEXT']
# 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']
# This class is a container for any source code, data, or other behavior that is necessary to
# customize the generator script for a specific target API variant (e.g. Vulkan SC). As such,
# all of these API-specific interfaces and their use in the generator script are part of the
# contract between this repository and its downstream users. Changing or removing any of these
# interfaces or their use in the generator script will have downstream effects and thus
# should be avoided unless absolutely necessary.
class APISpecific:
@staticmethod
def getApiVersionDisplayName(targetApiName: str, name: str) -> str:
match targetApiName:
case 'vulkan':
api_name = 'Vulkan'
version_number = name[name.find('_VERSION_') + len('_VERSION_'):].replace('_', '.')
return f'{api_name} {version_number}'
@staticmethod
def getFirstVersionName(targetApiName: str) -> str:
match targetApiName:
case 'vulkan':
return 'VK_VERSION_1_0'
# This handles the dependency across versions of APIs, even across API variants
# (e.g. maps VK_VERSION_1_2 to VKSC_VERSION_1_0 for Vulkan SC).
# This handles the lack of proper support for the "depends" attribute of <feature> tags in the XML.
@staticmethod
def getEffectiveVersionName(targetApiName: str, version: Version) -> str:
if version is None:
return APISpecific.getFirstVersionName(targetApiName)
match targetApiName:
case 'vulkan':
return version.name
class LoaderExtensionGenerator(BaseGenerator):
def __init__(self):
BaseGenerator.__init__(self)
self.core_commands = []
self.extension_commands = []
self.instance_extensions = []
def generate(self):
self.core_commands = [x for x in self.vk.commands.values() if len(x.extensions) == 0]
self.extension_commands = [x for x in self.vk.commands.values() if len(x.extensions) > 0]
self.instance_extensions = [x for x in self.vk.extensions.values() if x.instance]
out = []
self.add_preamble(out)
if self.filename == 'vk_loader_extensions.h':
self.print_vk_loader_extensions_h(out)
elif self.filename == 'vk_loader_extensions.c':
self.print_vk_loader_extensions_c(out)
elif self.filename == 'vk_layer_dispatch_table.h':
self.print_vk_layer_dispatch_table(out)
out.append('// clang-format on')
self.write(''.join(out))
def add_preamble(self, out):
out.append(f'''// *** THIS FILE IS GENERATED - DO NOT EDIT ***
// See {os.path.basename(__file__)} for modifications
/*
* Copyright (c) 2015-2025 The Khronos Group Inc.
* Copyright (c) 2015-2025 Valve Corporation
* Copyright (c) 2015-2025 LunarG, Inc.
* Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* Copyright (c) 2023-2023 RasterGrid Kft.
*
* 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 Lobodzinski <mark@lunarg.com>
* Author: Mark Young <marky@lunarg.com>
* Author: Charles Giessen <charles@lunarg.com>
*/
// clang-format off
''')
def print_vk_loader_extensions_h(self, out):
out.append('#pragma once\n')
out.append('\n')
out.append('#include <stdbool.h>\n')
out.append('#include <vulkan/vulkan.h>\n')
out.append('#include <vulkan/vk_layer.h>\n')
out.append('#include "vk_layer_dispatch_table.h"\n')
out.append('\n\n')
self.OutputPrototypesInHeader(out)
self.OutputLoaderTerminators(out)
self.OutputIcdDispatchTable(out)
self.OutputIcdExtensionEnableUnion(out)
self.OutputDeviceFunctionTerminatorDispatchTable(out)
def print_vk_loader_extensions_c(self, out):
out.append('#include <stdio.h>\n')
out.append('#include <stdlib.h>\n')
out.append('#include <string.h>\n')
out.append('#include "loader.h"\n')
out.append('#include "vk_loader_extensions.h"\n')
out.append('#include <vulkan/vk_icd.h>\n')
out.append('#include "wsi.h"\n')
out.append('#include "debug_utils.h"\n')
out.append('#include "extension_manual.h"\n')
self.OutputUtilitiesInSource(out)
self.OutputIcdDispatchTableInit(out)
self.OutputLoaderDispatchTables(out)
self.InitDeviceFunctionTerminatorDispatchTable(out)
self.OutputDeviceFunctionTrampolinePrototypes(out)
self.OutputLoaderLookupFunc(out)
self.CreateTrampTermFuncs(out)
self.InstExtensionGPA(out)
self.InstantExtensionCreate(out)
self.DeviceExtensionGetTerminator(out)
self.InitInstLoaderExtensionDispatchTable(out)
self.OutputInstantExtensionWhitelistArray(out)
def print_vk_layer_dispatch_table(self, out):
out.append('#pragma once\n')
out.append('\n')
out.append('#include <vulkan/vulkan.h>\n')
out.append('#include <vulkan/vk_layer.h>\n')
out.append('\n')
self.OutputLayerInstanceDispatchTable(out)
self.OutputLayerDeviceDispatchTable(out)
def DescribeBlock(self, command, current_block, out, custom_commands_string = ' commands', indent = ' '):
effective_version_name = APISpecific.getEffectiveVersionName(self.targetApiName, command.version)
if command.extensions != current_block and effective_version_name != current_block:
if effective_version_name and len(command.extensions) == 0:
# Entry points that have an effective version in the target API
api_version_display_name = APISpecific.getApiVersionDisplayName(self.targetApiName, effective_version_name)
out.append(f"\n{indent}// ---- Core {api_version_display_name}{custom_commands_string}\n")
return effective_version_name
else:
# don't repeat unless the first extension is different (while rest can vary)
if not isinstance(current_block, list) or current_block[0] != command.extensions[0]:
out.append(f"\n{indent}// ---- {command.extensions[0] if len(command.extensions) > 0 else ''} extension{custom_commands_string}\n")
return command.extensions
else:
return current_block
def OutputPrototypesInHeader(self, out: list):
out.append('''// Structures defined externally, but used here
struct loader_instance;
struct loader_device;
struct loader_icd_term;
struct loader_dev_dispatch_table;
// Device extension error function
VKAPI_ATTR VkResult VKAPI_CALL vkDevExtError(VkDevice dev);
// Extension interception for vkGetInstanceProcAddr function, so we can return
// the appropriate information for any instance extensions we know about.
bool extension_instance_gpa(struct loader_instance *ptr_instance, const char *name, void **addr);
struct loader_instance_extension_enable_list; // Forward declaration
// Extension interception for vkCreateInstance function, so we can properly
// detect and enable any instance extension information for extensions we know
// about.
void fill_out_enabled_instance_extensions(uint32_t extension_count, const char *const * extension_list, struct loader_instance_extension_enable_list* enables);
// Extension interception for vkGetDeviceProcAddr function, so we can return
// an appropriate terminator if this is one of those few device commands requiring
// a terminator.
PFN_vkVoidFunction get_extension_device_proc_terminator(struct loader_device *dev, const char *name, bool* found_name);
// Dispatch table properly filled in with appropriate terminators for the
// supported extensions.
extern const VkLayerInstanceDispatchTable instance_disp;
// Array of extension strings for instance extensions we support.
extern const char *const LOADER_INSTANCE_EXTENSIONS[];
VKAPI_ATTR bool VKAPI_CALL loader_icd_init_entries(struct loader_instance* inst, struct loader_icd_term *icd_term);
// Init Device function pointer dispatch table with core commands
VKAPI_ATTR void VKAPI_CALL loader_init_device_dispatch_table(struct loader_dev_dispatch_table *dev_table, PFN_vkGetDeviceProcAddr gpa,
VkDevice dev);
// Init Device function pointer dispatch table with extension commands
VKAPI_ATTR void VKAPI_CALL loader_init_device_extension_dispatch_table(struct loader_dev_dispatch_table *dev_table,
PFN_vkGetInstanceProcAddr gipa,
PFN_vkGetDeviceProcAddr gdpa,
VkInstance inst,
VkDevice dev);
// Init Instance function pointer dispatch table with core commands
VKAPI_ATTR void VKAPI_CALL loader_init_instance_core_dispatch_table(VkLayerInstanceDispatchTable *table, PFN_vkGetInstanceProcAddr gpa,
VkInstance inst);
// Init Instance function pointer dispatch table with core commands
VKAPI_ATTR void VKAPI_CALL loader_init_instance_extension_dispatch_table(VkLayerInstanceDispatchTable *table, PFN_vkGetInstanceProcAddr gpa,
VkInstance inst);
// Device command lookup function
VKAPI_ATTR void* VKAPI_CALL loader_lookup_device_dispatch_table(const VkLayerDispatchTable *table, const char *name, bool* name_found);
// Instance command lookup function
VKAPI_ATTR void* VKAPI_CALL loader_lookup_instance_dispatch_table(const VkLayerInstanceDispatchTable *table, const char *name,
bool *found_name);
''')
# Creates the prototypes for the loader's core instance command terminators
def OutputLoaderTerminators(self, out):
out.append('// Loader core instance terminators\n')
for command in [x for x in self.vk.commands.values() if len(x.extensions) == 0]:
if not (command.name in ADD_INST_CMDS) and not (command.params[0].type in ['VkInstance', 'VkPhysicalDevice']):
continue
mod_string = ''
new_terminator = command.cPrototype
mod_string = new_terminator.replace("VKAPI_CALL vk", "VKAPI_CALL terminator_")
if command.name in PRE_INSTANCE_FUNCTIONS:
pre_instance_basic_version = mod_string
mod_string = mod_string.replace("terminator_", "terminator_pre_instance_")
mod_string = mod_string.replace(command.name[2:] + '(\n', command.name[2:] + '(\n const Vk' + command.name[2:] + 'Chain* chain,\n')
if command.protect is not None:
out.append(f'#if defined({command.protect})\n')
if command.name in PRE_INSTANCE_FUNCTIONS:
out.append(pre_instance_basic_version)
out.append('\n')
out.append(mod_string)
out.append('\n')
if command.protect is not None:
out.append(f'#endif // {command.protect}\n')
out.append('\n')
# Create a dispatch table from the appropriate list
def OutputIcdDispatchTable(self, out):
skip_commands = ['vkGetInstanceProcAddr',
'vkEnumerateDeviceLayerProperties',
]
out.append('// ICD function pointer dispatch table\n')
out.append('struct loader_icd_term_dispatch {\n')
current_block = ''
for command in self.vk.commands.values():
if (command.name in skip_commands or command.device )and command.name != 'vkGetDeviceProcAddr':
continue
current_block = self.DescribeBlock(command, current_block, out)
if command.protect:
out.append(f'#if defined({command.protect})\n')
out.append(f' PFN_{command.name} {command.name[2:]};\n')
if command.protect:
out.append(f'#endif // {command.protect}\n')
out.append('};\n\n')
# Create the extension enable union
def OutputIcdExtensionEnableUnion(self, out):
out.append( 'struct loader_instance_extension_enable_list {\n')
for extension in self.instance_extensions:
if extension.protect:
out.append(f'#if defined({extension.protect})\n')
out.append( f' uint8_t {extension.name[3:].lower()};\n')
if extension.protect:
out.append(f'#endif // defined({extension.protect})\n')
out.append( '};\n\n')
# Create a dispatch table solely for device functions which have custom terminators
def OutputDeviceFunctionTerminatorDispatchTable(self, out):
out.append('// Functions that required a terminator need to have a separate dispatch table which contains their corresponding\n')
out.append('// device function. This is used in the terminators themselves.\n')
out.append('struct loader_device_terminator_dispatch {\n')
# last_protect = None
current_block = ''
for command in self.vk.commands.values():
if len(command.extensions) == 0:
continue
if command.name in DEVICE_CMDS_NEED_TERM:
if command.protect is not None:
out.append( f'#if defined({command.protect})\n')
current_block = self.DescribeBlock(command, current_block, out)
out.append( f' PFN_{command.name} {command.name[2:]};\n')
if command.protect is not None:
out.append( f'#endif // {command.protect}\n')
out.append( '};\n\n')
# Create a layer instance dispatch table from the appropriate list
def OutputLayerInstanceDispatchTable(self, out):
out.append('// Instance function pointer dispatch table\n')
out.append('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.
out.append(' // Manually add in GetPhysicalDeviceProcAddr entry\n')
out.append(' PFN_GetPhysicalDeviceProcAddr GetPhysicalDeviceProcAddr;\n')
current_block = ''
for command_name, command in self.vk.commands.items():
if not command.instance:
continue
current_block = self.DescribeBlock(command, current_block, out)
if command.protect:
out.append(f'#if defined({command.protect})\n')
out.append(f' PFN_{command_name} {command_name[2:]};\n')
if command.protect:
out.append(f'#endif // {command.protect}\n')
out.append('} VkLayerInstanceDispatchTable;\n\n')
# Create a layer device dispatch table from the appropriate list
def OutputLayerDeviceDispatchTable(self, out):
out.append('// Device function pointer dispatch table\n')
out.append('#define DEVICE_DISP_TABLE_MAGIC_NUMBER 0x10ADED040410ADEDUL\n')
out.append('typedef struct VkLayerDispatchTable_ {\n')
out.append(' uint64_t magic; // Should be DEVICE_DISP_TABLE_MAGIC_NUMBER\n')
current_block = ''
for command in [x for x in self.vk.commands.values() if x.device]:
current_block = self.DescribeBlock(command, current_block, out)
if command.protect:
out.append(f'#if defined({command.protect})\n')
out.append(f' PFN_{command.name} {command.name[2:]};\n')
if command.protect:
out.append(f'#endif // {command.protect}\n')
out.append('} VkLayerDispatchTable;\n\n')
def OutputUtilitiesInSource(self, out):
out.append('''
// Device extension error function
VKAPI_ATTR VkResult VKAPI_CALL vkDevExtError(VkDevice dev) {
struct loader_device *found_dev;
// The device going in is a trampoline device
struct loader_icd_term *icd_term = loader_get_icd_and_device(dev, &found_dev);
if (icd_term)
loader_log(icd_term->this_instance, VULKAN_LOADER_ERROR_BIT, 0,
"Bad destination in loader trampoline dispatch,"
"Are layers and extensions that you are calling enabled?");
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
''')
# Init a dispatch table from the appropriate list
def OutputIcdDispatchTableInit(self, out):
out.append('VKAPI_ATTR bool VKAPI_CALL loader_icd_init_entries(struct loader_instance* inst, struct loader_icd_term *icd_term) {\n')
out.append(' const PFN_vkGetInstanceProcAddr fp_gipa = icd_term->scanned_icd->GetInstanceProcAddr;\n')
out.append('\n')
out.append('#define LOOKUP_GIPA(func) icd_term->dispatch.func = (PFN_vk##func)fp_gipa(icd_term->instance, "vk" #func);\n')
out.append('\n')
out.append('#define LOOKUP_REQUIRED_GIPA(func) \\\n')
out.append(' do { \\\n')
out.append(' LOOKUP_GIPA(func); \\\n')
out.append(' if (!icd_term->dispatch.func) { \\\n')
out.append(' loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Unable to load %s from ICD %s",\\\n')
out.append(' "vk"#func, icd_term->scanned_icd->lib_name); \\\n')
out.append(' return false; \\\n')
out.append(' } \\\n')
out.append(' } while (0)\n')
out.append('\n')
skip_gipa_commands = ['vkGetInstanceProcAddr',
'vkEnumerateDeviceLayerProperties',
'vkCreateInstance',
'vkEnumerateInstanceExtensionProperties',
'vkEnumerateInstanceLayerProperties',
'vkEnumerateInstanceVersion',
]
current_block = ''
for command in [x for x in self.vk.commands.values() if x.instance or x.name == 'vkGetDeviceProcAddr']:
if command.name in skip_gipa_commands:
continue
custom_commands_string= ' commands' if len(command.extensions) > 0 else ''
current_block = self.DescribeBlock(command, current_block, out, custom_commands_string=custom_commands_string)
if command.protect is not None:
out.append( f'#if defined({command.protect})\n')
effective_version_name = APISpecific.getEffectiveVersionName(self.targetApiName, command.version)
if effective_version_name == APISpecific.getFirstVersionName(self.targetApiName) and len(command.extensions) == 0:
# 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
out.append(f' LOOKUP_REQUIRED_GIPA({command.name[2:]});\n')
else:
out.append( f' LOOKUP_GIPA({command.name[2:]});\n')
if command.protect is not None:
out.append(f'#endif // {command.protect}\n')
out.append('\n')
out.append('#undef LOOKUP_REQUIRED_GIPA\n')
out.append('#undef LOOKUP_GIPA\n')
out.append('\n')
out.append(' return true;\n')
out.append('};\n\n')
# Creates code to initialize the various dispatch tables
def OutputLoaderDispatchTables(self, out):
commands = []
gpa_param = ''
cur_type = ''
for x in range(0, 4):
if x == 0:
cur_type = 'device'
gpa_param = 'dev'
commands = self.core_commands
out.append('// Init Device function pointer dispatch table with core commands\n')
out.append('VKAPI_ATTR void VKAPI_CALL loader_init_device_dispatch_table(struct loader_dev_dispatch_table *dev_table, PFN_vkGetDeviceProcAddr gpa,\n')
out.append(' VkDevice dev) {\n')
out.append(' VkLayerDispatchTable *table = &dev_table->core_dispatch;\n')
out.append(' if (table->magic != DEVICE_DISP_TABLE_MAGIC_NUMBER) { abort(); }\n')
out.append(' 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.extension_commands
out.append('// Init Device function pointer dispatch table with extension commands\n')
out.append('VKAPI_ATTR void VKAPI_CALL loader_init_device_extension_dispatch_table(struct loader_dev_dispatch_table *dev_table,\n')
out.append(' PFN_vkGetInstanceProcAddr gipa,\n')
out.append(' PFN_vkGetDeviceProcAddr gdpa,\n')
out.append(' VkInstance inst,\n')
out.append(' VkDevice dev) {\n')
out.append(' VkLayerDispatchTable *table = &dev_table->core_dispatch;\n')
out.append(' table->magic = DEVICE_DISP_TABLE_MAGIC_NUMBER;\n')
elif x == 2:
cur_type = 'instance'
gpa_param = 'inst'
commands = self.core_commands
out.append('// Init Instance function pointer dispatch table with core commands\n')
out.append('VKAPI_ATTR void VKAPI_CALL loader_init_instance_core_dispatch_table(VkLayerInstanceDispatchTable *table, PFN_vkGetInstanceProcAddr gpa,\n')
out.append(' VkInstance inst) {\n')
else:
cur_type = 'instance'
gpa_param = 'inst'
commands = self.extension_commands
out.append('// Init Instance function pointer dispatch table with core commands\n')
out.append('VKAPI_ATTR void VKAPI_CALL loader_init_instance_extension_dispatch_table(VkLayerInstanceDispatchTable *table, PFN_vkGetInstanceProcAddr gpa,\n')
out.append(' VkInstance inst) {\n')
current_block = ''
for command in commands:
is_inst_handle_type = command.params[0].type in ['VkInstance', 'VkPhysicalDevice']
if ((cur_type == 'instance' and is_inst_handle_type) or (cur_type == 'device' and not is_inst_handle_type)):
current_block = self.DescribeBlock(command, current_block, out)
# Remove 'vk' from proto name
base_name = command.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 command.protect is not None:
out.append(f'#if defined({command.protect})\n')
# 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':
out.append(' table->GetDeviceProcAddr = gdpa;\n')
elif len(command.extensions) > 0 and self.vk.extensions[command.extensions[0]].instance:
out.append(f' table->{base_name} = (PFN_{command.name})gipa(inst, "{command.name}");\n')
else:
out.append(f' table->{base_name} = (PFN_{command.name})gdpa(dev, "{command.name}");\n')
elif (x < 1 and base_name == 'GetDeviceProcAddr'):
out.append(' table->GetDeviceProcAddr = gpa;\n')
elif (x > 1 and base_name == 'GetInstanceProcAddr'):
out.append(' table->GetInstanceProcAddr = gpa;\n')
else:
out.append(f' table->{base_name} = (PFN_{command.name})gpa({gpa_param}, "{command.name}");\n')
if command.protect is not None:
out.append(f'#endif // {command.protect}\n')
out.append('}\n\n')
#
# Create code to initialize a dispatch table from the appropriate list of extension entrypoints
def InitDeviceFunctionTerminatorDispatchTable(self, out):
out.append('// Functions that required a terminator need to have a separate dispatch table which contains their corresponding\n')
out.append('// device function. This is used in the terminators themselves.\n')
out.append('void init_extension_device_proc_terminator_dispatch(struct loader_device *dev) {\n')
out.append(' struct loader_device_terminator_dispatch* dispatch = &dev->loader_dispatch.extension_terminator_dispatch;\n')
out.append(' PFN_vkGetDeviceProcAddr gpda = (PFN_vkGetDeviceProcAddr)dev->phys_dev_term->this_icd_term->dispatch.GetDeviceProcAddr;\n')
current_block = ''
for command in [x for x in self.vk.commands.values() if x.extensions or x.version]:
if command.name in DEVICE_CMDS_NEED_TERM:
if command.protect is not None:
out.append(f'#if defined({command.protect})\n')
current_block = self.DescribeBlock(command, current_block, out)
enable_extension_expressions = []
for ext in command.extensions:
enable_extension_expressions.append(f'dev->driver_extensions.{ext[3:].lower()}_enabled')
out.append(f' if ({" || ".join(enable_extension_expressions)})\n')
out.append(f' dispatch->{command.name[2:]} = (PFN_{(command.name)})gpda(dev->icd_device, "{(command.name)}");\n')
if command.protect is not None:
out.append(f'#endif // {command.protect}\n')
out.append('}\n\n')
def OutputDeviceFunctionTrampolinePrototypes(self, out):
out.append('// These are prototypes for functions that need their trampoline called in all circumstances.\n')
out.append('// They are used in loader_lookup_device_dispatch_table but are defined afterwards.\n')
current_block = ''
for command in self.vk.commands.values():
if command.name in DEVICE_CMDS_MUST_USE_TRAMP:
if command.protect is not None:
out.append(f'#if defined({command.protect})\n')
current_block = self.DescribeBlock(command, current_block, out)
out.append(f'{command.cPrototype.replace("VKAPI_CALL vk", "VKAPI_CALL ")}\n')
if command.protect is not None:
out.append(f'#endif // {command.protect}\n')
out.append('\n')
#
# Create a lookup table function from the appropriate list of entrypoints
def OutputLoaderLookupFunc(self, out):
commands = []
cur_type = ''
for x in range(0, 2):
if x == 0:
cur_type = 'device'
out.append('// Device command lookup function\n')
out.append('VKAPI_ATTR void* VKAPI_CALL loader_lookup_device_dispatch_table(const VkLayerDispatchTable *table, const char *name, bool* found_name) {\n')
out.append(' if (!name || name[0] != \'v\' || name[1] != \'k\') {\n')
out.append(' *found_name = false;\n')
out.append(' return NULL;\n')
out.append(' }\n')
out.append('\n')
out.append(' name += 2;\n')
out.append(' *found_name = true;\n')
out.append(' struct loader_device* dev = (struct loader_device *)table;\n')
out.append(' const struct loader_instance* inst = dev->phys_dev_term->this_icd_term->this_instance;\n')
out.append(' uint32_t api_version = VK_MAKE_API_VERSION(0, inst->app_api_version.major, inst->app_api_version.minor, inst->app_api_version.patch);\n')
out.append('\n')
else:
cur_type = 'instance'
out.append('// Instance command lookup function\n')
out.append('VKAPI_ATTR void* VKAPI_CALL loader_lookup_instance_dispatch_table(const VkLayerInstanceDispatchTable *table, const char *name,\n')
out.append(' bool *found_name) {\n')
out.append(' if (!name || name[0] != \'v\' || name[1] != \'k\') {\n')
out.append(' *found_name = false;\n')
out.append(' return NULL;\n')
out.append(' }\n')
out.append('\n')
out.append(' *found_name = true;\n')
out.append(' name += 2;\n')
for command_list in [self.core_commands, self.extension_commands]:
commands = command_list
version_check = ''
current_block = ''
for command in commands:
is_inst_handle_type = command.params[0].type in ['VkInstance', 'VkPhysicalDevice']
if ((cur_type == 'instance' and is_inst_handle_type) or (cur_type == 'device' and not is_inst_handle_type)):
current_block = self.DescribeBlock(command, current_block, out)
if len(command.extensions) == 0:
if cur_type == 'device':
effective_version_name = APISpecific.getEffectiveVersionName(self.targetApiName, command.version)
api_version = effective_version_name.replace('_VERSION_', '_API_VERSION_')
version_check = f" if (dev->should_ignore_device_commands_from_newer_version && api_version < {api_version}) return NULL;\n"
else:
version_check = ''
# Remove 'vk' from proto name
base_name = command.name[2:]
if (base_name == 'CreateInstance' or base_name == 'CreateDevice' or
base_name == 'EnumerateInstanceExtensionProperties' or
base_name == 'EnumerateInstanceLayerProperties' or
base_name == 'EnumerateInstanceVersion'):
continue
if command.protect is not None:
out.append(f'#if defined({command.protect})\n')
out.append(f' if (!strcmp(name, "{base_name}")) ')
if command.name in DEVICE_CMDS_MUST_USE_TRAMP:
if version_check != '':
out.append(f'{{\n{version_check} return dev->layer_extensions.{command.extensions[0][3:].lower()}_enabled ? (void *){base_name} : NULL;\n }}\n')
else:
out.append(f'return dev->layer_extensions.{command.extensions[0][3:].lower()}_enabled ? (void *){base_name} : NULL;\n')
else:
if version_check != '':
out.append(f'{{\n{version_check} return (void *)table->{base_name};\n }}\n')
else:
out.append(f'return (void *)table->{base_name};\n')
if command.protect is not None:
out.append(f'#endif // {command.protect}\n')
out.append('\n')
out.append(' *found_name = false;\n')
out.append(' return NULL;\n')
out.append('}\n\n')
#
# Create the appropriate trampoline (and possibly terminator) functions
def CreateTrampTermFuncs(self, out):
# 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']
current_block = ''
for command in [x for x in self.vk.commands.values() if x.extensions]:
if (command.extensions[0] in WSI_EXT_NAMES or
command.extensions[0] in AVOID_EXT_NAMES or
command.name in AVOID_CMD_NAMES or
command.name in manual_ext_commands):
continue
current_block = self.DescribeBlock(command=command, current_block=current_block, out=out, custom_commands_string=' trampoline/terminators\n', indent='')
if command.protect is not None:
out.append(f'#if defined({command.protect})\n')
func_header = command.cPrototype.replace(";", " {\n")
tramp_header = func_header.replace("VKAPI_CALL vk", "VKAPI_CALL ")
return_prefix = ' '
base_name = command.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 command.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 = 'unwrapped_surface'
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 = unwrapped_surface;\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 command.returnType != 'void':
return_prefix += 'return '
has_return_type = True
if ( command.params[0].type in ['VkInstance', 'VkPhysicalDevice'] or
'DebugMarkerSetObject' in command.name or 'SetDebugUtilsObject' in command.name or
command.name in DEVICE_CMDS_NEED_TERM):
requires_terminator = 1
if requires_terminator == 1:
term_header = tramp_header.replace("VKAPI_CALL ", "VKAPI_CALL terminator_")
out.append(tramp_header)
if command.params[0].type == 'VkPhysicalDevice':
out.append(' const VkLayerInstanceDispatchTable *disp;\n')
out.append(f' VkPhysicalDevice unwrapped_phys_dev = loader_unwrap_physical_device({phys_dev_var_name});\n')
out.append(' if (VK_NULL_HANDLE == unwrapped_phys_dev) {\n')
out.append(' loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,\n')
out.append(f' "{command.name}: Invalid {phys_dev_var_name} "\n')
out.append(f' "[VUID-{command.name}-{phys_dev_var_name}-parameter]");\n')
out.append(' abort(); /* Intentionally fail so user can correct issue. */\n')
out.append(' }\n')
out.append(f' disp = loader_get_instance_layer_dispatch({phys_dev_var_name});\n')
elif command.params[0].type == 'VkInstance':
out.append(f' struct loader_instance *inst = loader_get_instance({instance_var_name});\n')
out.append(' if (NULL == inst) {\n')
out.append(' loader_log(\n')
out.append(' NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,\n')
out.append(f' "{command.name}: Invalid instance [VUID-{command.name}-{instance_var_name}-parameter]");\n')
out.append(' abort(); /* Intentionally fail so user can correct issue. */\n')
out.append(' }\n')
out.append('#error("Not implemented. Likely needs to be manually generated!");\n')
else:
out.append(' const VkLayerDispatchTable *disp = loader_get_dispatch(')
out.append(command.params[0].name)
out.append(');\n')
out.append(' if (NULL == disp) {\n')
out.append(' loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,\n')
out.append(f' "{command.name}: Invalid {command.params[0].name} "\n')
out.append(f' "[VUID-{command.name}-{command.params[0].name}-parameter]");\n')
out.append(' abort(); /* Intentionally fail so user can correct issue. */\n')
out.append(' }\n')
if 'DebugMarkerSetObjectName' in command.name:
out.append(' VkDebugMarkerObjectNameInfoEXT local_name_info;\n')
out.append(' memcpy(&local_name_info, pNameInfo, sizeof(VkDebugMarkerObjectNameInfoEXT));\n')
out.append(' // If this is a physical device, we have to replace it with the proper one for the next call.\n')
out.append(' if (pNameInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT) {\n')
out.append(' struct loader_physical_device_tramp *phys_dev_tramp = (struct loader_physical_device_tramp *)(uintptr_t)pNameInfo->object;\n')
out.append(' local_name_info.object = (uint64_t)(uintptr_t)phys_dev_tramp->phys_dev;\n')
out.append(' }\n')
out.append(' if (pNameInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT) {\n')
out.append(' struct loader_instance* instance = (struct loader_instance *)(uintptr_t)pNameInfo->object;\n')
out.append(' local_name_info.object = (uint64_t)(uintptr_t)instance->instance;\n')
out.append(' }\n')
elif 'DebugMarkerSetObjectTag' in command.name:
out.append(' VkDebugMarkerObjectTagInfoEXT local_tag_info;\n')
out.append(' memcpy(&local_tag_info, pTagInfo, sizeof(VkDebugMarkerObjectTagInfoEXT));\n')
out.append(' // If this is a physical device, we have to replace it with the proper one for the next call.\n')
out.append(' if (pTagInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT) {\n')
out.append(' struct loader_physical_device_tramp *phys_dev_tramp = (struct loader_physical_device_tramp *)(uintptr_t)pTagInfo->object;\n')
out.append(' local_tag_info.object = (uint64_t)(uintptr_t)phys_dev_tramp->phys_dev;\n')
out.append(' }\n')
out.append(' if (pTagInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT) {\n')
out.append(' struct loader_instance* instance = (struct loader_instance *)(uintptr_t)pTagInfo->object;\n')
out.append(' local_tag_info.object = (uint64_t)(uintptr_t)instance->instance;\n')
out.append(' }\n')
elif 'SetDebugUtilsObjectName' in command.name:
out.append(' VkDebugUtilsObjectNameInfoEXT local_name_info;\n')
out.append(' memcpy(&local_name_info, pNameInfo, sizeof(VkDebugUtilsObjectNameInfoEXT));\n')
out.append(' // If this is a physical device, we have to replace it with the proper one for the next call.\n')
out.append(' if (pNameInfo->objectType == VK_OBJECT_TYPE_PHYSICAL_DEVICE) {\n')
out.append(' struct loader_physical_device_tramp *phys_dev_tramp = (struct loader_physical_device_tramp *)(uintptr_t)pNameInfo->objectHandle;\n')
out.append(' local_name_info.objectHandle = (uint64_t)(uintptr_t)phys_dev_tramp->phys_dev;\n')
out.append(' }\n')
out.append(' if (pNameInfo->objectType == VK_OBJECT_TYPE_INSTANCE) {\n')
out.append(' struct loader_instance* instance = (struct loader_instance *)(uintptr_t)pNameInfo->objectHandle;\n')
out.append(' local_name_info.objectHandle = (uint64_t)(uintptr_t)instance->instance;\n')
out.append(' }\n')
elif 'SetDebugUtilsObjectTag' in command.name:
out.append(' VkDebugUtilsObjectTagInfoEXT local_tag_info;\n')
out.append(' memcpy(&local_tag_info, pTagInfo, sizeof(VkDebugUtilsObjectTagInfoEXT));\n')
out.append(' // If this is a physical device, we have to replace it with the proper one for the next call.\n')
out.append(' if (pTagInfo->objectType == VK_OBJECT_TYPE_PHYSICAL_DEVICE) {\n')
out.append(' struct loader_physical_device_tramp *phys_dev_tramp = (struct loader_physical_device_tramp *)(uintptr_t)pTagInfo->objectHandle;\n')
out.append(' local_tag_info.objectHandle = (uint64_t)(uintptr_t)phys_dev_tramp->phys_dev;\n')
out.append(' }\n')
out.append(' if (pTagInfo->objectType == VK_OBJECT_TYPE_INSTANCE) {\n')
out.append(' struct loader_instance* instance = (struct loader_instance *)(uintptr_t)pTagInfo->objectHandle;\n')
out.append(' local_tag_info.objectHandle = (uint64_t)(uintptr_t)instance->instance;\n')
out.append(' }\n')
if command.extensions[0] in NULL_CHECK_EXT_NAMES:
out.append(' if (disp->' + base_name + ' != NULL) {\n')
out.append(' ')
out.append(return_prefix)
if command.params[0].type == 'VkInstance':
out.append('inst->')
out.append('disp->')
out.append(base_name)
out.append('(')
count = 0
for param in command.params:
if count != 0:
out.append(', ')
if param.type == 'VkPhysicalDevice':
out.append('unwrapped_phys_dev')
elif ('DebugMarkerSetObject' in command.name or 'SetDebugUtilsObject' in command.name) and param.name == 'pNameInfo':
out.append('&local_name_info')
elif ('DebugMarkerSetObject' in command.name or 'SetDebugUtilsObject' in command.name) and param.name == 'pTagInfo':
out.append('&local_tag_info')
else:
out.append(param.name)
count += 1
out.append(');\n')
if command.extensions[0] in NULL_CHECK_EXT_NAMES:
if command.returnType != 'void':
out.append(' } else {\n')
out.append(' return VK_SUCCESS;\n')
out.append(' }\n')
out.append('}\n\n')
out.append(term_header)
if command.params[0].type == 'VkPhysicalDevice':
out.append(f' struct loader_physical_device_term *phys_dev_term = (struct loader_physical_device_term *){phys_dev_var_name};\n')
out.append(' struct loader_icd_term *icd_term = phys_dev_term->this_icd_term;\n')
out.append(' if (NULL == icd_term->dispatch.')
out.append(base_name)
out.append(') {\n')
fatal_error_bit = '' if has_return_type and (len(command.extensions) == 0 or self.vk.extensions[command.extensions[0]].instance) else 'VULKAN_LOADER_FATAL_ERROR_BIT | '
out.append(f' loader_log(icd_term->this_instance, {fatal_error_bit}VULKAN_LOADER_ERROR_BIT, 0,\n')
out.append(' "ICD associated with VkPhysicalDevice does not support ')
out.append(base_name)
out.append('");\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 has_return_type and (len(command.extensions) == 0 or self.vk.extensions[command.extensions[0]].instance):
out.append(' return VK_ERROR_EXTENSION_NOT_PRESENT;\n')
else:
out.append(' abort(); /* Intentionally fail so user can correct issue. */\n')
out.append(' }\n')
if has_surface == 1:
out.append(f' VkSurfaceKHR unwrapped_surface = {surface_var_name};\n')
out.append(' VkResult unwrap_res = wsi_unwrap_icd_surface(icd_term, &unwrapped_surface);\n')
out.append(' if (unwrap_res == VK_SUCCESS) {\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:
out.append(update_structure_string)
out.append(' ' + return_prefix + 'icd_term->dispatch.')
out.append(base_name)
out.append('(')
count = 0
for param in command.params:
if count != 0:
out.append(', ')
if not always_use_param_name:
if surface_type_to_replace and surface_type_to_replace == param.type:
out.append(surface_name_replacement)
elif physdev_type_to_replace and physdev_type_to_replace == param.type:
out.append(physdev_name_replacement)
else:
out.append(param.name)
else:
out.append(param.name)
count += 1
out.append(');\n')
if not has_return_type:
out.append(' return;\n')
out.append(' }\n')
out.append(return_prefix)
out.append('icd_term->dispatch.')
out.append(base_name)
out.append('(')
count = 0
for param in command.params:
if count != 0:
out.append(', ')
if param.type == 'VkPhysicalDevice':
out.append('phys_dev_term->phys_dev')
else:
out.append(param.name)
count += 1
out.append(');\n')
elif command.params[0].type == 'VkInstance':
out.append(f' struct loader_instance *inst = loader_get_instance({instance_var_name});\n')
out.append(' if (NULL == inst) {\n')
out.append(' loader_log(\n')
out.append(' NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,\n')
out.append(f' "{command.name}: Invalid instance [VUID-{command.name}-{instance_var_name}-parameter]");\n')
out.append(' abort(); /* Intentionally fail so user can correct issue. */\n')
out.append(' }\n')
out.append('#error("Not implemented. Likely needs to be manually generated!");\n')
elif command.extensions[0] in ['VK_EXT_debug_utils', 'VK_EXT_debug_marker']:
if command.name in ['vkDebugMarkerSetObjectNameEXT', 'vkDebugMarkerSetObjectTagEXT', 'vkSetDebugUtilsObjectNameEXT' , 'vkSetDebugUtilsObjectTagEXT']:
is_debug_utils = command.extensions[0] == "VK_EXT_debug_utils"
debug_struct_name = command.params[1].name
local_struct = 'local_name_info' if 'ObjectName' in command.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'
inst_check = 'VK_OBJECT_TYPE_INSTANCE' if is_debug_utils else 'VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT'
out.append(' struct loader_device *dev;\n')
out.append(f' struct loader_icd_term *icd_term = loader_get_icd_and_device({ command.params[0].name}, &dev);\n')
out.append(' if (NULL == icd_term || NULL == dev) {\n')
out.append(f' loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, "{command.name[2:]}: Invalid device handle");\n')
out.append(' abort(); /* Intentionally fail so user can correct issue. */\n')
out.append(' }\n')
out.append(f' { command.params[1].type} {local_struct};\n')
out.append(f' memcpy(&{local_struct}, {debug_struct_name}, sizeof({ command.params[1].type}));\n')
out.append(' // If this is a physical device, we have to replace it with the proper one for the next call.\n')
out.append(f' if ({debug_struct_name}->objectType == {phys_dev_check}) {{\n')
out.append(f' struct loader_physical_device_term *phys_dev_term = (struct loader_physical_device_term *)(uintptr_t){debug_struct_name}->{member_name};\n')
out.append(f' {local_struct}.{member_name} = (uint64_t)(uintptr_t)phys_dev_term->phys_dev;\n')
out.append(' // 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')
out.append(f' }} else if ({debug_struct_name}->objectType == {surf_check}) {{\n')
out.append(' if (NULL != dev && NULL != dev->loader_dispatch.core_dispatch.CreateSwapchainKHR) {\n')
out.append(f' VkSurfaceKHR surface = (VkSurfaceKHR)(uintptr_t){debug_struct_name}->{member_name};\n')
out.append(' if (wsi_unwrap_icd_surface(icd_term, &surface) == VK_SUCCESS) {\n')
out.append(f' {local_struct}.{member_name} = (uint64_t)surface;\n')
out.append(' }\n')
out.append(' }\n')
out.append(' // If this is an instance we have to replace it with the proper one for the next call.\n')
out.append(f' }} else if ({debug_struct_name}->objectType == {inst_check}) {{\n')
out.append(f' {local_struct}.{member_name} = (uint64_t)(uintptr_t)icd_term->instance;\n')
out.append(' }\n')
out.append(' // Exit early if the driver does not support the function - this can happen as a layer or the loader itself supports\n')
out.append(' // debug utils but the driver does not.\n')
out.append(f' if (NULL == dev->loader_dispatch.extension_terminator_dispatch.{command.name[2:]})\n return VK_SUCCESS;\n')
dispatch = 'dev->loader_dispatch.'
else:
out.append(f' struct loader_dev_dispatch_table *dispatch_table = loader_get_dev_dispatch({command.params[0].name});\n')
out.append(' if (NULL == dispatch_table) {\n')
out.append(f' loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0, "{command.extensions[0]}: Invalid device handle");\n')
out.append(' abort(); /* Intentionally fail so user can correct issue. */\n')
out.append(' }\n')
out.append(' // Only call down if the device supports the function\n')
out.append(f' if (NULL != dispatch_table->extension_terminator_dispatch.{base_name})\n ')
dispatch = 'dispatch_table->'
out.append(' ')
if has_return_type:
out.append('return ')
out.append(f'{dispatch}extension_terminator_dispatch.{base_name}(')
count = 0
for param in command.params:
if count != 0:
out.append(', ')
if param.type == 'VkPhysicalDevice':
out.append('phys_dev_term->phys_dev')
elif param.type == 'VkSurfaceKHR':
out.append( 'unwrapped_surface')
elif ('DebugMarkerSetObject' in command.name or 'SetDebugUtilsObject' in command.name) and param.name == 'pNameInfo':
out.append( '&local_name_info')
elif ('DebugMarkerSetObject' in command.name or 'SetDebugUtilsObject' in command.name) and param.name == 'pTagInfo':
out.append('&local_tag_info')
else:
out.append(param.name)
count += 1
out.append(');\n')
else:
out.append('#error("Unknown error path!");\n')
out.append('}\n\n')
else:
out.append(tramp_header)
out.append(' const VkLayerDispatchTable *disp = loader_get_dispatch(')
out.append(command.params[0].name)
out.append(');\n')
out.append(' if (NULL == disp) {\n')
out.append(' loader_log(NULL, VULKAN_LOADER_FATAL_ERROR_BIT | VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,\n')
out.append(f' "{command.name}: Invalid {command.params[0].name} "\n')
out.append(f' "[VUID-{command.name}-{command.params[0].name}-parameter]");\n')
out.append(' abort(); /* Intentionally fail so user can correct issue. */\n')
out.append(' }\n')
if command.extensions[0] in NULL_CHECK_EXT_NAMES:
out.append(' if (disp->' + base_name + ' != NULL) {\n')
out.append(' ')
out.append(return_prefix)
out.append('disp->')
out.append(base_name)
out.append('(')
count = 0
for param in command.params:
if count != 0:
out.append(', ')
out.append(param.name)
count += 1
out.append(');\n')
if command.extensions[0] in NULL_CHECK_EXT_NAMES:
if command.returnType != 'void':
out.append(' } else {\n')
out.append(' return VK_SUCCESS;\n')
out.append(' }\n')
out.append('}\n\n')
if command.protect is not None:
out.append(f'#endif // {command.protect}\n')
#
# Create a function for the extension GPA call
def InstExtensionGPA(self, out):
cur_extension_name = ''
out.append( '// GPA helpers for extensions\n')
out.append( 'bool extension_instance_gpa(struct loader_instance *ptr_instance, const char *name, void **addr) {\n')
out.append( ' *addr = NULL;\n\n')
for command in [x for x in self.vk.commands.values() if x.extensions]:
if (command.version or
command.extensions[0] in WSI_EXT_NAMES or
command.extensions[0] in AVOID_EXT_NAMES or
command.name in AVOID_CMD_NAMES ):
continue
if command.extensions[0] != cur_extension_name:
out.append( f'\n // ---- {command.extensions[0]} extension commands\n')
cur_extension_name = command.extensions[0]
if command.protect is not None:
out.append( f'#if defined({command.protect})\n')
#base_name = command.name[2:]
base_name = SHARED_ALIASES[command.name] if command.name in SHARED_ALIASES else command.name[2:]
if len(command.extensions) > 0 and self.vk.extensions[command.extensions[0]].instance:
out.append( f' if (!strcmp("{command.name}", name)) {{\n')
out.append( ' *addr = (ptr_instance->enabled_extensions.')
out.append( command.extensions[0][3:].lower())
out.append( ' == 1)\n')
out.append( f' ? (void *){base_name}\n')
out.append( ' : NULL;\n')
out.append( ' return true;\n')
out.append( ' }\n')
else:
out.append( f' if (!strcmp("{command.name}", name)) {{\n')
out.append( f' *addr = (void *){base_name};\n')
out.append( ' return true;\n')
out.append( ' }\n')
if command.protect is not None:
out.append( f'#endif // {command.protect}\n')
out.append( ' return false;\n')
out.append( '}\n\n')
#
# Create the extension name init function
def InstantExtensionCreate(self, out):
out.append( '// Used to keep track of all enabled instance extensions\n')
out.append( 'void fill_out_enabled_instance_extensions(uint32_t extension_count, const char *const * extension_list, struct loader_instance_extension_enable_list* enables) {\n')
out.append( ' for (uint32_t i = 0; i < extension_count; i++) {\n')
e = ''
cur_extension_name = ''
for extension in self.instance_extensions:
if extension.name != cur_extension_name:
out.append( f'\n // ---- {extension.name} extension commands\n')
cur_extension_name = extension.name
if extension.protect is not None:
out.append( f'#if defined({extension.protect})\n')
out.append(f' {e}if (0 == strcmp(extension_list[i], ')
out.append( f'{extension.nameString})) {{ enables->{extension.name[3:].lower()} = 1; }}\n')
if extension.protect is not None:
out.append( f'#endif // {extension.protect}\n')
e = 'else '
out.append( ' }\n')
out.append( '}\n\n')
#
# Create code to initialize a dispatch table from the appropriate list of extension entrypoints
def DeviceExtensionGetTerminator(self, out):
out.append('// Some device commands still need a terminator because the loader needs to unwrap something about them.\n')
out.append('// In many cases, the item needing unwrapping is a VkPhysicalDevice or VkSurfaceKHR object. But there may be other items\n')
out.append('// in the future.\n')
out.append('PFN_vkVoidFunction get_extension_device_proc_terminator(struct loader_device *dev, const char *name, bool* found_name) {\n')
out.append(''' *found_name = false;
if (!name || name[0] != 'v' || name[1] != 'k') {
return NULL;
}
name += 2;
''')
last_protect = None
last_ext = None
for command in [x for x in self.vk.commands.values() if x.extensions]:
version = command.version
if command.name in DEVICE_CMDS_NEED_TERM:
if version:
out.append(f' // ---- Core {version.name} commands\n')
else:
last_protect = command.protect
if command.protect is not None:
out.append(f'#if defined({command.protect})\n')
if last_ext != command.extensions[0]:
out.append(f' // ---- {command.extensions[0]} extension commands\n')
last_ext = command.extensions[0]
out.append(f' if (!strcmp(name, "{command.name[2:]}")) {{\n')
out.append(' *found_name = true;\n')
enable_extension_expressions = []
for ext in command.extensions:
enable_extension_expressions.append(f'dev->driver_extensions.{ext[3:].lower()}_enabled')
out.append(f' return {" || ".join(enable_extension_expressions)} ?\n')
out.append(f' (PFN_vkVoidFunction)terminator_{(command.name[2:])} : NULL;\n')
out.append(' }\n')
if last_protect is not None:
out.append(f'#endif // {last_protect}\n')
out.append(' return NULL;\n')
out.append('}\n\n')
#
# Create code to initialize a dispatch table from the appropriate list of core and extension entrypoints
def InitInstLoaderExtensionDispatchTable(self, out):
commands = []
out.append( '// This table contains the loader\'s instance dispatch table, which contains\n')
out.append( '// default functions if no instance layers are activated. This contains\n')
out.append( '// pointers to "terminator functions".\n')
out.append( 'const VkLayerInstanceDispatchTable instance_disp = {\n')
for command_list in [self.core_commands, self.extension_commands]:
commands = command_list
current_block = ''
for command in commands:
if command.params[0].type in ['VkInstance', 'VkPhysicalDevice']:
current_block = self.DescribeBlock(command, current_block, out)
# Remove 'vk' from proto name
base_name = command.name[2:]
aliased_name = SHARED_ALIASES[command.name][2:] if command.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 command.protect is not None:
out.append( f'#if defined({command.protect})\n')
if base_name == 'GetInstanceProcAddr':
out.append( f' .{base_name} = {command.name},\n')
else:
out.append( f' .{base_name} = terminator_{aliased_name},\n')
if command.protect is not None:
out.append( f'#endif // {command.protect}\n')
out.append( '};\n\n')
#
# Create the extension name whitelist array
def OutputInstantExtensionWhitelistArray(self, out):
out.append( '// A null-terminated list of all of the instance extensions supported by the loader.\n')
out.append( '// If an instance extension name is not in this list, but it is exported by one or more of the\n')
out.append( '// ICDs detected by the loader, then the extension name not in the list will be filtered out\n')
out.append( '// before passing the list of extensions to the application.\n')
out.append( 'const char *const LOADER_INSTANCE_EXTENSIONS[] = {\n')
for extension in self.instance_extensions:
if extension.protect is not None:
out.append( f'#if defined({extension.protect})\n')
out.append( ' ')
out.append( extension.nameString + ',\n')
if extension.protect is not None:
out.append( f'#endif // {extension.protect}\n')
out.append( ' NULL };\n')