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

View File

@ -5,7 +5,10 @@
#include <fstream>
#include <string>
#include "../tests/common.h"
#include <vulkan/vulkan.h>
#include <GLFW/glfw3.h>
#include <VkBootstrap.h>
#include "example_config.h"
@ -13,13 +16,12 @@ const int MAX_FRAMES_IN_FLIGHT = 2;
struct Init {
GLFWwindow* window;
VulkanLibrary vk_lib;
vkb::Instance instance;
vkb::InstanceDispatchTable inst_disp;
VkSurfaceKHR surface;
vkb::Device device;
vkb::DispatchTable disp;
vkb::Swapchain swapchain;
//convenience
VulkanLibrary* operator->(){ return &vk_lib; }
};
struct RenderData {
@ -44,72 +46,102 @@ struct RenderData {
size_t current_frame = 0;
};
int device_initialization (Init& init) {
init.window = create_window_glfw ("Vulkan Triangle", true);
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;
}
int device_initialization(Init& init) {
init.window = create_window_glfw("Vulkan Triangle", true);
vkb::InstanceBuilder instance_builder;
auto instance_ret = instance_builder.use_default_debug_messenger ().request_validation_layers ().build ();
auto instance_ret = instance_builder.use_default_debug_messenger().request_validation_layers().build();
if (!instance_ret) {
std::cout << instance_ret.error ().message () << "\n";
std::cout << instance_ret.error().message() << "\n";
return -1;
}
init.instance = instance_ret.value ();
init.instance = instance_ret.value();
init.vk_lib.init(init.instance);
init.inst_disp = init.instance.make_table();
init.surface = create_surface_glfw (init.instance, init.window);
init.surface = create_surface_glfw(init.instance, init.window);
vkb::PhysicalDeviceSelector phys_device_selector (init.instance);
auto phys_device_ret = phys_device_selector.set_surface (init.surface).select ();
vkb::PhysicalDeviceSelector phys_device_selector(init.instance);
auto phys_device_ret = phys_device_selector.set_surface(init.surface).select();
if (!phys_device_ret) {
std::cout << phys_device_ret.error ().message () << "\n";
std::cout << phys_device_ret.error().message() << "\n";
return -1;
}
vkb::PhysicalDevice physical_device = phys_device_ret.value ();
vkb::PhysicalDevice physical_device = phys_device_ret.value();
vkb::DeviceBuilder device_builder{ physical_device };
auto device_ret = device_builder.build ();
auto device_ret = device_builder.build();
if (!device_ret) {
std::cout << device_ret.error ().message () << "\n";
std::cout << device_ret.error().message() << "\n";
return -1;
}
init.device = device_ret.value ();
init.vk_lib.init(init.device);
init.device = device_ret.value();
init.disp = init.device.make_table();
return 0;
}
int create_swapchain (Init& init) {
int create_swapchain(Init& init) {
vkb::SwapchainBuilder swapchain_builder{ init.device };
auto swap_ret = swapchain_builder.set_old_swapchain (init.swapchain).build ();
auto swap_ret = swapchain_builder.set_old_swapchain(init.swapchain).build();
if (!swap_ret) {
std::cout << swap_ret.error ().message () << " " << swap_ret.vk_result () << "\n";
std::cout << swap_ret.error().message() << " " << swap_ret.vk_result() << "\n";
return -1;
}
vkb::destroy_swapchain(init.swapchain);
init.swapchain = swap_ret.value ();
init.swapchain = swap_ret.value();
return 0;
}
int get_queues (Init& init, RenderData& data) {
auto gq = init.device.get_queue (vkb::QueueType::graphics);
if (!gq.has_value ()) {
std::cout << "failed to get graphics queue: " << gq.error ().message () << "\n";
int get_queues(Init& init, RenderData& data) {
auto gq = init.device.get_queue(vkb::QueueType::graphics);
if (!gq.has_value()) {
std::cout << "failed to get graphics queue: " << gq.error().message() << "\n";
return -1;
}
data.graphics_queue = gq.value ();
data.graphics_queue = gq.value();
auto pq = init.device.get_queue (vkb::QueueType::present);
if (!pq.has_value ()) {
std::cout << "failed to get present queue: " << pq.error ().message () << "\n";
auto pq = init.device.get_queue(vkb::QueueType::present);
if (!pq.has_value()) {
std::cout << "failed to get present queue: " << pq.error().message() << "\n";
return -1;
}
data.present_queue = pq.value ();
data.present_queue = pq.value();
return 0;
}
int create_render_pass (Init& init, RenderData& data) {
int create_render_pass(Init& init, RenderData& data) {
VkAttachmentDescription color_attachment = {};
color_attachment.format = init.swapchain.image_format;
color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
@ -146,51 +178,51 @@ int create_render_pass (Init& init, RenderData& data) {
render_pass_info.dependencyCount = 1;
render_pass_info.pDependencies = &dependency;
if (init->vkCreateRenderPass (init.device, &render_pass_info, nullptr, &data.render_pass) != VK_SUCCESS) {
if (init.disp.createRenderPass(&render_pass_info, nullptr, &data.render_pass) != VK_SUCCESS) {
std::cout << "failed to create render pass\n";
return -1; // failed to create render pass!
}
return 0;
}
std::vector<char> readFile (const std::string& filename) {
std::ifstream file (filename, std::ios::ate | std::ios::binary);
std::vector<char> readFile(const std::string& filename) {
std::ifstream file(filename, std::ios::ate | std::ios::binary);
if (!file.is_open ()) {
throw std::runtime_error ("failed to open file!");
if (!file.is_open()) {
throw std::runtime_error("failed to open file!");
}
size_t file_size = (size_t)file.tellg ();
std::vector<char> buffer (file_size);
size_t file_size = (size_t)file.tellg();
std::vector<char> buffer(file_size);
file.seekg (0);
file.read (buffer.data (), static_cast<std::streamsize> (file_size));
file.seekg(0);
file.read(buffer.data(), static_cast<std::streamsize>(file_size));
file.close ();
file.close();
return buffer;
}
VkShaderModule createShaderModule (Init& init, const std::vector<char>& code) {
VkShaderModule createShaderModule(Init& init, const std::vector<char>& code) {
VkShaderModuleCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
create_info.codeSize = code.size ();
create_info.pCode = reinterpret_cast<const uint32_t*> (code.data ());
create_info.codeSize = code.size();
create_info.pCode = reinterpret_cast<const uint32_t*>(code.data());
VkShaderModule shaderModule;
if (init->vkCreateShaderModule (init.device, &create_info, nullptr, &shaderModule) != VK_SUCCESS) {
if (init.disp.createShaderModule(&create_info, nullptr, &shaderModule) != VK_SUCCESS) {
return VK_NULL_HANDLE; // failed to create shader module
}
return shaderModule;
}
int create_graphics_pipeline (Init& init, RenderData& data) {
int create_graphics_pipeline(Init& init, RenderData& data) {
auto vert_code = readFile(std::string(EXAMPLE_BUILD_DIRECTORY) + "/vert.spv");
auto frag_code = readFile(std::string(EXAMPLE_BUILD_DIRECTORY) + "/frag.spv");
VkShaderModule vert_module = createShaderModule (init, vert_code);
VkShaderModule frag_module = createShaderModule (init, frag_code);
VkShaderModule vert_module = createShaderModule(init, vert_code);
VkShaderModule frag_module = createShaderModule(init, frag_code);
if (vert_module == VK_NULL_HANDLE || frag_module == VK_NULL_HANDLE) {
std::cout << "failed to create shader module\n";
return -1; // failed to create shader modules
@ -255,8 +287,8 @@ int create_graphics_pipeline (Init& init, RenderData& data) {
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.colorWriteMask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo color_blending = {};
@ -275,8 +307,7 @@ int create_graphics_pipeline (Init& init, RenderData& data) {
pipeline_layout_info.setLayoutCount = 0;
pipeline_layout_info.pushConstantRangeCount = 0;
if (init->vkCreatePipelineLayout (
init.device, &pipeline_layout_info, nullptr, &data.pipeline_layout) != VK_SUCCESS) {
if (init.disp.createPipelineLayout(&pipeline_layout_info, nullptr, &data.pipeline_layout) != VK_SUCCESS) {
std::cout << "failed to create pipeline layout\n";
return -1; // failed to create pipeline layout
}
@ -285,8 +316,8 @@ int create_graphics_pipeline (Init& init, RenderData& data) {
VkPipelineDynamicStateCreateInfo dynamic_info = {};
dynamic_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamic_info.dynamicStateCount = static_cast<uint32_t> (dynamic_states.size ());
dynamic_info.pDynamicStates = dynamic_states.data ();
dynamic_info.dynamicStateCount = static_cast<uint32_t>(dynamic_states.size());
dynamic_info.pDynamicStates = dynamic_states.data();
VkGraphicsPipelineCreateInfo pipeline_info = {};
pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
@ -304,24 +335,23 @@ int create_graphics_pipeline (Init& init, RenderData& data) {
pipeline_info.subpass = 0;
pipeline_info.basePipelineHandle = VK_NULL_HANDLE;
if (init->vkCreateGraphicsPipelines (
init.device, VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &data.graphics_pipeline) != VK_SUCCESS) {
if (init.disp.createGraphicsPipelines(VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &data.graphics_pipeline) != VK_SUCCESS) {
std::cout << "failed to create pipline\n";
return -1; // failed to create graphics pipeline
}
init->vkDestroyShaderModule (init.device, frag_module, nullptr);
init->vkDestroyShaderModule (init.device, vert_module, nullptr);
init.disp.destroyShaderModule(frag_module, nullptr);
init.disp.destroyShaderModule(vert_module, nullptr);
return 0;
}
int create_framebuffers (Init& init, RenderData& data) {
data.swapchain_images = init.swapchain.get_images ().value ();
data.swapchain_image_views = init.swapchain.get_image_views ().value ();
int create_framebuffers(Init& init, RenderData& data) {
data.swapchain_images = init.swapchain.get_images().value();
data.swapchain_image_views = init.swapchain.get_image_views().value();
data.framebuffers.resize (data.swapchain_image_views.size ());
data.framebuffers.resize(data.swapchain_image_views.size());
for (size_t i = 0; i < data.swapchain_image_views.size (); i++) {
for (size_t i = 0; i < data.swapchain_image_views.size(); i++) {
VkImageView attachments[] = { data.swapchain_image_views[i] };
VkFramebufferCreateInfo framebuffer_info = {};
@ -333,43 +363,43 @@ int create_framebuffers (Init& init, RenderData& data) {
framebuffer_info.height = init.swapchain.extent.height;
framebuffer_info.layers = 1;
if (init->vkCreateFramebuffer (init.device, &framebuffer_info, nullptr, &data.framebuffers[i]) != VK_SUCCESS) {
if (init.disp.createFramebuffer(&framebuffer_info, nullptr, &data.framebuffers[i]) != VK_SUCCESS) {
return -1; // failed to create framebuffer
}
}
return 0;
}
int create_command_pool (Init& init, RenderData& data) {
int create_command_pool(Init& init, RenderData& data) {
VkCommandPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
pool_info.queueFamilyIndex = init.device.get_queue_index (vkb::QueueType::graphics).value ();
pool_info.queueFamilyIndex = init.device.get_queue_index(vkb::QueueType::graphics).value();
if (init->vkCreateCommandPool (init.device, &pool_info, nullptr, &data.command_pool) != VK_SUCCESS) {
if (init.disp.createCommandPool(&pool_info, nullptr, &data.command_pool) != VK_SUCCESS) {
std::cout << "failed to create command pool\n";
return -1; // failed to create command pool
}
return 0;
}
int create_command_buffers (Init& init, RenderData& data) {
data.command_buffers.resize (data.framebuffers.size ());
int create_command_buffers(Init& init, RenderData& data) {
data.command_buffers.resize(data.framebuffers.size());
VkCommandBufferAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = data.command_pool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = (uint32_t)data.command_buffers.size ();
allocInfo.commandBufferCount = (uint32_t)data.command_buffers.size();
if (init->vkAllocateCommandBuffers (init.device, &allocInfo, data.command_buffers.data ()) != VK_SUCCESS) {
if (init.disp.allocateCommandBuffers(&allocInfo, data.command_buffers.data()) != VK_SUCCESS) {
return -1; // failed to allocate command buffers;
}
for (size_t i = 0; i < data.command_buffers.size (); i++) {
for (size_t i = 0; i < data.command_buffers.size(); i++) {
VkCommandBufferBeginInfo begin_info = {};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
if (init->vkBeginCommandBuffer (data.command_buffers[i], &begin_info) != VK_SUCCESS) {
if (init.disp.beginCommandBuffer(data.command_buffers[i], &begin_info) != VK_SUCCESS) {
return -1; // failed to begin recording command buffer
}
@ -395,18 +425,18 @@ int create_command_buffers (Init& init, RenderData& data) {
scissor.offset = { 0, 0 };
scissor.extent = init.swapchain.extent;
init->vkCmdSetViewport (data.command_buffers[i], 0, 1, &viewport);
init->vkCmdSetScissor (data.command_buffers[i], 0, 1, &scissor);
init.disp.cmdSetViewport(data.command_buffers[i], 0, 1, &viewport);
init.disp.cmdSetScissor(data.command_buffers[i], 0, 1, &scissor);
init->vkCmdBeginRenderPass (data.command_buffers[i], &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);
init.disp.cmdBeginRenderPass(data.command_buffers[i], &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);
init->vkCmdBindPipeline (data.command_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, data.graphics_pipeline);
init.disp.cmdBindPipeline(data.command_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, data.graphics_pipeline);
init->vkCmdDraw (data.command_buffers[i], 3, 1, 0, 0);
init.disp.cmdDraw(data.command_buffers[i], 3, 1, 0, 0);
init->vkCmdEndRenderPass (data.command_buffers[i]);
init.disp.cmdEndRenderPass(data.command_buffers[i]);
if (init->vkEndCommandBuffer (data.command_buffers[i]) != VK_SUCCESS) {
if (init.disp.endCommandBuffer(data.command_buffers[i]) != VK_SUCCESS) {
std::cout << "failed to record command buffer\n";
return -1; // failed to record command buffer!
}
@ -414,11 +444,11 @@ int create_command_buffers (Init& init, RenderData& data) {
return 0;
}
int create_sync_objects (Init& init, RenderData& data) {
data.available_semaphores.resize (MAX_FRAMES_IN_FLIGHT);
data.finished_semaphore.resize (MAX_FRAMES_IN_FLIGHT);
data.in_flight_fences.resize (MAX_FRAMES_IN_FLIGHT);
data.image_in_flight.resize (init.swapchain.image_count, VK_NULL_HANDLE);
int create_sync_objects(Init& init, RenderData& data) {
data.available_semaphores.resize(MAX_FRAMES_IN_FLIGHT);
data.finished_semaphore.resize(MAX_FRAMES_IN_FLIGHT);
data.in_flight_fences.resize(MAX_FRAMES_IN_FLIGHT);
data.image_in_flight.resize(init.swapchain.image_count, VK_NULL_HANDLE);
VkSemaphoreCreateInfo semaphore_info = {};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
@ -428,9 +458,9 @@ int create_sync_objects (Init& init, RenderData& data) {
fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
if (init->vkCreateSemaphore (init.device, &semaphore_info, nullptr, &data.available_semaphores[i]) != VK_SUCCESS ||
init->vkCreateSemaphore (init.device, &semaphore_info, nullptr, &data.finished_semaphore[i]) != VK_SUCCESS ||
init->vkCreateFence (init.device, &fence_info, nullptr, &data.in_flight_fences[i]) != VK_SUCCESS) {
if (init.disp.createSemaphore(&semaphore_info, nullptr, &data.available_semaphores[i]) != VK_SUCCESS ||
init.disp.createSemaphore(&semaphore_info, nullptr, &data.finished_semaphore[i]) != VK_SUCCESS ||
init.disp.createFence(&fence_info, nullptr, &data.in_flight_fences[i]) != VK_SUCCESS) {
std::cout << "failed to create sync objects\n";
return -1; // failed to create synchronization objects for a frame
}
@ -438,44 +468,40 @@ int create_sync_objects (Init& init, RenderData& data) {
return 0;
}
int recreate_swapchain (Init& init, RenderData& data) {
init->vkDeviceWaitIdle (init.device);
int recreate_swapchain(Init& init, RenderData& data) {
init.disp.deviceWaitIdle();
init->vkDestroyCommandPool (init.device, data.command_pool, nullptr);
init.disp.destroyCommandPool(data.command_pool, nullptr);
for (auto framebuffer : data.framebuffers) {
init->vkDestroyFramebuffer (init.device, framebuffer, nullptr);
init.disp.destroyFramebuffer(framebuffer, nullptr);
}
init.swapchain.destroy_image_views (data.swapchain_image_views);
init.swapchain.destroy_image_views(data.swapchain_image_views);
if (0 != create_swapchain (init)) return -1;
if (0 != create_framebuffers (init, data)) return -1;
if (0 != create_command_pool (init, data)) return -1;
if (0 != create_command_buffers (init, data)) return -1;
if (0 != create_swapchain(init)) return -1;
if (0 != create_framebuffers(init, data)) return -1;
if (0 != create_command_pool(init, data)) return -1;
if (0 != create_command_buffers(init, data)) return -1;
return 0;
}
int draw_frame (Init& init, RenderData& data) {
init->vkWaitForFences (init.device, 1, &data.in_flight_fences[data.current_frame], VK_TRUE, UINT64_MAX);
int draw_frame(Init& init, RenderData& data) {
init.disp.waitForFences(1, &data.in_flight_fences[data.current_frame], VK_TRUE, UINT64_MAX);
uint32_t image_index = 0;
VkResult result = init->vkAcquireNextImageKHR (init.device,
init.swapchain,
UINT64_MAX,
data.available_semaphores[data.current_frame],
VK_NULL_HANDLE,
&image_index);
VkResult result = init.disp.acquireNextImageKHR(
init.swapchain, UINT64_MAX, data.available_semaphores[data.current_frame], VK_NULL_HANDLE, &image_index);
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
return recreate_swapchain (init, data);
return recreate_swapchain(init, data);
} else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
std::cout << "failed to acquire swapchain image. Error " << result << "\n";
return -1;
}
if (data.image_in_flight[image_index] != VK_NULL_HANDLE) {
init->vkWaitForFences (init.device, 1, &data.image_in_flight[image_index], VK_TRUE, UINT64_MAX);
init.disp.waitForFences(1, &data.image_in_flight[image_index], VK_TRUE, UINT64_MAX);
}
data.image_in_flight[image_index] = data.in_flight_fences[data.current_frame];
@ -495,9 +521,9 @@ int draw_frame (Init& init, RenderData& data) {
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signal_semaphores;
init->vkResetFences (init.device, 1, &data.in_flight_fences[data.current_frame]);
init.disp.resetFences(1, &data.in_flight_fences[data.current_frame]);
if (init->vkQueueSubmit (data.graphics_queue, 1, &submitInfo, data.in_flight_fences[data.current_frame]) != VK_SUCCESS) {
if (init.disp.queueSubmit(data.graphics_queue, 1, &submitInfo, data.in_flight_fences[data.current_frame]) != VK_SUCCESS) {
std::cout << "failed to submit draw command buffer\n";
return -1; //"failed to submit draw command buffer
}
@ -514,9 +540,9 @@ int draw_frame (Init& init, RenderData& data) {
present_info.pImageIndices = &image_index;
result = init->vkQueuePresentKHR (data.present_queue, &present_info);
result = init.disp.queuePresentKHR(data.present_queue, &present_info);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
return recreate_swapchain (init, data);
return recreate_swapchain(init, data);
} else if (result != VK_SUCCESS) {
std::cout << "failed to present swapchain image\n";
return -1;
@ -526,56 +552,56 @@ int draw_frame (Init& init, RenderData& data) {
return 0;
}
void cleanup (Init& init, RenderData& data) {
void cleanup(Init& init, RenderData& data) {
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
init->vkDestroySemaphore (init.device, data.finished_semaphore[i], nullptr);
init->vkDestroySemaphore (init.device, data.available_semaphores[i], nullptr);
init->vkDestroyFence (init.device, data.in_flight_fences[i], nullptr);
init.disp.destroySemaphore(data.finished_semaphore[i], nullptr);
init.disp.destroySemaphore(data.available_semaphores[i], nullptr);
init.disp.destroyFence(data.in_flight_fences[i], nullptr);
}
init->vkDestroyCommandPool (init.device, data.command_pool, nullptr);
init.disp.destroyCommandPool(data.command_pool, nullptr);
for (auto framebuffer : data.framebuffers) {
init->vkDestroyFramebuffer (init.device, framebuffer, nullptr);
init.disp.destroyFramebuffer(framebuffer, nullptr);
}
init->vkDestroyPipeline (init.device, data.graphics_pipeline, nullptr);
init->vkDestroyPipelineLayout (init.device, data.pipeline_layout, nullptr);
init->vkDestroyRenderPass (init.device, data.render_pass, nullptr);
init.disp.destroyPipeline(data.graphics_pipeline, nullptr);
init.disp.destroyPipelineLayout(data.pipeline_layout, nullptr);
init.disp.destroyRenderPass(data.render_pass, nullptr);
init.swapchain.destroy_image_views (data.swapchain_image_views);
init.swapchain.destroy_image_views(data.swapchain_image_views);
vkb::destroy_swapchain (init.swapchain);
vkb::destroy_device (init.device);
vkb::destroy_swapchain(init.swapchain);
vkb::destroy_device(init.device);
vkb::destroy_surface(init.instance, init.surface);
vkb::destroy_instance (init.instance);
destroy_window_glfw (init.window);
vkb::destroy_instance(init.instance);
destroy_window_glfw(init.window);
}
int main () {
int main() {
Init init;
RenderData render_data;
if (0 != device_initialization (init)) return -1;
if (0 != create_swapchain (init)) return -1;
if (0 != get_queues (init, render_data)) return -1;
if (0 != create_render_pass (init, render_data)) return -1;
if (0 != create_graphics_pipeline (init, render_data)) return -1;
if (0 != create_framebuffers (init, render_data)) return -1;
if (0 != create_command_pool (init, render_data)) return -1;
if (0 != create_command_buffers (init, render_data)) return -1;
if (0 != create_sync_objects (init, render_data)) return -1;
if (0 != device_initialization(init)) return -1;
if (0 != create_swapchain(init)) return -1;
if (0 != get_queues(init, render_data)) return -1;
if (0 != create_render_pass(init, render_data)) return -1;
if (0 != create_graphics_pipeline(init, render_data)) return -1;
if (0 != create_framebuffers(init, render_data)) return -1;
if (0 != create_command_pool(init, render_data)) return -1;
if (0 != create_command_buffers(init, render_data)) return -1;
if (0 != create_sync_objects(init, render_data)) return -1;
while (!glfwWindowShouldClose (init.window)) {
glfwPollEvents ();
int res = draw_frame (init, render_data);
while (!glfwWindowShouldClose(init.window)) {
glfwPollEvents();
int res = draw_frame(init, render_data);
if (res != 0) {
std::cout << "failed to draw frame \n";
return -1;
}
}
init->vkDeviceWaitIdle (init.device);
init.disp.deviceWaitIdle();
cleanup (init, render_data);
cleanup(init, render_data);
return 0;
}

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();