vulkan-wsi-layer/layer/layer.cpp
Muhamad Sammar 29a7a23847 Handle NULL instance parameter in vkGetInstanceProcAddr
The Vulkan spec allows passing a NULL VkInstance to
vkGetInstanceProcAddr when querying global (pre-instance) function pointers, but these functions must be resolved only through the loader, not through layers.
This change ensures that the layer correctly handles NULL instance and
prevents such incorrect behavior comming from upper layers.

Signed-off-by: Muhamad Sammar <muhamad.sammar@arm.com>
Change-Id: Icd67fa4fb819a6f5ab4c5f40d73514ba79174c05
2025-11-26 16:04:13 +01:00

816 lines
34 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2016-2025 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <cassert>
#include <cstdio>
#include <cstring>
#include <array>
#include <vulkan/vk_layer.h>
#include <vulkan/vulkan.h>
#include "layer/calibrated_timestamps_api.hpp"
#include "present_wait_api.hpp"
#include "wsi_layer_experimental.hpp"
#include "private_data.hpp"
#include "surface_api.hpp"
#include "swapchain_api.hpp"
#include "swapchain_maintenance_api.hpp"
#include "util/extension_list.hpp"
#include "util/custom_allocator.hpp"
#include "wsi/wsi_factory.hpp"
#include "wsi/extensions/present_timing.hpp"
#include "util/log.hpp"
#include "util/macros.hpp"
#include "util/helpers.hpp"
#define VK_LAYER_API_VERSION VK_MAKE_VERSION(1, 2, VK_HEADER_VERSION)
namespace layer
{
VKAPI_ATTR VkLayerInstanceCreateInfo *get_chain_info(const VkInstanceCreateInfo *pCreateInfo, VkLayerFunction func)
{
auto *chain_info = reinterpret_cast<const VkLayerInstanceCreateInfo *>(pCreateInfo->pNext);
while (chain_info &&
!(chain_info->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO && chain_info->function == func))
{
chain_info = reinterpret_cast<const VkLayerInstanceCreateInfo *>(chain_info->pNext);
}
return const_cast<VkLayerInstanceCreateInfo *>(chain_info);
}
VKAPI_ATTR VkLayerDeviceCreateInfo *get_chain_info(const VkDeviceCreateInfo *pCreateInfo, VkLayerFunction func)
{
auto *chain_info = reinterpret_cast<const VkLayerDeviceCreateInfo *>(pCreateInfo->pNext);
while (chain_info &&
!(chain_info->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO && chain_info->function == func))
{
chain_info = reinterpret_cast<const VkLayerDeviceCreateInfo *>(chain_info->pNext);
}
return const_cast<VkLayerDeviceCreateInfo *>(chain_info);
}
template <typename T>
static T get_instance_proc_addr(PFN_vkGetInstanceProcAddr fp_get_instance_proc_addr, const char *name,
VkInstance instance = VK_NULL_HANDLE)
{
T func = reinterpret_cast<T>(fp_get_instance_proc_addr(instance, name));
if (func == nullptr)
{
WSI_LOG_WARNING("Failed to get address of %s", name);
}
return func;
}
template <typename T>
static T get_device_proc_addr(PFN_vkGetDeviceProcAddr fp_get_device_proc_addr, const char *name,
VkDevice device = VK_NULL_HANDLE)
{
T func = reinterpret_cast<T>(fp_get_device_proc_addr(device, name));
if (func == nullptr)
{
WSI_LOG_WARNING("Failed to get address of %s", name);
}
return func;
}
/* This is where the layer is initialised and the instance dispatch table is constructed. */
VKAPI_ATTR VkResult create_instance(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator,
VkInstance *pInstance)
{
VkLayerInstanceCreateInfo *layer_link_info = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
VkLayerInstanceCreateInfo *loader_data_callback = get_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK);
if (nullptr == layer_link_info || nullptr == layer_link_info->u.pLayerInfo || nullptr == loader_data_callback)
{
WSI_LOG_ERROR("Unexpected NULL pointer in layer initialization structures during vkCreateInstance");
return VK_ERROR_INITIALIZATION_FAILED;
}
PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = layer_link_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
PFN_vkSetInstanceLoaderData loader_callback = loader_data_callback->u.pfnSetInstanceLoaderData;
if (nullptr == fpGetInstanceProcAddr || nullptr == loader_callback)
{
WSI_LOG_ERROR("Unexpected NULL pointer for loader callback functions during vkCreateInstance");
return VK_ERROR_INITIALIZATION_FAILED;
}
auto fpCreateInstance = get_instance_proc_addr<PFN_vkCreateInstance>(fpGetInstanceProcAddr, "vkCreateInstance");
if (nullptr == fpCreateInstance)
{
WSI_LOG_ERROR("Unexpected NULL return value from pfnNextGetInstanceProcAddr");
return VK_ERROR_INITIALIZATION_FAILED;
}
/* For instances handled by the layer, we need to enable extra extensions, therefore take a copy of pCreateInfo. */
VkInstanceCreateInfo modified_info = *pCreateInfo;
/* Create a util::vector in case we need to modify the modified_info.ppEnabledExtensionNames list.
* This object and the extension_list object need to be in the global scope so they can be alive by the time
* vkCreateInstance is called.
*/
util::allocator allocator{ VK_SYSTEM_ALLOCATION_SCOPE_COMMAND, pAllocator };
util::vector<const char *> modified_enabled_extensions{ allocator };
util::extension_list extensions{ allocator };
/* Find all the platforms that the layer can handle based on pCreateInfo->ppEnabledExtensionNames. */
auto layer_platforms_to_enable = wsi::find_enabled_layer_platforms(pCreateInfo);
/* Create a list of extensions to enable, including the provided extensions and those required by the layer. */
TRY_LOG_CALL(extensions.add(pCreateInfo->ppEnabledExtensionNames, pCreateInfo->enabledExtensionCount));
uint32_t api_version =
pCreateInfo->pApplicationInfo != nullptr ? pCreateInfo->pApplicationInfo->apiVersion : VK_API_VERSION_1_3;
if (!layer_platforms_to_enable.empty())
{
if (!extensions.contains(VK_KHR_SURFACE_EXTENSION_NAME))
{
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
TRY_LOG_CALL(wsi::add_instance_extensions_required_by_layer(layer_platforms_to_enable, extensions, api_version));
}
TRY_LOG_CALL(extensions.get_extension_strings(modified_enabled_extensions));
modified_info.ppEnabledExtensionNames = modified_enabled_extensions.data();
modified_info.enabledExtensionCount = modified_enabled_extensions.size();
/* Advance the link info for the next element on the chain. */
layer_link_info->u.pLayerInfo = layer_link_info->u.pLayerInfo->pNext;
/* Now call create instance on the chain further down the list.
* Note that we do not remove the extensions that the layer supports from modified_info.ppEnabledExtensionNames.
* Layers have to abide the rule that vkCreateInstance must not generate an error for unrecognized extension names.
* Also, the loader filters the extension list to ensure that ICDs do not see extensions that they do not support.
*/
TRY_LOG(fpCreateInstance(&modified_info, pAllocator, pInstance), "Failed to create the instance");
/* Note: If the call to vkCreateInstance succeeded, the loader will do the clean-up for us
* after this function returns with an error code. We can't call vkDestroyInstance
* ourselves as this will cause double-free from the loader attempting to clean up after us.
* Any failing calls below this point should NOT call vkDestroyInstance and rather just
* return the error code. */
/* Following the spec: use the callbacks provided to vkCreateInstance() if not nullptr,
* otherwise use the default callbacks.
*/
util::allocator instance_allocator{ VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE, pAllocator };
std::optional<instance_dispatch_table> table = instance_dispatch_table::create(instance_allocator);
if (!table.has_value())
{
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
TRY_LOG_CALL(table->populate(*pInstance, fpGetInstanceProcAddr, api_version));
table->set_user_enabled_extensions(pCreateInfo->ppEnabledExtensionNames, pCreateInfo->enabledExtensionCount);
TRY_LOG_CALL(instance_private_data::associate(*pInstance, std::move(*table), loader_callback,
layer_platforms_to_enable, api_version, instance_allocator));
/*
* Store the enabled instance extensions in order to return nullptr in
* vkGetInstanceProcAddr for functions of disabled extensions.
*/
VkResult result =
instance_private_data::get(*pInstance)
.set_instance_enabled_extensions(pCreateInfo->ppEnabledExtensionNames, pCreateInfo->enabledExtensionCount);
if (result != VK_SUCCESS)
{
instance_private_data::disassociate(*pInstance);
return result;
}
return VK_SUCCESS;
}
VKAPI_ATTR VkResult create_device(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkDevice *pDevice)
{
VkLayerDeviceCreateInfo *layer_link_info = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
VkLayerDeviceCreateInfo *loader_data_callback = get_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK);
if (nullptr == layer_link_info || nullptr == layer_link_info->u.pLayerInfo || nullptr == loader_data_callback)
{
WSI_LOG_ERROR("Unexpected NULL pointer in layer initialization structures during vkCreateDevice");
return VK_ERROR_INITIALIZATION_FAILED;
}
/* Retrieve the vkGetDeviceProcAddr and the vkCreateDevice function pointers for the next layer in the chain. */
PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = layer_link_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = layer_link_info->u.pLayerInfo->pfnNextGetDeviceProcAddr;
PFN_vkSetDeviceLoaderData loader_callback = loader_data_callback->u.pfnSetDeviceLoaderData;
if (nullptr == fpGetInstanceProcAddr || nullptr == fpGetDeviceProcAddr || nullptr == loader_callback)
{
WSI_LOG_ERROR("Unexpected NULL pointer for loader callback functions during vkCreateDevice");
return VK_ERROR_INITIALIZATION_FAILED;
}
auto fpCreateDevice = get_instance_proc_addr<PFN_vkCreateDevice>(fpGetInstanceProcAddr, "vkCreateDevice");
if (nullptr == fpCreateDevice)
{
WSI_LOG_ERROR("Unexpected NULL return value from pfnNextGetInstanceProcAddr");
return VK_ERROR_INITIALIZATION_FAILED;
}
/* Advance the link info for the next element on the chain. */
layer_link_info->u.pLayerInfo = layer_link_info->u.pLayerInfo->pNext;
/* Enable extra extensions if needed by the layer, similarly to what done in vkCreateInstance. */
VkDeviceCreateInfo modified_info = *pCreateInfo;
auto &inst_data = instance_private_data::get(physicalDevice);
util::allocator allocator{ inst_data.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND, pAllocator };
util::vector<const char *> modified_enabled_extensions{ allocator };
util::extension_list enabled_extensions{ allocator };
#if VULKAN_WSI_LAYER_EXPERIMENTAL
VkPhysicalDeviceMaintenance9FeaturesKHR maintenance9_features = {};
#endif /* VULKAN_WSI_LAYER_EXPERIMENTAL */
const util::wsi_platform_set &enabled_platforms = inst_data.get_enabled_platforms();
if (!enabled_platforms.empty())
{
TRY_LOG_CALL(enabled_extensions.add(pCreateInfo->ppEnabledExtensionNames, pCreateInfo->enabledExtensionCount));
TRY_LOG_CALL(wsi::add_device_extensions_required_by_layer(physicalDevice, enabled_platforms, enabled_extensions,
inst_data.api_version));
#if VULKAN_WSI_LAYER_EXPERIMENTAL
auto present_timing_supported = wsi::present_timing_dependencies_supported(physicalDevice);
if (std::holds_alternative<VkResult>(present_timing_supported))
{
return std::get<VkResult>(present_timing_supported);
}
if (std::get<bool>(present_timing_supported))
{
TRY_LOG_CALL(enabled_extensions.add(VK_KHR_MAINTENANCE_9_EXTENSION_NAME));
const auto *device_maintenance9_features = util::find_extension<VkPhysicalDeviceMaintenance9FeaturesKHR>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_9_FEATURES_KHR, pCreateInfo->pNext);
if (device_maintenance9_features)
{
if (device_maintenance9_features->maintenance9 == VK_FALSE)
{
/* We are taking the same risk with the frame boundary features below. */
auto *maintenance9_features_non_const =
const_cast<VkPhysicalDeviceMaintenance9FeaturesKHR *>(device_maintenance9_features);
maintenance9_features_non_const->maintenance9 = VK_TRUE;
}
}
else
{
maintenance9_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_9_FEATURES_KHR;
maintenance9_features.pNext = const_cast<void *>(modified_info.pNext);
maintenance9_features.maintenance9 = VK_TRUE;
modified_info.pNext = &maintenance9_features;
}
}
#endif /* VULKAN_WSI_LAYER_EXPERIMENTAL */
TRY_LOG_CALL(enabled_extensions.get_extension_strings(modified_enabled_extensions));
modified_info.ppEnabledExtensionNames = modified_enabled_extensions.data();
modified_info.enabledExtensionCount = modified_enabled_extensions.size();
}
bool should_layer_handle_frame_boundary_events = false;
VkPhysicalDeviceFrameBoundaryFeaturesEXT frame_boundary;
if (ENABLE_INSTRUMENTATION)
{
if (enabled_extensions.contains(VK_EXT_FRAME_BOUNDARY_EXTENSION_NAME))
{
if (inst_data.has_frame_boundary_support(physicalDevice))
{
const auto *application_frame_boundary_features =
util::find_extension<VkPhysicalDeviceFrameBoundaryFeaturesEXT>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAME_BOUNDARY_FEATURES_EXT, pCreateInfo->pNext);
if (application_frame_boundary_features)
{
if (application_frame_boundary_features->frameBoundary == VK_FALSE)
{
/* The original features cannot be modified as they are marked as constant.
* Additionally, it is not possible to unlink this extension from the pNext
* chain as all other passed structures are also marked as const. We'll take
* the risk to modify the original structure as there is no trivial way to
* re-enable frame boundary feature or swap out the original structure. */
auto *frame_boundary_features_non_const =
const_cast<VkPhysicalDeviceFrameBoundaryFeaturesEXT *>(application_frame_boundary_features);
frame_boundary_features_non_const->frameBoundary = VK_TRUE;
}
}
else
{
frame_boundary.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAME_BOUNDARY_FEATURES_EXT;
frame_boundary.pNext = const_cast<void *>(modified_info.pNext);
frame_boundary.frameBoundary = VK_TRUE;
modified_info.pNext = &frame_boundary;
}
should_layer_handle_frame_boundary_events = true;
}
}
}
/* Now call create device on the chain further down the list. */
TRY_LOG(fpCreateDevice(physicalDevice, &modified_info, pAllocator, pDevice), "Failed to create the device");
auto fn_destroy_device = get_device_proc_addr<PFN_vkDestroyDevice>(fpGetDeviceProcAddr, "vkDestroyDevice", *pDevice);
/* This should never be nullptr */
assert(fn_destroy_device != nullptr);
/* Following the spec: use the callbacks provided to vkCreateDevice() if not nullptr, otherwise use the callbacks
* provided to the instance (if no allocator callbacks was provided to the instance, it will use default ones).
*/
util::allocator device_allocator{ inst_data.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_DEVICE, pAllocator };
std::optional<device_dispatch_table> table = device_dispatch_table::create(device_allocator);
if (!table.has_value())
{
fn_destroy_device(*pDevice, pAllocator);
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
VkResult result = table->populate(*pDevice, fpGetDeviceProcAddr, inst_data.api_version);
if (result != VK_SUCCESS)
{
fn_destroy_device(*pDevice, pAllocator);
return result;
}
table->set_user_enabled_extensions(pCreateInfo->ppEnabledExtensionNames, pCreateInfo->enabledExtensionCount);
result = device_private_data::associate(*pDevice, inst_data, physicalDevice, std::move(*table), loader_callback,
device_allocator);
if (result != VK_SUCCESS)
{
fn_destroy_device(*pDevice, pAllocator);
return result;
}
/*
* Store the enabled device extensions in order to return nullptr in
* vkGetDeviceProcAddr for functions of disabled extensions.
*/
auto &device_data = layer::device_private_data::get(*pDevice);
device_data.set_layer_frame_boundary_handling_enabled(should_layer_handle_frame_boundary_events);
result = device_data.set_device_enabled_extensions(pCreateInfo->ppEnabledExtensionNames,
pCreateInfo->enabledExtensionCount);
if (result != VK_SUCCESS)
{
layer::device_private_data::disassociate(*pDevice);
fn_destroy_device(*pDevice, pAllocator);
return result;
}
const auto *swapchain_compression_feature =
util::find_extension<VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT, pCreateInfo->pNext);
if (swapchain_compression_feature != nullptr)
{
device_data.set_swapchain_compression_control_enabled(
swapchain_compression_feature->imageCompressionControlSwapchain);
}
const auto present_id_features = util::find_extension<VkPhysicalDevicePresentIdFeaturesKHR>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR, pCreateInfo->pNext);
if (present_id_features != nullptr)
{
device_data.set_present_id_feature_enabled(present_id_features->presentId);
}
const auto present_id2_features = util::find_extension<VkPhysicalDevicePresentId2FeaturesKHR>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_2_FEATURES_KHR, pCreateInfo->pNext);
if (present_id2_features != nullptr)
{
device_data.set_present_id2_feature_enabled(present_id2_features->presentId2);
}
const auto present_mode_fifo_latest_ready_features =
util::find_extension<VkPhysicalDevicePresentModeFifoLatestReadyFeaturesEXT>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_MODE_FIFO_LATEST_READY_FEATURES_EXT, pCreateInfo->pNext);
if (present_mode_fifo_latest_ready_features != nullptr)
{
device_data.set_present_mode_fifo_latest_ready_enabled(
present_mode_fifo_latest_ready_features->presentModeFifoLatestReady);
}
auto *physical_device_swapchain_maintenance1_features =
util::find_extension<VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT, pCreateInfo->pNext);
if (physical_device_swapchain_maintenance1_features != nullptr)
{
device_data.set_swapchain_maintenance1_enabled(
physical_device_swapchain_maintenance1_features->swapchainMaintenance1);
}
auto *present_wait_features = util::find_extension<VkPhysicalDevicePresentWaitFeaturesKHR>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR, pCreateInfo->pNext);
if (present_wait_features != nullptr)
{
device_data.set_present_wait_enabled(present_wait_features->presentWait);
}
auto *present_wait2_features = util::find_extension<VkPhysicalDevicePresentWait2FeaturesKHR>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_2_FEATURES_KHR, pCreateInfo->pNext);
if (present_wait2_features != nullptr)
{
device_data.set_present_wait2_enabled(present_wait2_features->presentWait2);
}
return VK_SUCCESS;
}
} /* namespace layer */
VWL_VKAPI_CALL(PFN_vkVoidFunction)
wsi_layer_vkGetDeviceProcAddr(VkDevice device, const char *funcName) VWL_API_POST;
VWL_VKAPI_CALL(PFN_vkVoidFunction)
wsi_layer_vkGetInstanceProcAddr(VkInstance instance, const char *funcName) VWL_API_POST;
/* Clean up the dispatch table for this instance. */
VWL_VKAPI_CALL(void)
wsi_layer_vkDestroyInstance(VkInstance instance, const VkAllocationCallbacks *pAllocator) VWL_API_POST
{
if (instance == VK_NULL_HANDLE)
{
return;
}
auto fn_destroy_instance =
layer::instance_private_data::get(instance).disp.get_fn<PFN_vkDestroyInstance>("vkDestroyInstance");
/* Call disassociate() before doing vkDestroyInstance as an instance may be created by a different thread
* just after we call vkDestroyInstance() and it could get the same address if we are unlucky.
*/
layer::instance_private_data::disassociate(instance);
assert(fn_destroy_instance.has_value());
(*fn_destroy_instance)(instance, pAllocator);
}
VWL_VKAPI_CALL(void)
wsi_layer_vkDestroyDevice(VkDevice device, const VkAllocationCallbacks *pAllocator) VWL_API_POST
{
if (device == VK_NULL_HANDLE)
{
return;
}
auto fn_destroy_device = layer::device_private_data::get(device).disp.get_fn<PFN_vkDestroyDevice>("vkDestroyDevice");
/* Call disassociate() before doing vkDestroyDevice as a device may be created by a different thread
* just after we call vkDestroyDevice().
*/
layer::device_private_data::disassociate(device);
assert(fn_destroy_device.has_value());
(*fn_destroy_device)(device, pAllocator);
}
VWL_VKAPI_CALL(VkResult)
wsi_layer_vkCreateInstance(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator,
VkInstance *pInstance) VWL_API_POST
{
return layer::create_instance(pCreateInfo, pAllocator, pInstance);
}
VWL_VKAPI_CALL(VkResult)
wsi_layer_vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) VWL_API_POST
{
return layer::create_device(physicalDevice, pCreateInfo, pAllocator, pDevice);
}
VWL_VKAPI_CALL(VkResult)
VWL_VKAPI_EXPORT wsi_layer_vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface *pVersionStruct)
VWL_API_POST
{
assert(pVersionStruct);
assert(pVersionStruct->sType == LAYER_NEGOTIATE_INTERFACE_STRUCT);
/* 2 is the minimum interface version which would utilize this function. */
assert(pVersionStruct->loaderLayerInterfaceVersion >= 2);
/* Set our requested interface version. Set to 2 for now to separate us from newer versions. */
pVersionStruct->loaderLayerInterfaceVersion = 2;
/* Fill in struct values. */
pVersionStruct->pfnGetInstanceProcAddr = &wsi_layer_vkGetInstanceProcAddr;
pVersionStruct->pfnGetDeviceProcAddr = &wsi_layer_vkGetDeviceProcAddr;
pVersionStruct->pfnGetPhysicalDeviceProcAddr = nullptr;
return VK_SUCCESS;
}
VWL_VKAPI_CALL(void)
wsi_layer_vkGetPhysicalDeviceFeatures2(VkPhysicalDevice physical_device,
VkPhysicalDeviceFeatures2 *pFeatures) VWL_API_POST
{
auto &instance = layer::instance_private_data::get(physical_device);
auto *swapchain_maintenance1_features = util::find_extension<VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT, pFeatures->pNext);
if (swapchain_maintenance1_features != nullptr)
{
swapchain_maintenance1_features->swapchainMaintenance1 = VK_FALSE;
}
auto *present_wait_features = util::find_extension<VkPhysicalDevicePresentWaitFeaturesKHR>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR, pFeatures->pNext);
if (present_wait_features != nullptr)
{
present_wait_features->presentWait = VK_FALSE;
}
instance.disp.GetPhysicalDeviceFeatures2KHR(physical_device, pFeatures);
auto *present_wait2_features = util::find_extension<VkPhysicalDevicePresentWait2FeaturesKHR>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_2_FEATURES_KHR, pFeatures->pNext);
if (present_wait2_features != nullptr)
{
present_wait2_features->presentWait2 = VK_TRUE;
}
auto *image_compression_control_swapchain_features =
util::find_extension<VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT, pFeatures->pNext);
if (image_compression_control_swapchain_features != nullptr)
{
image_compression_control_swapchain_features->imageCompressionControlSwapchain =
instance.has_image_compression_support(physical_device);
}
auto *present_id_features = util::find_extension<VkPhysicalDevicePresentIdFeaturesKHR>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR, pFeatures->pNext);
if (present_id_features != nullptr)
{
present_id_features->presentId = VK_TRUE;
}
auto *present_id2_features = util::find_extension<VkPhysicalDevicePresentId2FeaturesKHR>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_2_FEATURES_KHR, pFeatures->pNext);
if (present_id2_features != nullptr)
{
present_id2_features->presentId2 = VK_TRUE;
}
wsi::set_swapchain_maintenance1_state(physical_device, swapchain_maintenance1_features);
if (present_wait_features != nullptr)
{
/* If there is an surface extension in use that is unsupported by the layer, defer to the ICD */
if (!instance.is_unsupported_surface_extension_enabled())
{
present_wait_features->presentWait = VK_TRUE;
}
}
#if VULKAN_WSI_LAYER_EXPERIMENTAL
auto *present_timing_features = util::find_extension<VkPhysicalDevicePresentTimingFeaturesEXT>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_TIMING_FEATURES_EXT, pFeatures->pNext);
if (present_timing_features != nullptr)
{
bool support;
if (wsi::wsi_ext_present_timing::physical_device_has_supported_queue_family(physical_device, support) !=
VK_SUCCESS)
{
WSI_LOG_ERROR("Failed to query physical device for present timing support");
support = false;
}
present_timing_features->presentTiming = support ? VK_TRUE : VK_FALSE;
present_timing_features->presentAtAbsoluteTime = VK_TRUE;
present_timing_features->presentAtRelativeTime = VK_TRUE;
}
#endif
auto *present_mode_fifo_latest_ready_features =
util::find_extension<VkPhysicalDevicePresentModeFifoLatestReadyFeaturesEXT>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_MODE_FIFO_LATEST_READY_FEATURES_EXT, pFeatures->pNext);
if (present_mode_fifo_latest_ready_features != nullptr)
{
present_mode_fifo_latest_ready_features->presentModeFifoLatestReady = VK_TRUE;
}
}
#define GET_PROC_ADDR(func) \
if (!strcmp(funcName, #func)) \
return (PFN_vkVoidFunction)&wsi_layer_##func;
/**
* @brief Trampoline for **vkGetDeviceProcAddr** inside the WSI layer.
*
* Workflow:
* 1. Retrieve the devices private state (enabled extensions and downstream dispatch table).
* 2. If the function is one that this layer intercepts, return the layers handler.
* 3. Otherwise, forward to the downstream dispatch table (next layer or ICD), or return nullptr if unavailable.
*
* This layer never exposes entrypoints for disabled extensions, preserving the Vulkan dispatch-chain contract.
*
* @param device The VkDevice being queried.
* @param funcName Name of the device-level command to resolve.
* @return Pointer to the layers implementation, the next-layer/ICD function, or nullptr.
*/
VWL_VKAPI_CALL(PFN_vkVoidFunction)
wsi_layer_vkGetDeviceProcAddr(VkDevice device, const char *funcName) VWL_API_POST
{
auto &device_data = layer::device_private_data::get(device);
const uint64_t api_version = device_data.instance_data.api_version;
const bool core_1_1 = api_version >= VK_API_VERSION_1_1;
if (device_data.is_device_extension_enabled(VK_KHR_SWAPCHAIN_EXTENSION_NAME))
{
GET_PROC_ADDR(vkCreateSwapchainKHR);
GET_PROC_ADDR(vkDestroySwapchainKHR);
GET_PROC_ADDR(vkGetSwapchainImagesKHR);
GET_PROC_ADDR(vkAcquireNextImageKHR);
GET_PROC_ADDR(vkQueuePresentKHR);
if (device_data.is_device_extension_enabled(VK_KHR_DEVICE_GROUP_EXTENSION_NAME) || core_1_1)
{
GET_PROC_ADDR(vkAcquireNextImage2KHR);
}
if (core_1_1)
{
GET_PROC_ADDR(vkGetDeviceGroupSurfacePresentModesKHR);
GET_PROC_ADDR(vkGetDeviceGroupPresentCapabilitiesKHR);
}
}
if (device_data.is_device_extension_enabled(VK_KHR_DEVICE_GROUP_EXTENSION_NAME) &&
device_data.is_device_extension_enabled(VK_KHR_SURFACE_EXTENSION_NAME))
{
GET_PROC_ADDR(vkGetDeviceGroupSurfacePresentModesKHR);
GET_PROC_ADDR(vkGetDeviceGroupPresentCapabilitiesKHR);
}
if (device_data.is_device_extension_enabled(VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME))
{
GET_PROC_ADDR(vkGetSwapchainStatusKHR);
}
#if VULKAN_WSI_LAYER_EXPERIMENTAL
if (device_data.is_device_extension_enabled(VK_EXT_PRESENT_TIMING_EXTENSION_NAME))
{
GET_PROC_ADDR(vkSetSwapchainPresentTimingQueueSizeEXT);
GET_PROC_ADDR(vkGetSwapchainTimingPropertiesEXT);
GET_PROC_ADDR(vkGetSwapchainTimeDomainPropertiesEXT);
GET_PROC_ADDR(vkGetPastPresentationTimingEXT);
GET_PROC_ADDR(vkGetCalibratedTimestampsKHR);
if (device_data.is_device_extension_enabled(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME))
{
GET_PROC_ADDR(vkGetCalibratedTimestampsEXT);
}
}
#endif
GET_PROC_ADDR(vkDestroyDevice);
GET_PROC_ADDR(vkCreateImage);
if (device_data.is_device_extension_enabled(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME))
{
if (!strcmp(funcName, "vkBindImageMemory2KHR"))
{
return (PFN_vkVoidFunction)&wsi_layer_vkBindImageMemory2;
}
}
if (core_1_1)
{
GET_PROC_ADDR(vkBindImageMemory2);
}
/* VK_EXT_swapchain_maintenance1 */
if (device_data.is_device_extension_enabled(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME))
{
GET_PROC_ADDR(vkReleaseSwapchainImagesEXT);
}
/* VK_KHR_swapchain_maintenance1 */
if (device_data.is_device_extension_enabled(VK_KHR_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME))
{
if (!strcmp(funcName, "vkReleaseSwapchainImagesKHR"))
{
return (PFN_vkVoidFunction)&wsi_layer_vkReleaseSwapchainImagesEXT;
}
}
/* VK_KHR_present_wait */
if (device_data.is_device_extension_enabled(VK_KHR_PRESENT_WAIT_EXTENSION_NAME))
{
GET_PROC_ADDR(vkWaitForPresentKHR);
}
/* VK_KHR_present_wait2 */
if (device_data.is_device_extension_enabled(VK_KHR_PRESENT_WAIT_2_EXTENSION_NAME))
{
GET_PROC_ADDR(vkWaitForPresent2KHR);
}
return device_data.disp.get_user_enabled_entrypoint(device, funcName);
}
/**
* @brief Trampoline for **vkGetInstanceProcAddr** inside the WSI layer.
*
* Workflow:
* 1. Publish loader-critical symbols (i.e. `vkGetDeviceProcAddr`, `vkGetInstanceProcAddr`).
* 2. Retrieve the instances private state (API version and enabled extensions)
* and intercept layer-handled commands.
* 3. Forward all other commands to the downstream instance dispatch table,
* or return nullptr if unavailable.
*
* This layer only exposes core commands and enabled-extension entrypoints,
* preserving the Vulkan dispatch-chain contract.
*
* @param instance The VkInstance being queried (may be VK_NULL_HANDLE).
* @param funcName Name of the instance-level command to resolve.
* @return Pointer to this layers handler, the next-layer/ICD function, or nullptr.
*/
VWL_VKAPI_CALL(PFN_vkVoidFunction)
wsi_layer_vkGetInstanceProcAddr(VkInstance instance, const char *funcName) VWL_API_POST
{
GET_PROC_ADDR(vkGetDeviceProcAddr);
GET_PROC_ADDR(vkGetInstanceProcAddr);
GET_PROC_ADDR(vkCreateInstance);
GET_PROC_ADDR(vkDestroyInstance);
GET_PROC_ADDR(vkCreateDevice);
if (instance == VK_NULL_HANDLE)
{
return nullptr;
}
auto &instance_data = layer::instance_private_data::get(instance);
const bool core_1_1 = instance_data.api_version >= VK_API_VERSION_1_1;
if ((instance_data.is_instance_extension_enabled(VK_KHR_DEVICE_GROUP_EXTENSION_NAME) &&
instance_data.is_instance_extension_enabled(VK_KHR_SURFACE_EXTENSION_NAME)) ||
core_1_1)
{
GET_PROC_ADDR(vkGetPhysicalDevicePresentRectanglesKHR);
}
if (instance_data.is_instance_extension_enabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME))
{
if (!strcmp(funcName, "vkGetPhysicalDeviceFeatures2KHR"))
{
return (PFN_vkVoidFunction)&wsi_layer_vkGetPhysicalDeviceFeatures2;
}
}
if (core_1_1)
{
GET_PROC_ADDR(vkGetPhysicalDeviceFeatures2);
}
if (instance_data.is_instance_extension_enabled(VK_KHR_SURFACE_EXTENSION_NAME))
{
PFN_vkVoidFunction wsi_func = wsi::get_proc_addr(funcName, instance_data);
if (wsi_func)
{
return wsi_func;
}
GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceSupportKHR);
GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceFormatsKHR);
GET_PROC_ADDR(vkGetPhysicalDeviceSurfacePresentModesKHR);
GET_PROC_ADDR(vkDestroySurfaceKHR);
if (instance_data.is_instance_extension_enabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME))
{
GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceCapabilities2KHR);
GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceFormats2KHR);
}
#if VULKAN_WSI_LAYER_EXPERIMENTAL
GET_PROC_ADDR(vkGetPhysicalDeviceCalibrateableTimeDomainsKHR);
GET_PROC_ADDR(vkGetPhysicalDeviceCalibrateableTimeDomainsEXT);
#endif
if (instance_data.is_instance_extension_enabled(VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME))
{
GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceCapabilities2EXT);
}
}
return instance_data.disp.get_user_enabled_entrypoint(instance, funcName);
}