Add VulkanMock for isolating tests from system

Allows the tests to set the exact values vk-bootstrap will receieve
rather than require a real vulkan driver to be present on the system.

This is done by creating a `vulkan-1.dll` on windows that implements
all of the API functions, and on linux/macOS intercepts `dlsym` to
make it return the mock's functions.
This commit is contained in:
Charles Giessen 2023-09-29 17:47:44 -06:00 committed by Charles Giessen
parent 5e351fcdf8
commit d759d3d575
10 changed files with 1262 additions and 669 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +1,2 @@
set(VK_BOOTSTRAP_SOURCE_HEADER_VERSION 1.3.265)
set(VK_BOOTSTRAP_SOURCE_HEADER_VERSION_GIT_TAG v1.3.265)
set(VK_BOOTSTRAP_SOURCE_HEADER_VERSION 1.3.266)
set(VK_BOOTSTRAP_SOURCE_HEADER_VERSION_GIT_TAG v1.3.266)

View File

@ -133,26 +133,22 @@ class VulkanFunctions {
PFN_vkEnumerateInstanceLayerProperties fp_vkEnumerateInstanceLayerProperties = nullptr;
PFN_vkEnumerateInstanceVersion fp_vkEnumerateInstanceVersion = nullptr;
PFN_vkCreateInstance fp_vkCreateInstance = nullptr;
PFN_vkDestroyInstance fp_vkDestroyInstance = nullptr;
PFN_vkDestroyInstance fp_vkDestroyInstance = nullptr;
PFN_vkCreateDebugUtilsMessengerEXT fp_vkCreateDebugUtilsMessengerEXT = nullptr;
PFN_vkDestroyDebugUtilsMessengerEXT fp_vkDestroyDebugUtilsMessengerEXT = nullptr;
PFN_vkEnumeratePhysicalDevices fp_vkEnumeratePhysicalDevices = nullptr;
PFN_vkGetPhysicalDeviceFeatures fp_vkGetPhysicalDeviceFeatures = nullptr;
PFN_vkGetPhysicalDeviceFeatures2 fp_vkGetPhysicalDeviceFeatures2 = nullptr;
PFN_vkGetPhysicalDeviceFeatures2KHR fp_vkGetPhysicalDeviceFeatures2KHR = nullptr;
PFN_vkGetPhysicalDeviceFormatProperties fp_vkGetPhysicalDeviceFormatProperties = nullptr;
PFN_vkGetPhysicalDeviceImageFormatProperties fp_vkGetPhysicalDeviceImageFormatProperties = nullptr;
PFN_vkGetPhysicalDeviceProperties fp_vkGetPhysicalDeviceProperties = nullptr;
PFN_vkGetPhysicalDeviceProperties2 fp_vkGetPhysicalDeviceProperties2 = nullptr;
PFN_vkGetPhysicalDeviceQueueFamilyProperties fp_vkGetPhysicalDeviceQueueFamilyProperties = nullptr;
PFN_vkGetPhysicalDeviceQueueFamilyProperties2 fp_vkGetPhysicalDeviceQueueFamilyProperties2 = nullptr;
PFN_vkGetPhysicalDeviceMemoryProperties fp_vkGetPhysicalDeviceMemoryProperties = nullptr;
PFN_vkGetPhysicalDeviceFormatProperties2 fp_vkGetPhysicalDeviceFormatProperties2 = nullptr;
PFN_vkGetPhysicalDeviceMemoryProperties2 fp_vkGetPhysicalDeviceMemoryProperties2 = nullptr;
PFN_vkGetDeviceProcAddr fp_vkGetDeviceProcAddr = nullptr;
PFN_vkCreateDevice fp_vkCreateDevice = nullptr;
PFN_vkEnumerateDeviceExtensionProperties fp_vkEnumerateDeviceExtensionProperties = nullptr;
PFN_vkCreateDevice fp_vkCreateDevice = nullptr;
PFN_vkGetDeviceProcAddr fp_vkGetDeviceProcAddr = nullptr;
PFN_vkDestroySurfaceKHR fp_vkDestroySurfaceKHR = nullptr;
PFN_vkGetPhysicalDeviceSurfaceSupportKHR fp_vkGetPhysicalDeviceSurfaceSupportKHR = nullptr;
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fp_vkGetPhysicalDeviceSurfaceFormatsKHR = nullptr;
@ -169,24 +165,21 @@ class VulkanFunctions {
void init_instance_funcs(VkInstance inst) {
instance = inst;
get_inst_proc_addr(fp_vkDestroyInstance, "vkDestroyInstance");
get_inst_proc_addr(fp_vkCreateDebugUtilsMessengerEXT, "vkCreateDebugUtilsMessengerEXT");
get_inst_proc_addr(fp_vkDestroyDebugUtilsMessengerEXT, "vkDestroyDebugUtilsMessengerEXT");
get_inst_proc_addr(fp_vkEnumeratePhysicalDevices, "vkEnumeratePhysicalDevices");
get_inst_proc_addr(fp_vkGetPhysicalDeviceFeatures, "vkGetPhysicalDeviceFeatures");
get_inst_proc_addr(fp_vkGetPhysicalDeviceFeatures2, "vkGetPhysicalDeviceFeatures2");
get_inst_proc_addr(fp_vkGetPhysicalDeviceFeatures2KHR, "vkGetPhysicalDeviceFeatures2KHR");
get_inst_proc_addr(fp_vkGetPhysicalDeviceFormatProperties, "vkGetPhysicalDeviceFormatProperties");
get_inst_proc_addr(fp_vkGetPhysicalDeviceImageFormatProperties, "vkGetPhysicalDeviceImageFormatProperties");
get_inst_proc_addr(fp_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
get_inst_proc_addr(fp_vkGetPhysicalDeviceProperties2, "vkGetPhysicalDeviceProperties2");
get_inst_proc_addr(fp_vkGetPhysicalDeviceQueueFamilyProperties, "vkGetPhysicalDeviceQueueFamilyProperties");
get_inst_proc_addr(fp_vkGetPhysicalDeviceQueueFamilyProperties2, "vkGetPhysicalDeviceQueueFamilyProperties2");
get_inst_proc_addr(fp_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
get_inst_proc_addr(fp_vkGetPhysicalDeviceFormatProperties2, "vkGetPhysicalDeviceFormatProperties2");
get_inst_proc_addr(fp_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2");
get_inst_proc_addr(fp_vkGetDeviceProcAddr, "vkGetDeviceProcAddr");
get_inst_proc_addr(fp_vkCreateDevice, "vkCreateDevice");
get_inst_proc_addr(fp_vkEnumerateDeviceExtensionProperties, "vkEnumerateDeviceExtensionProperties");
get_inst_proc_addr(fp_vkCreateDevice, "vkCreateDevice");
get_inst_proc_addr(fp_vkGetDeviceProcAddr, "vkGetDeviceProcAddr");
get_inst_proc_addr(fp_vkDestroySurfaceKHR, "vkDestroySurfaceKHR");
get_inst_proc_addr(fp_vkGetPhysicalDeviceSurfaceSupportKHR, "vkGetPhysicalDeviceSurfaceSupportKHR");
get_inst_proc_addr(fp_vkGetPhysicalDeviceSurfaceFormatsKHR, "vkGetPhysicalDeviceSurfaceFormatsKHR");
@ -269,11 +262,9 @@ VkResult create_debug_utils_messenger(VkInstance instance,
messengerCreateInfo.pfnUserCallback = debug_callback;
messengerCreateInfo.pUserData = user_data_pointer;
PFN_vkCreateDebugUtilsMessengerEXT createMessengerFunc;
detail::vulkan_functions().get_inst_proc_addr(createMessengerFunc, "vkCreateDebugUtilsMessengerEXT");
if (createMessengerFunc != nullptr) {
return createMessengerFunc(instance, &messengerCreateInfo, allocation_callbacks, pDebugMessenger);
if (detail::vulkan_functions().fp_vkCreateDebugUtilsMessengerEXT != nullptr) {
return detail::vulkan_functions().fp_vkCreateDebugUtilsMessengerEXT(
instance, &messengerCreateInfo, allocation_callbacks, pDebugMessenger);
} else {
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
@ -282,11 +273,8 @@ VkResult create_debug_utils_messenger(VkInstance instance,
void destroy_debug_utils_messenger(
VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, VkAllocationCallbacks* allocation_callbacks) {
PFN_vkDestroyDebugUtilsMessengerEXT deleteMessengerFunc;
detail::vulkan_functions().get_inst_proc_addr(deleteMessengerFunc, "vkDestroyDebugUtilsMessengerEXT");
if (deleteMessengerFunc != nullptr) {
deleteMessengerFunc(instance, debugMessenger, allocation_callbacks);
if (detail::vulkan_functions().fp_vkDestroyDebugUtilsMessengerEXT != nullptr) {
detail::vulkan_functions().fp_vkDestroyDebugUtilsMessengerEXT(instance, debugMessenger, allocation_callbacks);
}
}
@ -930,7 +918,13 @@ bool supports_features(VkPhysicalDeviceFeatures supported,
if (requested.variableMultisampleRate && !supported.variableMultisampleRate) return false;
if (requested.inheritedQueries && !supported.inheritedQueries) return false;
for(size_t i = 0; i < extension_requested.size(); ++i) {
// Should only be false if extension_supported was unable to be filled out, due to the
// physical device not supporting vkGetPhysicalDeviceFeatures2 in any capacity.
if (extension_requested.size() != extension_supported.size()) {
return false;
}
for(size_t i = 0; i < extension_requested.size() && i < extension_supported.size(); ++i) {
auto res = GenericFeaturesPNextNode::match(extension_requested[i], extension_supported[i]);
if(!res) return false;
}

View File

@ -2236,6 +2236,21 @@ struct DispatchTable {
#endif
#if (defined(VK_AMDX_shader_enqueue))
fp_vkCmdDispatchGraphIndirectCountAMDX = reinterpret_cast<PFN_vkCmdDispatchGraphIndirectCountAMDX>(procAddr(device, "vkCmdDispatchGraphIndirectCountAMDX"));
#endif
#if (defined(VK_NV_low_latency2))
fp_vkSetLatencySleepModeNV = reinterpret_cast<PFN_vkSetLatencySleepModeNV>(procAddr(device, "vkSetLatencySleepModeNV"));
#endif
#if (defined(VK_NV_low_latency2))
fp_vkLatencySleepNV = reinterpret_cast<PFN_vkLatencySleepNV>(procAddr(device, "vkLatencySleepNV"));
#endif
#if (defined(VK_NV_low_latency2))
fp_vkSetLatencyMarkerNV = reinterpret_cast<PFN_vkSetLatencyMarkerNV>(procAddr(device, "vkSetLatencyMarkerNV"));
#endif
#if (defined(VK_NV_low_latency2))
fp_vkGetLatencyTimingsNV = reinterpret_cast<PFN_vkGetLatencyTimingsNV>(procAddr(device, "vkGetLatencyTimingsNV"));
#endif
#if (defined(VK_NV_low_latency2))
fp_vkQueueNotifyOutOfBandNV = reinterpret_cast<PFN_vkQueueNotifyOutOfBandNV>(procAddr(device, "vkQueueNotifyOutOfBandNV"));
#endif
}
void getDeviceQueue(uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue) const noexcept {
@ -4439,6 +4454,31 @@ struct DispatchTable {
void cmdDispatchGraphIndirectCountAMDX(VkCommandBuffer commandBuffer, VkDeviceAddress scratch, VkDeviceAddress countInfo) const noexcept {
fp_vkCmdDispatchGraphIndirectCountAMDX(commandBuffer, scratch, countInfo);
}
#endif
#if (defined(VK_NV_low_latency2))
VkResult setLatencySleepModeNV(VkSwapchainKHR swapchain, VkLatencySleepModeInfoNV* pSleepModeInfo) const noexcept {
return fp_vkSetLatencySleepModeNV(device, swapchain, pSleepModeInfo);
}
#endif
#if (defined(VK_NV_low_latency2))
VkResult latencySleepNV(VkSwapchainKHR swapchain, VkLatencySleepInfoNV* pSleepInfo) const noexcept {
return fp_vkLatencySleepNV(device, swapchain, pSleepInfo);
}
#endif
#if (defined(VK_NV_low_latency2))
void setLatencyMarkerNV(VkSwapchainKHR swapchain, VkSetLatencyMarkerInfoNV* pLatencyMarkerInfo) const noexcept {
fp_vkSetLatencyMarkerNV(device, swapchain, pLatencyMarkerInfo);
}
#endif
#if (defined(VK_NV_low_latency2))
void getLatencyTimingsNV(VkSwapchainKHR swapchain, uint32_t* pTimingCount, VkGetLatencyMarkerInfoNV* pLatencyMarkerInfo) const noexcept {
fp_vkGetLatencyTimingsNV(device, swapchain, pTimingCount, pLatencyMarkerInfo);
}
#endif
#if (defined(VK_NV_low_latency2))
void queueNotifyOutOfBandNV(VkQueue queue, VkOutOfBandQueueTypeInfoNV pQueueTypeInfo) const noexcept {
fp_vkQueueNotifyOutOfBandNV(queue, pQueueTypeInfo);
}
#endif
PFN_vkGetDeviceQueue fp_vkGetDeviceQueue = nullptr;
PFN_vkQueueSubmit fp_vkQueueSubmit = nullptr;
@ -5665,6 +5705,21 @@ struct DispatchTable {
#endif
#if (defined(VK_AMDX_shader_enqueue))
PFN_vkCmdDispatchGraphIndirectCountAMDX fp_vkCmdDispatchGraphIndirectCountAMDX = nullptr;
#endif
#if (defined(VK_NV_low_latency2))
PFN_vkSetLatencySleepModeNV fp_vkSetLatencySleepModeNV = nullptr;
#endif
#if (defined(VK_NV_low_latency2))
PFN_vkLatencySleepNV fp_vkLatencySleepNV = nullptr;
#endif
#if (defined(VK_NV_low_latency2))
PFN_vkSetLatencyMarkerNV fp_vkSetLatencyMarkerNV = nullptr;
#endif
#if (defined(VK_NV_low_latency2))
PFN_vkGetLatencyTimingsNV fp_vkGetLatencyTimingsNV = nullptr;
#endif
#if (defined(VK_NV_low_latency2))
PFN_vkQueueNotifyOutOfBandNV fp_vkQueueNotifyOutOfBandNV = nullptr;
#endif
bool is_populated() const { return populated; }
VkDevice device = VK_NULL_HANDLE;

View File

@ -1,14 +1,31 @@
if (WIN32)
add_library(VulkanMock SHARED vulkan_mock.hpp vulkan_mock.cpp)
# Need to name the target "vulkan-1" so that it'll be loaded instead of the *actual* vulkan-1.dll on the system
set_target_properties(VulkanMock PROPERTIES OUTPUT_NAME "vulkan-1")
else()
add_library(VulkanMock STATIC vulkan_mock.hpp vulkan_mock.cpp)
endif()
target_link_libraries(VulkanMock
PUBLIC
vk-bootstrap-vulkan-headers
PRIVATE
vk-bootstrap-compiler-warnings
)
target_compile_features(VulkanMock PUBLIC cxx_std_17)
add_executable(vk-bootstrap-test
vulkan_library_loader.hpp
bootstrap_tests.cpp
error_code_tests.cpp
unit_tests.cpp)
unit_tests.cpp
)
target_link_libraries(vk-bootstrap-test
PRIVATE
vk-bootstrap
vk-bootstrap-vulkan-headers
vk-bootstrap-compiler-warnings
glfw
VulkanMock
Catch2::Catch2WithMain
)

View File

@ -1,4 +1,8 @@
#include "common.h"
#include "vulkan_library_loader.hpp"
#include "vulkan_mock.hpp"
#include <algorithm>
#include <catch2/catch_test_macros.hpp>
@ -14,14 +18,64 @@ vkb::Instance get_headless_instance(uint32_t minor_version = 0) {
return instance_ret.value();
}
VkExtensionProperties get_extension_properties(const char* extName) {
VkExtensionProperties ext_props{};
std::copy_n(extName, VK_MAX_EXTENSION_NAME_SIZE, ext_props.extensionName);
return ext_props;
}
VulkanMock& get_and_setup_default() {
VulkanMock& mock = get_vulkan_mock();
mock.instance_extensions.push_back(get_extension_properties(VK_KHR_SURFACE_EXTENSION_NAME));
#if defined(_WIN32)
mock.instance_extensions.push_back(get_extension_properties("VK_KHR_win32_surface"));
#elif defined(__ANDROID__)
mock.instance_extensions.push_back(get_extension_properties("VK_KHR_android_surface"));
#elif defined(_DIRECT2DISPLAY)
mock.instance_extensions.push_back(get_extension_properties("VK_KHR_android_surface"));
#elif defined(__linux__)
mock.instance_extensions.push_back(get_extension_properties("VK_KHR_xcb_surface"));
mock.instance_extensions.push_back(get_extension_properties("VK_KHR_xlib_surface"));
mock.instance_extensions.push_back(get_extension_properties("VK_KHR_wayland_surface"));
#elif defined(__APPLE__)
mock.instance_extensions.push_back(get_extension_properties("VK_EXT_metal_surface"));
#endif
mock.instance_extensions.push_back(get_extension_properties(VK_EXT_DEBUG_UTILS_EXTENSION_NAME));
VulkanMock::PhysicalDeviceDetails physical_device_details{};
physical_device_details.extensions.push_back(get_extension_properties(VK_KHR_SWAPCHAIN_EXTENSION_NAME));
physical_device_details.properties.apiVersion = VK_API_VERSION_1_0;
VkQueueFamilyProperties queue_family_properties{};
queue_family_properties.queueCount = 1;
queue_family_properties.queueFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT;
queue_family_properties.minImageTransferGranularity = { 1, 1, 1 };
physical_device_details.queue_family_properties.push_back(queue_family_properties);
mock.add_physical_device(std::move(physical_device_details));
return mock;
}
VulkanMock::SurfaceDetails get_basic_surface_details() {
VulkanMock::SurfaceDetails details;
details.present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
details.surface_formats.push_back(VkSurfaceFormatKHR{ VK_FORMAT_R8G8B8_SRGB, VK_COLORSPACE_SRGB_NONLINEAR_KHR });
details.capabilities.minImageCount = 2;
details.capabilities.minImageExtent = { 600, 800 };
details.capabilities.currentExtent = { 600, 800 };
details.capabilities.supportedUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
return details;
}
// TODO
// changing present modes and/or image formats
TEST_CASE("Instance with surface", "[VkBootstrap.bootstrap]") {
VulkanMock& mock = get_and_setup_default();
mock.api_version = VK_API_VERSION_1_1;
mock.physical_devices_details[0].properties.apiVersion = VK_API_VERSION_1_1;
mock.physical_devices_details[0].extensions.push_back(get_extension_properties("VK_KHR_multiview"));
mock.physical_devices_details[0].extensions.push_back(get_extension_properties("VK_KHR_driver_properties"));
auto surface = mock.get_new_surface(get_basic_surface_details());
GIVEN("A window and a vulkan instance") {
auto window = create_window_glfw("Instance with surface");
auto sys_info_ret = vkb::SystemInfo::get_system_info();
REQUIRE(sys_info_ret);
@ -32,7 +86,6 @@ TEST_CASE("Instance with surface", "[VkBootstrap.bootstrap]") {
.build();
REQUIRE(instance_ret);
vkb::Instance instance = instance_ret.value();
auto surface = create_surface_glfw(instance.instance, window);
GIVEN("A default selected physical device") {
vkb::PhysicalDeviceSelector phys_device_selector(instance);
@ -64,7 +117,6 @@ TEST_CASE("Instance with surface", "[VkBootstrap.bootstrap]") {
vkb::destroy_surface(instance, surface);
vkb::destroy_instance(instance);
destroy_window_glfw(window);
}
GIVEN("Two Instances") {
vkb::InstanceBuilder instance_builder1;
@ -80,6 +132,7 @@ TEST_CASE("Instance with surface", "[VkBootstrap.bootstrap]") {
}
TEST_CASE("instance configuration", "[VkBootstrap.bootstrap]") {
[[maybe_unused]] VulkanMock& mock = get_and_setup_default();
SECTION("custom debug callback") {
vkb::InstanceBuilder builder;
@ -121,6 +174,7 @@ TEST_CASE("instance configuration", "[VkBootstrap.bootstrap]") {
}
TEST_CASE("Headless Vulkan", "[VkBootstrap.bootstrap]") {
[[maybe_unused]] VulkanMock& mock = get_and_setup_default();
auto instance = get_headless_instance();
vkb::PhysicalDeviceSelector phys_device_selector(instance);
@ -137,10 +191,11 @@ TEST_CASE("Headless Vulkan", "[VkBootstrap.bootstrap]") {
}
TEST_CASE("Device Configuration", "[VkBootstrap.bootstrap]") {
auto window = create_window_glfw("Device Configuration");
VulkanMock& mock = get_and_setup_default();
mock.api_version = VK_API_VERSION_1_1;
mock.physical_devices_details[0].properties.apiVersion = VK_API_VERSION_1_1;
auto instance = get_instance(1);
auto surface = create_surface_glfw(instance.instance, window);
auto surface = mock.get_new_surface(get_basic_surface_details());
vkb::PhysicalDeviceSelector phys_device_selector(instance);
@ -198,15 +253,18 @@ TEST_CASE("Device Configuration", "[VkBootstrap.bootstrap]") {
TEST_CASE("Select all Physical Devices", "[VkBootstrap.bootstrap]") {
VulkanMock& mock = get_and_setup_default();
mock.api_version = VK_API_VERSION_1_1;
mock.physical_devices_details[0].properties.apiVersion = VK_API_VERSION_1_1;
std::copy_n("mocking_gpus_for_fun_and_profit", VK_MAX_DRIVER_NAME_SIZE, mock.physical_devices_details[0].properties.deviceName);
auto window = create_window_glfw("Select all Physical Devices");
auto instance = get_instance(1);
auto surface = mock.get_new_surface(get_basic_surface_details());
auto instance_dispatch_table = instance.make_table();
// needs to successfully create an instance dispatch table
REQUIRE(instance_dispatch_table.fp_vkEnumeratePhysicalDevices);
auto surface = create_surface_glfw(instance.instance, window);
vkb::PhysicalDeviceSelector phys_device_selector(instance, surface);
@ -229,6 +287,7 @@ TEST_CASE("Select all Physical Devices", "[VkBootstrap.bootstrap]") {
}
TEST_CASE("Loading Dispatch Table", "[VkBootstrap.bootstrap]") {
[[maybe_unused]] VulkanMock& mock = get_and_setup_default();
auto instance = get_headless_instance(0);
{
vkb::PhysicalDeviceSelector selector(instance);
@ -255,10 +314,12 @@ TEST_CASE("Loading Dispatch Table", "[VkBootstrap.bootstrap]") {
}
TEST_CASE("Swapchain", "[VkBootstrap.bootstrap]") {
VulkanMock& mock = get_and_setup_default();
mock.api_version = VK_API_VERSION_1_1;
mock.physical_devices_details[0].properties.apiVersion = VK_API_VERSION_1_1;
auto surface = mock.get_new_surface(get_basic_surface_details());
GIVEN("A working instance, window, surface, and device") {
auto window = create_window_glfw("Swapchain");
auto instance = get_instance(1);
auto surface = create_surface_glfw(instance.instance, window);
vkb::PhysicalDeviceSelector phys_device_selector(instance);
auto phys_device_ret = phys_device_selector.set_surface(surface).select();
@ -389,12 +450,14 @@ void VKAPI_PTR shim_vkFreeFunction(void* /*pUserData*/, void* pMemory) { return
TEST_CASE("Allocation Callbacks", "[VkBootstrap.bootstrap]") {
VulkanMock& mock = get_and_setup_default();
auto surface = mock.get_new_surface(get_basic_surface_details());
VkAllocationCallbacks allocation_callbacks{};
allocation_callbacks.pfnAllocation = &shim_vkAllocationFunction;
allocation_callbacks.pfnReallocation = &shim_vkReallocationFunction;
allocation_callbacks.pfnFree = &shim_vkFreeFunction;
auto window = create_window_glfw("Allocation Callbacks");
vkb::InstanceBuilder builder;
auto instance_ret = builder.request_validation_layers()
@ -402,7 +465,6 @@ TEST_CASE("Allocation Callbacks", "[VkBootstrap.bootstrap]") {
.use_default_debug_messenger()
.build();
REQUIRE(instance_ret.has_value());
auto surface = create_surface_glfw(instance_ret.value().instance, window, &allocation_callbacks);
vkb::PhysicalDeviceSelector phys_device_selector(instance_ret.value());
@ -428,6 +490,7 @@ TEST_CASE("Allocation Callbacks", "[VkBootstrap.bootstrap]") {
}
TEST_CASE("SystemInfo Loading Vulkan Automatically", "[VkBootstrap.loading]") {
[[maybe_unused]] VulkanMock& mock = get_and_setup_default();
auto info_ret = vkb::SystemInfo::get_system_info();
REQUIRE(info_ret);
vkb::InstanceBuilder builder;
@ -436,6 +499,7 @@ TEST_CASE("SystemInfo Loading Vulkan Automatically", "[VkBootstrap.loading]") {
}
TEST_CASE("SystemInfo Loading Vulkan Manually", "[VkBootstrap.loading]") {
[[maybe_unused]] VulkanMock& mock = get_and_setup_default();
VulkanLibrary vk_lib;
REQUIRE(vk_lib.vkGetInstanceProcAddr != NULL);
auto info_ret = vkb::SystemInfo::get_system_info(vk_lib.vkGetInstanceProcAddr);
@ -447,12 +511,14 @@ TEST_CASE("SystemInfo Loading Vulkan Manually", "[VkBootstrap.loading]") {
}
TEST_CASE("InstanceBuilder Loading Vulkan Automatically", "[VkBootstrap.loading]") {
[[maybe_unused]] VulkanMock& mock = get_and_setup_default();
vkb::InstanceBuilder builder;
auto ret = builder.build();
REQUIRE(ret);
}
TEST_CASE("InstanceBuilder Loading Vulkan Manually", "[VkBootstrap.loading]") {
[[maybe_unused]] VulkanMock& mock = get_and_setup_default();
VulkanLibrary vk_lib;
REQUIRE(vk_lib.vkGetInstanceProcAddr != NULL);
vkb::InstanceBuilder builder{ vk_lib.vkGetInstanceProcAddr };
@ -460,6 +526,7 @@ TEST_CASE("InstanceBuilder Loading Vulkan Manually", "[VkBootstrap.loading]") {
vk_lib.close();
}
TEST_CASE("ReLoading Vulkan Automatically", "[VkBootstrap.loading]") {
[[maybe_unused]] VulkanMock& mock = get_and_setup_default();
{
vkb::InstanceBuilder builder;
auto ret = builder.build();
@ -473,6 +540,7 @@ TEST_CASE("ReLoading Vulkan Automatically", "[VkBootstrap.loading]") {
}
TEST_CASE("ReLoading Vulkan Manually", "[VkBootstrap.loading]") {
[[maybe_unused]] VulkanMock& mock = get_and_setup_default();
{
VulkanLibrary vk_lib;
REQUIRE(vk_lib.vkGetInstanceProcAddr != NULL);
@ -492,6 +560,13 @@ TEST_CASE("ReLoading Vulkan Manually", "[VkBootstrap.loading]") {
}
TEST_CASE("Querying Required Extension Features but with 1.0", "[VkBootstrap.select_features]") {
VulkanMock& mock = get_and_setup_default();
mock.instance_extensions.push_back(get_extension_properties("VK_KHR_get_physical_device_properties2"));
mock.physical_devices_details[0].extensions.push_back(get_extension_properties("VK_EXT_descriptor_indexing"));
mock.physical_devices_details[0].extensions.push_back(get_extension_properties("VK_KHR_maintenance3"));
auto mock_descriptor_indexing_features = VkPhysicalDeviceDescriptorIndexingFeaturesEXT{};
mock_descriptor_indexing_features.runtimeDescriptorArray = true;
mock.physical_devices_details[0].add_features_pNext_struct(mock_descriptor_indexing_features);
GIVEN("A working instance") {
auto instance = get_headless_instance();
// Requires a device that supports runtime descriptor arrays via descriptor indexing extension.
@ -517,6 +592,13 @@ TEST_CASE("Querying Required Extension Features but with 1.0", "[VkBootstrap.sel
}
}
TEST_CASE("Querying Required Extension Features", "[VkBootstrap.select_features]") {
VulkanMock& mock = get_and_setup_default();
mock.instance_extensions.push_back(get_extension_properties("VK_KHR_get_physical_device_properties2"));
mock.physical_devices_details[0].extensions.push_back(get_extension_properties("VK_EXT_descriptor_indexing"));
mock.physical_devices_details[0].extensions.push_back(get_extension_properties("VK_KHR_maintenance3"));
auto mock_descriptor_indexing_features = VkPhysicalDeviceDescriptorIndexingFeaturesEXT{};
mock_descriptor_indexing_features.runtimeDescriptorArray = true;
mock.physical_devices_details[0].add_features_pNext_struct(mock_descriptor_indexing_features);
GIVEN("A working instance") {
auto instance = get_headless_instance();
// Requires a device that supports runtime descriptor arrays via descriptor indexing extension.
@ -543,6 +625,8 @@ TEST_CASE("Querying Required Extension Features", "[VkBootstrap.select_features]
}
TEST_CASE("Passing vkb classes to Vulkan handles", "[VkBootstrap.pass_class_to_handle]") {
VulkanMock& mock = get_and_setup_default();
auto surface = mock.get_new_surface(get_basic_surface_details());
GIVEN("A working instance") {
auto instance = get_instance();
@ -550,9 +634,6 @@ TEST_CASE("Passing vkb classes to Vulkan handles", "[VkBootstrap.pass_class_to_h
PFN_vkVoidFunction instanceFunction = instance.fp_vkGetInstanceProcAddr(instance, "vkEnumeratePhysicalDevices"); // validation layers should be provided.
REQUIRE(instanceFunction != NULL);
auto window = create_window_glfw("Conversion operators");
auto surface = create_surface_glfw(instance, window);
vkb::PhysicalDeviceSelector physicalDeviceSelector(instance);
auto physicalDevice =
physicalDeviceSelector.add_required_extension(VK_KHR_SWAPCHAIN_EXTENSION_NAME).set_surface(surface).select();
@ -567,8 +648,14 @@ TEST_CASE("Passing vkb classes to Vulkan handles", "[VkBootstrap.pass_class_to_h
}
}
#if defined(VKB_VK_API_VERSION_1_1)
TEST_CASE("Querying Required Extension Features in 1.1", "[VkBootstrap.version]") {
VulkanMock& mock = get_and_setup_default();
mock.instance_extensions.push_back(get_extension_properties("VK_KHR_get_physical_device_properties2"));
mock.physical_devices_details[0].extensions.push_back(get_extension_properties("VK_EXT_descriptor_indexing"));
mock.physical_devices_details[0].extensions.push_back(get_extension_properties("VK_KHR_maintenance3"));
auto mock_descriptor_indexing_features = VkPhysicalDeviceDescriptorIndexingFeaturesEXT{};
mock_descriptor_indexing_features.runtimeDescriptorArray = true;
mock.physical_devices_details[0].add_features_pNext_struct(mock_descriptor_indexing_features);
GIVEN("A working instance") {
auto instance = get_headless_instance();
SECTION("Requires a device that supports runtime descriptor arrays via descriptor indexing extension.") {
@ -638,10 +725,20 @@ TEST_CASE("Querying Required Extension Features in 1.1", "[VkBootstrap.version]"
vkb::destroy_instance(instance);
}
}
#endif
#if defined(VKB_VK_API_VERSION_1_2)
TEST_CASE("Querying Vulkan 1.1 and 1.2 features", "[VkBootstrap.version]") {
[[maybe_unused]] VulkanMock& mock = get_and_setup_default();
mock.api_version = VK_API_VERSION_1_2;
mock.physical_devices_details[0].properties.apiVersion = VK_API_VERSION_1_2;
auto mock_vulkan_11_features = VkPhysicalDeviceVulkan11Features{};
mock_vulkan_11_features.multiview = true;
mock.physical_devices_details[0].add_features_pNext_struct(mock_vulkan_11_features);
auto mock_vulkan_12_features = VkPhysicalDeviceVulkan12Features{};
mock_vulkan_12_features.bufferDeviceAddress = true;
mock.physical_devices_details[0].add_features_pNext_struct(mock_vulkan_12_features);
GIVEN("A working instance") {
vkb::InstanceBuilder builder;
auto instance = get_headless_instance(2); // make sure we use 1.2
@ -663,6 +760,8 @@ TEST_CASE("Querying Vulkan 1.1 and 1.2 features", "[VkBootstrap.version]") {
REQUIRE(device_ret.has_value());
vkb::destroy_device(device_ret.value());
}
mock.api_version = VK_API_VERSION_1_1;
mock.physical_devices_details[0].properties.apiVersion = VK_API_VERSION_1_1;
SECTION("protectedMemory should NOT be supported") {
VkPhysicalDeviceVulkan11Features features_11{};
features_11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
@ -676,5 +775,3 @@ TEST_CASE("Querying Vulkan 1.1 and 1.2 features", "[VkBootstrap.version]") {
vkb::destroy_instance(instance);
}
}
#endif

View File

@ -1,168 +0,0 @@
#pragma once
#include <stdio.h>
#include <memory>
#include <iostream>
#if defined(_WIN32)
#include <fcntl.h>
#define NOMINMAX
#include <windows.h>
#endif // _WIN32
#if defined(__linux__) || defined(__APPLE__)
#include <dlfcn.h>
#endif
#define GLFW_INCLUDE_VULKAN
#include "GLFW/glfw3.h"
#include "../src/VkBootstrap.h"
GLFWwindow* create_window_glfw(const char* window_name = "", bool resize = true) {
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
if (!resize) glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
return glfwCreateWindow(1024, 1024, window_name, NULL, NULL);
}
void destroy_window_glfw(GLFWwindow* window) {
glfwDestroyWindow(window);
glfwTerminate();
}
VkSurfaceKHR create_surface_glfw(
VkInstance instance, GLFWwindow* window, VkAllocationCallbacks* allocator = nullptr) {
VkSurfaceKHR surface = VK_NULL_HANDLE;
VkResult err = glfwCreateWindowSurface(instance, window, allocator, &surface);
if (err) {
const char* error_msg;
int ret = glfwGetError(&error_msg);
if (ret != 0) {
std::cout << ret << " ";
if (error_msg != nullptr) std::cout << error_msg;
std::cout << "\n";
}
surface = VK_NULL_HANDLE;
}
return surface;
}
struct VulkanLibrary {
#if defined(__linux__) || defined(__APPLE__)
void* library;
#elif defined(_WIN32)
HMODULE library;
#endif
VulkanLibrary() {
#if defined(__linux__)
library = dlopen("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL);
if (!library) library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
#elif defined(__APPLE__)
library = dlopen("libvulkan.dylib", RTLD_NOW | RTLD_LOCAL);
if (!library) library = dlopen("libvulkan.1.dylib", RTLD_NOW | RTLD_LOCAL);
#elif defined(_WIN32)
library = LoadLibrary(TEXT("vulkan-1.dll"));
#else
assert(false && "Unsupported platform");
#endif
if (!library) return;
#if defined(__linux__) || defined(__APPLE__)
vkGetInstanceProcAddr =
reinterpret_cast<PFN_vkGetInstanceProcAddr>(dlsym(library, "vkGetInstanceProcAddr"));
#elif defined(_WIN32)
vkGetInstanceProcAddr =
reinterpret_cast<PFN_vkGetInstanceProcAddr>(GetProcAddress(library, "vkGetInstanceProcAddr"));
#endif
}
void close() {
#if defined(__linux__) || defined(__APPLE__)
dlclose(library);
#elif defined(_WIN32)
FreeLibrary(library);
#endif
library = 0;
}
void init(VkInstance instance) {
vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr(instance, "vkGetDeviceProcAddr");
vkDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR)vkGetInstanceProcAddr(instance, "vkDestroySurfaceKHR");
}
void init(VkDevice device) {
vkCreateRenderPass = (PFN_vkCreateRenderPass)vkGetDeviceProcAddr(device, "vkCreateRenderPass");
vkCreateShaderModule = (PFN_vkCreateShaderModule)vkGetDeviceProcAddr(device, "vkCreateShaderModule");
vkCreatePipelineLayout =
(PFN_vkCreatePipelineLayout)vkGetDeviceProcAddr(device, "vkCreatePipelineLayout");
vkCreateGraphicsPipelines =
(PFN_vkCreateGraphicsPipelines)vkGetDeviceProcAddr(device, "vkCreateGraphicsPipelines");
vkDestroyShaderModule = (PFN_vkDestroyShaderModule)vkGetDeviceProcAddr(device, "vkDestroyShaderModule");
vkCreateFramebuffer = (PFN_vkCreateFramebuffer)vkGetDeviceProcAddr(device, "vkCreateFramebuffer");
vkCreateCommandPool = (PFN_vkCreateCommandPool)vkGetDeviceProcAddr(device, "vkCreateCommandPool");
vkAllocateCommandBuffers =
(PFN_vkAllocateCommandBuffers)vkGetDeviceProcAddr(device, "vkAllocateCommandBuffers");
vkBeginCommandBuffer = (PFN_vkBeginCommandBuffer)vkGetDeviceProcAddr(device, "vkBeginCommandBuffer");
vkEndCommandBuffer = (PFN_vkEndCommandBuffer)vkGetDeviceProcAddr(device, "vkEndCommandBuffer");
vkCmdSetViewport = (PFN_vkCmdSetViewport)vkGetDeviceProcAddr(device, "vkCmdSetViewport");
vkCmdSetScissor = (PFN_vkCmdSetScissor)vkGetDeviceProcAddr(device, "vkCmdSetScissor");
vkCmdBeginRenderPass = (PFN_vkCmdBeginRenderPass)vkGetDeviceProcAddr(device, "vkCmdBeginRenderPass");
vkCmdEndRenderPass = (PFN_vkCmdEndRenderPass)vkGetDeviceProcAddr(device, "vkCmdEndRenderPass");
vkCmdBindPipeline = (PFN_vkCmdBindPipeline)vkGetDeviceProcAddr(device, "vkCmdBindPipeline");
vkCmdDraw = (PFN_vkCmdDraw)vkGetDeviceProcAddr(device, "vkCmdDraw");
vkCreateSemaphore = (PFN_vkCreateSemaphore)vkGetDeviceProcAddr(device, "vkCreateSemaphore");
vkCreateFence = (PFN_vkCreateFence)vkGetDeviceProcAddr(device, "vkCreateFence");
vkDeviceWaitIdle = (PFN_vkDeviceWaitIdle)vkGetDeviceProcAddr(device, "vkDeviceWaitIdle");
vkDestroyCommandPool = (PFN_vkDestroyCommandPool)vkGetDeviceProcAddr(device, "vkDestroyCommandPool");
vkDestroyFramebuffer = (PFN_vkDestroyFramebuffer)vkGetDeviceProcAddr(device, "vkDestroyFramebuffer");
vkWaitForFences = (PFN_vkWaitForFences)vkGetDeviceProcAddr(device, "vkWaitForFences");
vkAcquireNextImageKHR = (PFN_vkAcquireNextImageKHR)vkGetDeviceProcAddr(device, "vkAcquireNextImageKHR");
vkResetFences = (PFN_vkResetFences)vkGetDeviceProcAddr(device, "vkResetFences");
vkQueueSubmit = (PFN_vkQueueSubmit)vkGetDeviceProcAddr(device, "vkQueueSubmit");
vkQueuePresentKHR = (PFN_vkQueuePresentKHR)vkGetDeviceProcAddr(device, "vkQueuePresentKHR");
vkDestroySemaphore = (PFN_vkDestroySemaphore)vkGetDeviceProcAddr(device, "vkDestroySemaphore");
vkDestroyFence = (PFN_vkDestroyFence)vkGetDeviceProcAddr(device, "vkDestroyFence");
vkDestroyPipeline = (PFN_vkDestroyPipeline)vkGetDeviceProcAddr(device, "vkDestroyPipeline");
vkDestroyPipelineLayout =
(PFN_vkDestroyPipelineLayout)vkGetDeviceProcAddr(device, "vkDestroyPipelineLayout");
vkDestroyRenderPass = (PFN_vkDestroyRenderPass)vkGetDeviceProcAddr(device, "vkDestroyRenderPass");
}
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = VK_NULL_HANDLE;
PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr = VK_NULL_HANDLE;
PFN_vkCreateRenderPass vkCreateRenderPass = VK_NULL_HANDLE;
PFN_vkCreateShaderModule vkCreateShaderModule = VK_NULL_HANDLE;
PFN_vkCreatePipelineLayout vkCreatePipelineLayout = VK_NULL_HANDLE;
PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines = VK_NULL_HANDLE;
PFN_vkDestroyShaderModule vkDestroyShaderModule = VK_NULL_HANDLE;
PFN_vkCreateFramebuffer vkCreateFramebuffer = VK_NULL_HANDLE;
PFN_vkCreateCommandPool vkCreateCommandPool = VK_NULL_HANDLE;
PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers = VK_NULL_HANDLE;
PFN_vkBeginCommandBuffer vkBeginCommandBuffer = VK_NULL_HANDLE;
PFN_vkEndCommandBuffer vkEndCommandBuffer = VK_NULL_HANDLE;
PFN_vkCmdSetViewport vkCmdSetViewport = VK_NULL_HANDLE;
PFN_vkCmdSetScissor vkCmdSetScissor = VK_NULL_HANDLE;
PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass = VK_NULL_HANDLE;
PFN_vkCmdEndRenderPass vkCmdEndRenderPass = VK_NULL_HANDLE;
PFN_vkCmdBindPipeline vkCmdBindPipeline = VK_NULL_HANDLE;
PFN_vkCmdDraw vkCmdDraw = VK_NULL_HANDLE;
PFN_vkCreateSemaphore vkCreateSemaphore = VK_NULL_HANDLE;
PFN_vkCreateFence vkCreateFence = VK_NULL_HANDLE;
PFN_vkDeviceWaitIdle vkDeviceWaitIdle = VK_NULL_HANDLE;
PFN_vkDestroyCommandPool vkDestroyCommandPool = VK_NULL_HANDLE;
PFN_vkDestroyFramebuffer vkDestroyFramebuffer = VK_NULL_HANDLE;
PFN_vkWaitForFences vkWaitForFences = VK_NULL_HANDLE;
PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR = VK_NULL_HANDLE;
PFN_vkResetFences vkResetFences = VK_NULL_HANDLE;
PFN_vkQueueSubmit vkQueueSubmit = VK_NULL_HANDLE;
PFN_vkQueuePresentKHR vkQueuePresentKHR = VK_NULL_HANDLE;
PFN_vkDestroySemaphore vkDestroySemaphore = VK_NULL_HANDLE;
PFN_vkDestroyFence vkDestroyFence = VK_NULL_HANDLE;
PFN_vkDestroyPipeline vkDestroyPipeline = VK_NULL_HANDLE;
PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout = VK_NULL_HANDLE;
PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR = VK_NULL_HANDLE;
PFN_vkDestroyRenderPass vkDestroyRenderPass = VK_NULL_HANDLE;
};

View File

@ -0,0 +1,63 @@
#pragma once
#include <stdio.h>
#include <memory>
#include <iostream>
#if defined(_WIN32)
#include <fcntl.h>
#define NOMINMAX
#include <windows.h>
#endif // _WIN32
#include "../src/VkBootstrap.h"
#if defined(__linux__) || defined(__APPLE__)
#include <dlfcn.h>
#endif
struct VulkanLibrary {
#if defined(__linux__) || defined(__APPLE__)
void* library;
#elif defined(_WIN32)
HMODULE library;
#endif
VulkanLibrary() {
#if defined(__linux__)
library = dlopen("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL);
if (!library) library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
#elif defined(__APPLE__)
library = dlopen("libvulkan.dylib", RTLD_NOW | RTLD_LOCAL);
if (!library) library = dlopen("libvulkan.1.dylib", RTLD_NOW | RTLD_LOCAL);
#elif defined(_WIN32)
library = LoadLibrary(TEXT("vulkan-1.dll"));
#else
assert(false && "Unsupported platform");
#endif
if (!library) return;
#if defined(__linux__) || defined(__APPLE__)
vkGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(dlsym(library, "vkGetInstanceProcAddr"));
#elif defined(_WIN32)
vkGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(GetProcAddress(library, "vkGetInstanceProcAddr"));
#endif
}
void close() {
#if defined(__linux__) || defined(__APPLE__)
dlclose(library);
#elif defined(_WIN32)
FreeLibrary(library);
#endif
library = 0;
}
void init(VkInstance instance) {
vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr(instance, "vkGetDeviceProcAddr");
}
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = VK_NULL_HANDLE;
PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr = VK_NULL_HANDLE;
};

414
tests/vulkan_mock.cpp Normal file
View File

@ -0,0 +1,414 @@
#include "vulkan_mock.hpp"
#include <cstring>
#include <algorithm>
#if defined(__linux__) || defined(__APPLE__)
#include <dlfcn.h>
#endif
#define GPA_IMPL(x) \
if (strcmp(pName, #x) == 0) { \
return reinterpret_cast<PFN_vkVoidFunction>(shim_##x); \
}
VulkanMock mock;
EXPORT_MACRO VulkanMock& get_vulkan_mock() {
mock = VulkanMock{};
return mock;
}
template <typename T> VkResult fill_out_count_pointer_pair(std::vector<T> const& data_vec, uint32_t* pCount, T* pData) {
if (pCount == nullptr) {
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
if (pData == nullptr) {
if (pCount) *pCount = static_cast<uint32_t>(data_vec.size());
return VK_SUCCESS;
} else {
uint32_t amount_written = 0;
uint32_t amount_to_write = static_cast<uint32_t>(data_vec.size());
if (*pCount < data_vec.size()) {
amount_to_write = *pCount;
}
for (size_t i = 0; i < amount_to_write; i++) {
pData[i] = data_vec[i];
amount_written++;
}
if (*pCount < data_vec.size()) {
*pCount = amount_written;
return VK_INCOMPLETE;
}
*pCount = amount_written;
return VK_SUCCESS;
}
}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkEnumerateInstanceVersion(uint32_t* pApiVersion) {
if (pApiVersion == nullptr) {
return VK_ERROR_DEVICE_LOST;
}
*pApiVersion = mock.api_version;
return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkEnumerateInstanceExtensionProperties(
const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties) {
if (pLayerName) {
for (size_t i = 0; i < mock.instance_layers.size(); i++) {
if (strcmp(mock.instance_layers[i].layerName, pLayerName) == 0) {
return fill_out_count_pointer_pair(mock.per_layer_instance_extension_properties[i], pPropertyCount, pProperties);
}
}
// Layer not found, fill out with empty list
return fill_out_count_pointer_pair({}, pPropertyCount, pProperties);
}
return fill_out_count_pointer_pair(mock.instance_extensions, pPropertyCount, pProperties);
}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkEnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties) {
return fill_out_count_pointer_pair(mock.instance_layers, pPropertyCount, pProperties);
}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkCreateInstance([[maybe_unused]] const VkInstanceCreateInfo* pCreateInfo,
[[maybe_unused]] const VkAllocationCallbacks* pAllocator,
VkInstance* pInstance) {
if (pInstance == nullptr) {
return VK_ERROR_INITIALIZATION_FAILED;
}
*pInstance = reinterpret_cast<VkInstance>(0x0000ABCD);
return VK_SUCCESS;
}
VKAPI_ATTR void VKAPI_CALL shim_vkDestroyInstance(
[[maybe_unused]] VkInstance instance, [[maybe_unused]] const VkAllocationCallbacks* pAllocator) {}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkCreateDebugUtilsMessengerEXT([[maybe_unused]] VkInstance instance,
[[maybe_unused]] const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
[[maybe_unused]] const VkAllocationCallbacks* pAllocator,
VkDebugUtilsMessengerEXT* pMessenger) {
if (instance == nullptr) {
return VK_ERROR_INITIALIZATION_FAILED;
}
*pMessenger = reinterpret_cast<VkDebugUtilsMessengerEXT>(0xDEBE0000DEBE0000);
return VK_SUCCESS;
}
VKAPI_ATTR void VKAPI_CALL shim_vkDestroyDebugUtilsMessengerEXT([[maybe_unused]] VkInstance instance,
[[maybe_unused]] VkDebugUtilsMessengerEXT messenger,
[[maybe_unused]] const VkAllocationCallbacks* pAllocator) {}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkEnumeratePhysicalDevices(
VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) {
if (instance == nullptr) {
return VK_ERROR_INITIALIZATION_FAILED;
}
return fill_out_count_pointer_pair(mock.physical_device_handles, pPhysicalDeviceCount, pPhysicalDevices);
}
VulkanMock::PhysicalDeviceDetails& get_physical_device_details(VkPhysicalDevice physicalDevice) {
for (size_t i = 0; i < mock.physical_device_handles.size(); i++) {
if (mock.physical_device_handles[i] == physicalDevice) return mock.physical_devices_details[i];
}
assert(false && "should never reach here!");
return mock.physical_devices_details.front();
}
VKAPI_ATTR void VKAPI_CALL shim_vkGetPhysicalDeviceFeatures(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures* pFeatures) {
*pFeatures = get_physical_device_details(physicalDevice).features;
}
VKAPI_ATTR void VKAPI_CALL shim_vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties) {
*pProperties = get_physical_device_details(physicalDevice).properties;
}
VKAPI_ATTR void VKAPI_CALL shim_vkGetPhysicalDeviceQueueFamilyProperties(
VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties* pQueueFamilyProperties) {
fill_out_count_pointer_pair(
get_physical_device_details(physicalDevice).queue_family_properties, pQueueFamilyPropertyCount, pQueueFamilyProperties);
}
VKAPI_ATTR void VKAPI_CALL shim_vkGetPhysicalDeviceMemoryProperties(
VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties) {
*pMemoryProperties = get_physical_device_details(physicalDevice).memory_properties;
}
VKAPI_ATTR void VKAPI_CALL shim_vkGetPhysicalDeviceFeatures2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures) {
const auto& phys_dev = get_physical_device_details(physicalDevice);
VkBaseOutStructure* current = static_cast<VkBaseOutStructure*>(pFeatures->pNext);
while (current) {
for (const auto& features_pNext : phys_dev.features_pNextChain) {
if (features_pNext->sType == current->sType) {
VkBaseOutStructure* next = static_cast<VkBaseOutStructure*>(current->pNext);
std::memcpy(current, features_pNext.get(), get_pnext_chain_struct_size(features_pNext->sType));
current->pNext = next;
break;
}
}
current = static_cast<VkBaseOutStructure*>(current->pNext);
}
}
VKAPI_ATTR void VKAPI_CALL shim_vkGetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures) {
shim_vkGetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures);
}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkEnumerateDeviceExtensionProperties(
VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties) {
if (pLayerName) {
for (size_t i = 0; i < mock.instance_layers.size(); i++) {
if (strcmp(mock.instance_layers[i].layerName, pLayerName) == 0) {
return fill_out_count_pointer_pair(mock.per_layer_device_extension_properties[i], pPropertyCount, pProperties);
}
}
// Layer not found, fill out with empty list
return fill_out_count_pointer_pair({}, pPropertyCount, pProperties);
}
return fill_out_count_pointer_pair(get_physical_device_details(physicalDevice).extensions, pPropertyCount, pProperties);
}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkCreateDevice(VkPhysicalDevice physicalDevice,
[[maybe_unused]] const VkDeviceCreateInfo* pCreateInfo,
[[maybe_unused]] const VkAllocationCallbacks* pAllocator,
VkDevice* pDevice) {
if (physicalDevice == nullptr) {
return VK_ERROR_INITIALIZATION_FAILED;
}
*pDevice = reinterpret_cast<VkDevice>(0x00FEDC00);
get_physical_device_details(physicalDevice).created_devices.push_back(*pDevice);
return VK_SUCCESS;
}
VKAPI_ATTR void VKAPI_CALL shim_vkDestroyDevice(
[[maybe_unused]] VkDevice device, [[maybe_unused]] const VkAllocationCallbacks* pAllocator) {
for (auto& physical_devices : mock.physical_devices_details) {
auto it = std::find(std::begin(physical_devices.created_devices), std::end(physical_devices.created_devices), device);
if (it != std::end(physical_devices.created_devices)) {
physical_devices.created_devices.erase(it);
}
}
}
VKAPI_ATTR void VKAPI_CALL shim_vkGetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue) {
for (auto& physical_devices : mock.physical_devices_details) {
auto it = std::find(std::begin(physical_devices.created_devices), std::end(physical_devices.created_devices), device);
if (it != std::end(physical_devices.created_devices)) {
if (queueFamilyIndex < physical_devices.queue_family_properties.size() &&
queueIndex < physical_devices.queue_family_properties[queueFamilyIndex].queueCount) {
*pQueue = reinterpret_cast<VkQueue>(0x0000CCEE);
return;
}
}
}
}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkCreateCommandPool([[maybe_unused]] VkDevice device,
[[maybe_unused]] const VkCommandPoolCreateInfo* pCreateInfo,
[[maybe_unused]] const VkAllocationCallbacks* pAllocator,
VkCommandPool* pCommandPool) {
*pCommandPool = reinterpret_cast<VkCommandPool>(0x0000ABBB);
return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkCreateFence([[maybe_unused]] VkDevice device,
[[maybe_unused]] const VkFenceCreateInfo* pCreateInfo,
[[maybe_unused]] const VkAllocationCallbacks* pAllocator,
VkFence* pFence) {
*pFence = reinterpret_cast<VkFence>(0x0000AAAC);
return VK_SUCCESS;
}
VKAPI_ATTR void VKAPI_CALL shim_vkDestroyFence(
[[maybe_unused]] VkDevice device, [[maybe_unused]] VkFence fence, [[maybe_unused]] const VkAllocationCallbacks* pAllocator) {}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkCreateSwapchainKHR([[maybe_unused]] VkDevice device,
[[maybe_unused]] const VkSwapchainCreateInfoKHR* pCreateInfo,
[[maybe_unused]] const VkAllocationCallbacks* pAllocator,
VkSwapchainKHR* pSwapchain) {
*pSwapchain = reinterpret_cast<VkSwapchainKHR>(0x0000FFFE);
return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkGetSwapchainImagesKHR(
[[maybe_unused]] VkDevice device, [[maybe_unused]] VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages) {
std::vector<VkImage> images = { reinterpret_cast<VkImage>(0x0000EDD0),
reinterpret_cast<VkImage>(0x0000EDD1),
reinterpret_cast<VkImage>(0x0000EDD1) };
return fill_out_count_pointer_pair(images, pSwapchainImageCount, pSwapchainImages);
}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkCreateImageView([[maybe_unused]] VkDevice device,
[[maybe_unused]] const VkImageViewCreateInfo* pCreateInfo,
[[maybe_unused]] const VkAllocationCallbacks* pAllocator,
VkImageView* pView) {
if (pView) *pView = reinterpret_cast<VkImageView>(0x0000CCCE);
return VK_SUCCESS;
}
VKAPI_ATTR void VKAPI_CALL shim_vkDestroyImageView([[maybe_unused]] VkDevice device,
[[maybe_unused]] VkImageView imageView,
[[maybe_unused]] const VkAllocationCallbacks* pAllocator) {}
VKAPI_ATTR void VKAPI_CALL shim_vkDestroySwapchainKHR([[maybe_unused]] VkDevice device,
[[maybe_unused]] VkSwapchainKHR swapchain,
[[maybe_unused]] const VkAllocationCallbacks* pAllocator) {}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkAcquireNextImageKHR([[maybe_unused]] VkDevice device,
[[maybe_unused]] VkSwapchainKHR swapchain,
[[maybe_unused]] uint64_t timeout,
[[maybe_unused]] VkSemaphore semaphore,
[[maybe_unused]] VkFence fence,
[[maybe_unused]] uint32_t* pImageIndex) {
return VK_SUCCESS;
}
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL shim_vkGetDeviceProcAddr(VkDevice device, const char* pName) {
if (device == VK_NULL_HANDLE) {
return nullptr;
}
GPA_IMPL(vkDestroyDevice)
GPA_IMPL(vkGetDeviceQueue)
GPA_IMPL(vkCreateCommandPool)
GPA_IMPL(vkCreateFence)
GPA_IMPL(vkDestroyFence)
GPA_IMPL(vkCreateSwapchainKHR)
GPA_IMPL(vkGetSwapchainImagesKHR)
GPA_IMPL(vkCreateImageView)
GPA_IMPL(vkDestroyImageView)
GPA_IMPL(vkDestroySwapchainKHR)
GPA_IMPL(vkAcquireNextImageKHR)
return nullptr;
}
VKAPI_ATTR void VKAPI_CALL shim_vkDestroySurfaceKHR([[maybe_unused]] VkInstance instance,
[[maybe_unused]] VkSurfaceKHR surface,
[[maybe_unused]] const VkAllocationCallbacks* pAllocator) {}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkGetPhysicalDeviceSurfaceSupportKHR(
VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32* pSupported) {
for (size_t i = 0; i < mock.physical_device_handles.size(); i++) {
if (physicalDevice == mock.physical_device_handles[i]) {
if (queueFamilyIndex >= mock.physical_devices_details[i].queue_family_properties.size()) {
return VK_ERROR_FORMAT_NOT_SUPPORTED;
}
}
}
if (surface && pSupported) *pSupported = true;
return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkGetPhysicalDeviceSurfaceFormatsKHR(
[[maybe_unused]] VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pSurfaceFormatCount, VkSurfaceFormatKHR* pSurfaceFormats) {
for (size_t i = 0; i < mock.surface_handles.size(); i++) {
if (mock.surface_handles[i] == surface) {
return fill_out_count_pointer_pair(mock.surface_details[i].surface_formats, pSurfaceFormatCount, pSurfaceFormats);
}
}
return VK_ERROR_SURFACE_LOST_KHR;
}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkGetPhysicalDeviceSurfacePresentModesKHR(
[[maybe_unused]] VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pPresentModeCount, VkPresentModeKHR* pPresentModes) {
for (size_t i = 0; i < mock.surface_handles.size(); i++) {
if (mock.surface_handles[i] == surface) {
return fill_out_count_pointer_pair(mock.surface_details[i].present_modes, pPresentModeCount, pPresentModes);
}
}
return VK_ERROR_SURFACE_LOST_KHR;
}
VKAPI_ATTR VkResult VKAPI_CALL shim_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
[[maybe_unused]] VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities) {
for (size_t i = 0; i < mock.surface_handles.size(); i++) {
if (mock.surface_handles[i] == surface) {
*pSurfaceCapabilities = mock.surface_details[i].capabilities;
return VK_SUCCESS;
}
}
return VK_ERROR_SURFACE_LOST_KHR;
}
PFN_vkVoidFunction shim_vkGetInstanceProcAddr([[maybe_unused]] VkInstance instance, [[maybe_unused]] const char* pName) {
GPA_IMPL(vkEnumerateInstanceVersion)
GPA_IMPL(vkEnumerateInstanceExtensionProperties)
GPA_IMPL(vkEnumerateInstanceLayerProperties)
GPA_IMPL(vkCreateInstance)
GPA_IMPL(vkDestroyInstance)
GPA_IMPL(vkCreateDebugUtilsMessengerEXT)
GPA_IMPL(vkDestroyDebugUtilsMessengerEXT)
GPA_IMPL(vkEnumeratePhysicalDevices)
GPA_IMPL(vkGetPhysicalDeviceFeatures)
GPA_IMPL(vkGetPhysicalDeviceFeatures2)
GPA_IMPL(vkGetPhysicalDeviceFeatures2KHR)
GPA_IMPL(vkGetPhysicalDeviceProperties)
GPA_IMPL(vkGetPhysicalDeviceQueueFamilyProperties)
GPA_IMPL(vkGetPhysicalDeviceMemoryProperties)
GPA_IMPL(vkEnumerateDeviceExtensionProperties)
GPA_IMPL(vkCreateDevice)
GPA_IMPL(vkGetDeviceProcAddr)
GPA_IMPL(vkGetDeviceQueue)
GPA_IMPL(vkDestroyDevice)
GPA_IMPL(vkDestroySurfaceKHR)
GPA_IMPL(vkGetPhysicalDeviceSurfaceSupportKHR)
GPA_IMPL(vkGetPhysicalDeviceSurfaceFormatsKHR)
GPA_IMPL(vkGetPhysicalDeviceSurfacePresentModesKHR)
GPA_IMPL(vkGetPhysicalDeviceSurfaceCapabilitiesKHR)
// Only used by the tests, not by vk-bootstrap
GPA_IMPL(vkCreateCommandPool)
return nullptr;
}
extern "C" {
EXPORT_MACRO VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char* pName) {
return shim_vkGetInstanceProcAddr(instance, pName);
}
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__)
#define DLSYM_FUNC_NAME dlsym
#elif defined(__APPLE__)
#define DLSYM_FUNC_NAME my_dlsym
#endif
using PFN_DLSYM = void* (*)(void* handle, const char* symbol);
#if defined(__APPLE__)
#define real_dlsym dlsym
#else
PFN_DLSYM real_dlsym = nullptr;
#endif
void* DLSYM_FUNC_NAME([[maybe_unused]] void* handle, const char* symbol) {
if (strcmp(symbol, "vkGetInstanceProcAddr") == 0) {
return reinterpret_cast<void*>(shim_vkGetInstanceProcAddr);
}
return nullptr;
}
/* Shiming functions on apple is limited by the linker prefering to not use functions in the
* executable in loaded dylibs. By adding an interposer, we redirect the linker to use our
* version of the function over the real one, thus shimming the system function.
*/
#if defined(__APPLE__)
#define MACOS_ATTRIB __attribute__((section("__DATA,__interpose")))
#define VOIDCP_CAST(_func) reinterpret_cast<const void*>(&_func)
struct Interposer {
const void* shim_function;
const void* underlying_function;
};
__attribute__((used)) static Interposer _interpose_dlsym MACOS_ATTRIB = { VOIDCP_CAST(my_dlsym), VOIDCP_CAST(dlsym) };
#endif
}

95
tests/vulkan_mock.hpp Normal file
View File

@ -0,0 +1,95 @@
#pragma once
#include <assert.h>
#include <memory>
#include <utility>
#include <string>
#include <vector>
#define VK_NO_PROTOTYPES
#include <vulkan/vulkan.h>
// Helper function to get the size of a struct given a VkStructureType
// Hand written, must be updated to include any used struct.
inline size_t get_pnext_chain_struct_size(VkStructureType type) {
switch (type) {
case (VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES):
return sizeof(VkPhysicalDeviceDescriptorIndexingFeatures);
case (VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES):
return sizeof(VkPhysicalDeviceVulkan11Features);
case (VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES):
return sizeof(VkPhysicalDeviceVulkan12Features);
default:
assert(false && "Must update get_pnext_chain_struct_size(VkStructureType type) to add type!");
}
return 0;
}
struct VulkanMock {
uint32_t api_version = VK_API_VERSION_1_3;
std::vector<VkExtensionProperties> instance_extensions;
std::vector<VkLayerProperties> instance_layers;
std::vector<std::vector<VkExtensionProperties>> per_layer_instance_extension_properties;
std::vector<std::vector<VkExtensionProperties>> per_layer_device_extension_properties;
void add_layer(VkLayerProperties layer_properties,
std::vector<VkExtensionProperties> layer_instance_extensions,
std::vector<VkExtensionProperties> layer_device_extensions) {
instance_layers.push_back(layer_properties);
per_layer_instance_extension_properties.push_back(layer_instance_extensions);
per_layer_instance_extension_properties.push_back(layer_device_extensions);
}
struct SurfaceDetails {
VkSurfaceCapabilitiesKHR capabilities{};
std::vector<VkSurfaceFormatKHR> surface_formats;
std::vector<VkPresentModeKHR> present_modes;
};
std::vector<VkSurfaceKHR> surface_handles;
std::vector<SurfaceDetails> surface_details;
VkSurfaceKHR get_new_surface(SurfaceDetails details) {
size_t new_index = 0x123456789AB + surface_handles.size();
surface_handles.push_back(reinterpret_cast<VkSurfaceKHR>(new_index));
surface_details.push_back(details);
return surface_handles.back();
}
struct PhysicalDeviceDetails {
VkPhysicalDeviceProperties properties{};
VkPhysicalDeviceFeatures features{};
VkPhysicalDeviceMemoryProperties memory_properties{};
std::vector<VkExtensionProperties> extensions;
std::vector<VkQueueFamilyProperties> queue_family_properties;
std::vector<std::unique_ptr<VkBaseOutStructure>> features_pNextChain;
std::vector<VkDevice> created_devices;
template <typename T> void add_features_pNext_struct(T t) {
T* new_type = new T();
*new_type = t;
features_pNextChain.emplace_back(reinterpret_cast<VkBaseOutStructure*>(new_type));
}
};
std::vector<VkPhysicalDevice> physical_device_handles;
std::vector<PhysicalDeviceDetails> physical_devices_details;
void add_physical_device(PhysicalDeviceDetails details) {
size_t new_index = 0x001122334455 + physical_device_handles.size();
physical_device_handles.push_back(reinterpret_cast<VkPhysicalDevice>(new_index));
physical_devices_details.emplace_back(std::move(details));
}
};
#if !defined(EXPORT_MACRO)
#if defined(WIN32)
#define EXPORT_MACRO __declspec(dllexport)
#else
#define EXPORT_MACRO
#endif
#endif
EXPORT_MACRO VulkanMock& get_vulkan_mock();