Added many tests and fixed up api as a consequence

Added device::get_queue_families() to facilitate custom queue setups

get_swapchain_images() renamed to get_image(), made member function to
swapchain.
get_swapchain_image_views() renamed to get_image_views(), also moved to
member function of swapchain

fixed bug with headless instance not being correct

Stopped Catch2 from polluting the buildable targets
This commit is contained in:
Charles Giessen 2020-04-18 21:24:59 -06:00
parent e906cfc093
commit 1f462b42f3
6 changed files with 309 additions and 74 deletions

View File

@ -294,10 +294,8 @@ int create_graphics_pipeline (Init& init, RenderData& data) {
}
int create_framebuffers (Init& init, RenderData& data) {
data.swapchain_images = vkb::get_swapchain_images (init.swapchain).value ();
// init.swapchain.image_count = data.swapchain_images.size ();
data.swapchain_image_views =
vkb::get_swapchain_image_views (init.swapchain, data.swapchain_images).value ();
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 ());

View File

@ -11,4 +11,10 @@ set(GLFW_BUILD_EXAMPLES
OFF
CACHE BOOL "" FORCE)
add_subdirectory(glfw)
set(CATCH_BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(CATCH_ENABLE_WERROR OFF CACHE BOOL "" FORCE)
set(CATCH_INSTALL_DOCS OFF CACHE BOOL "" FORCE)
set(CATCH_INSTALL_HELPERS OFF CACHE BOOL "" FORCE)
set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) #remove Catch2 target spam
add_subdirectory(Catch2)

View File

@ -939,6 +939,9 @@ bool PhysicalDevice::has_dedicated_transfer_queue () const {
bool PhysicalDevice::has_separate_transfer_queue () const {
return detail::get_separate_transfer_queue_index (queue_families) >= 0;
}
std::vector<VkQueueFamilyProperties> PhysicalDevice::get_queue_families () const {
return queue_families;
}
// ---- Queues ---- //
@ -1002,6 +1005,11 @@ detail::Expected<VkQueue, detail::Error<QueueError>> Device::get_dedicated_queue
// ---- Device ---- //
CustomQueueDescription::CustomQueueDescription (uint32_t index, uint32_t count, std::vector<float> priorities)
: index (index), count (count), priorities (priorities) {
assert (count == priorities.size ());
}
void destroy_device (Device device) {
vkDestroyDevice (device.device, device.allocation_callbacks);
}
@ -1039,6 +1047,14 @@ detail::Expected<Device, detail::Error<DeviceError>> DeviceBuilder::build () con
std::vector<const char*> extensions = info.extensions_to_enable;
if (info.surface != VK_NULL_HANDLE) extensions.push_back ({ VK_KHR_SWAPCHAIN_EXTENSION_NAME });
// VUID-VkDeviceCreateInfo-pNext-00373 - don't add pEnabledFeatures if the phys_dev_features_2 is present
bool has_phys_dev_features_2 = false;
for (auto& pNext_struct : info.pNext_chain) {
if (pNext_struct->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2) {
has_phys_dev_features_2 = true;
}
}
VkDeviceCreateInfo device_create_info = {};
device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
detail::setup_pNext_chain (device_create_info, info.pNext_chain);
@ -1047,7 +1063,9 @@ detail::Expected<Device, detail::Error<DeviceError>> DeviceBuilder::build () con
device_create_info.pQueueCreateInfos = queueCreateInfos.data ();
device_create_info.enabledExtensionCount = static_cast<uint32_t> (extensions.size ());
device_create_info.ppEnabledExtensionNames = extensions.data ();
device_create_info.pEnabledFeatures = &info.features;
if (!has_phys_dev_features_2) {
device_create_info.pEnabledFeatures = &info.features;
}
Device device;
VkResult res = vkCreateDevice (info.physical_device.physical_device,
@ -1067,15 +1085,13 @@ DeviceBuilder& DeviceBuilder::custom_queue_setup (std::vector<CustomQueueDescrip
info.queue_descriptions = queue_descriptions;
return *this;
}
template <typename T> DeviceBuilder& DeviceBuilder::add_pNext (T* structure) {
info.pNext_chain.push_back (reinterpret_cast<VkBaseOutStructure*> (structure));
return *this;
}
DeviceBuilder& DeviceBuilder::set_allocation_callbacks (VkAllocationCallbacks* callbacks) {
info.allocation_callbacks = callbacks;
return *this;
}
// ---- Swapchain ---- //
namespace detail {
struct SurfaceSupportDetails {
VkSurfaceCapabilitiesKHR capabilities;
@ -1245,7 +1261,10 @@ detail::Expected<Swapchain, detail::Error<SwapchainError>> SwapchainBuilder::bui
swapchain.device = info.device;
swapchain.image_format = surface_format.format;
swapchain.extent = extent;
auto images = get_swapchain_images (swapchain);
auto images = swapchain.get_images ();
if (!images) {
return detail::Error<SwapchainError>{ SwapchainError::failed_get_swapchain_images };
}
swapchain.image_count = static_cast<uint32_t> (images.value ().size ());
swapchain.allocation_callbacks = info.allocation_callbacks;
return swapchain;
@ -1254,27 +1273,28 @@ detail::Expected<Swapchain, detail::Error<SwapchainError>> SwapchainBuilder::rec
Swapchain const& swapchain) const {
return build (swapchain.swapchain);
}
detail::Expected<std::vector<VkImage>, detail::Error<SwapchainError>> get_swapchain_images (
Swapchain const& swapchain) {
auto swapchain_images =
detail::get_vector<VkImage> (vkGetSwapchainImagesKHR, swapchain.device, swapchain.swapchain);
detail::Expected<std::vector<VkImage>, detail::Error<SwapchainError>> Swapchain::get_images () {
auto swapchain_images = detail::get_vector<VkImage> (vkGetSwapchainImagesKHR, device, swapchain);
if (!swapchain_images) {
return detail::Error<SwapchainError>{ SwapchainError::failed_get_swapchain_images,
swapchain_images.error () };
}
return swapchain_images.value ();
}
detail::Expected<std::vector<VkImageView>, detail::Error<SwapchainError>> Swapchain::get_image_views () {
detail::Expected<std::vector<VkImageView>, detail::Error<SwapchainError>>
get_swapchain_image_views (Swapchain const& swapchain, std::vector<VkImage> const& images) {
std::vector<VkImageView> views{ swapchain.image_count };
auto swapchain_images_ret = get_images ();
if (!swapchain_images_ret) return swapchain_images_ret.error ();
auto swapchain_images = swapchain_images_ret.value ();
for (size_t i = 0; i < swapchain.image_count; i++) {
std::vector<VkImageView> views{ swapchain_images.size () };
for (size_t i = 0; i < swapchain_images.size (); i++) {
VkImageViewCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.image = images[i];
createInfo.image = swapchain_images[i];
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
createInfo.format = swapchain.image_format;
createInfo.format = image_format;
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
@ -1285,14 +1305,17 @@ get_swapchain_image_views (Swapchain const& swapchain, std::vector<VkImage> cons
createInfo.subresourceRange.baseArrayLayer = 0;
createInfo.subresourceRange.layerCount = 1;
VkResult res = vkCreateImageView (swapchain.device, &createInfo, nullptr, &views[i]);
VkResult res = vkCreateImageView (device, &createInfo, allocation_callbacks, &views[i]);
if (res != VK_SUCCESS)
return detail::Error<SwapchainError>{ SwapchainError::failed_create_swapchain_image_views, res };
}
return views;
}
void Swapchain::destroy_image_views (std::vector<VkImageView> const& image_views) {
for (auto& image_view : image_views) {
vkDestroyImageView (device, image_view, allocation_callbacks);
}
}
void destroy_swapchain (Swapchain const& swapchain) {
if (swapchain.device != VK_NULL_HANDLE && swapchain.swapchain != VK_NULL_HANDLE)
vkDestroySwapchainKHR (swapchain.device, swapchain.swapchain, swapchain.allocation_callbacks);

View File

@ -197,8 +197,8 @@ class InstanceBuilder {
// Adds an extension to be enabled. Will fail to create an instance if the extension isn't available.
InstanceBuilder& enable_extension (const char* extension_name);
// Headless Mode does not load the required extensions for presentation. Defaults to false.
InstanceBuilder& set_headless (bool headless = false);
// Headless Mode does not load the required extensions for presentation. Defaults to true.
InstanceBuilder& set_headless (bool headless = true);
// Enables the validation layers. Will fail to create an instance if the validation layers aren't available.
InstanceBuilder& enable_validation_layers (bool require_validation = true);
@ -311,6 +311,9 @@ struct PhysicalDevice {
// Has a queue family that supports transfer operations but not graphics.
bool has_separate_transfer_queue () const;
// Advanced: Get the VkQueueFamilyProperties of the device if special queue setup is needed
std::vector<VkQueueFamilyProperties> get_queue_families () const;
private:
std::vector<const char*> extensions_to_enable;
std::vector<VkQueueFamilyProperties> queue_families;
@ -450,16 +453,16 @@ struct Device {
detail::Expected<VkQueue, detail::Error<QueueError>> get_dedicated_queue (QueueType type) const;
};
void destroy_device (Device device);
// For advanced device queue setup
struct CustomQueueDescription {
CustomQueueDescription (uint32_t index, uint32_t count, std::vector<float> priorities)
: index (index), count (count), priorities (priorities) {}
CustomQueueDescription (uint32_t index, uint32_t count, std::vector<float> priorities);
uint32_t index = 0;
uint32_t count = 0;
std::vector<float> priorities;
};
void destroy_device (Device device);
class DeviceBuilder {
public:
// Any features and extensions that are requested/required in PhysicalDeviceSelector are automatically enabled.
@ -473,7 +476,10 @@ class DeviceBuilder {
// Add a structure to the pNext chain of VkDeviceCreateInfo.
// The structure must be valid when DeviceBuilder::build() is called.
template <typename T> DeviceBuilder& add_pNext (T* structure);
template <typename T> DeviceBuilder& add_pNext (T* structure) {
info.pNext_chain.push_back (reinterpret_cast<VkBaseOutStructure*> (structure));
return *this;
}
// Provide custom allocation callbacks.
DeviceBuilder& set_allocation_callbacks (VkAllocationCallbacks* callbacks);
@ -500,15 +506,18 @@ struct Swapchain {
VkFormat image_format = VK_FORMAT_UNDEFINED;
VkExtent2D extent = { 0, 0 };
VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE;
// Returns a vector of VkImage handles to the swapchain
detail::Expected<std::vector<VkImage>, detail::Error<SwapchainError>> get_images ();
// Returns a vector of VkImageView's to the VkImage's of the swapchain
// VkImageViews must be destroyed
detail::Expected<std::vector<VkImageView>, detail::Error<SwapchainError>> get_image_views ();
void destroy_image_views (std::vector<VkImageView> const& image_views);
};
void destroy_swapchain (Swapchain const& swapchain);
detail::Expected<std::vector<VkImage>, detail::Error<SwapchainError>> get_swapchain_images (
Swapchain const& swapchain);
detail::Expected<std::vector<VkImageView>, detail::Error<SwapchainError>>
get_swapchain_image_views (Swapchain const& swapchain, std::vector<VkImage> const& images);
class SwapchainBuilder {
public:
SwapchainBuilder (Device const& device);

View File

@ -5,8 +5,7 @@
// TODO
// Getting queues
// get dedicated vs distinct compute queues
// Swapchain creation
// Swapchain recreation
// changing present modes and/or image formats
TEST_CASE ("Instance with surface") {
@ -55,45 +54,253 @@ TEST_CASE ("Instance with surface") {
}
}
TEST_CASE ("basic instance") {
TEST_CASE ("instance configuration") {
SECTION ("custom debug callback") {
vkb::InstanceBuilder builder;
auto instance_ret =
builder.request_validation_layers ()
.set_app_name ("test app")
.set_app_version (1, 0, 0)
.set_engine_name ("engine_name")
.set_engine_version (9, 9, 9)
.set_debug_callback ([] (VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void *
/*pUserData*/) -> VkBool32 {
auto ms = vkb::to_string_message_severity (messageSeverity);
auto mt = vkb::to_string_message_type (messageType);
printf ("[%s: %s](user defined)\n%s\n", ms, mt, pCallbackData->pMessage);
return VK_FALSE;
})
.build ();
REQUIRE (instance_ret.has_value ());
vkb::destroy_instance (instance_ret.value ());
}
SECTION ("Validation configuration") {
vkb::InstanceBuilder builder;
auto instance_ret =
builder.request_validation_layers ()
.require_api_version (1, 0, 34)
.use_default_debug_messenger ()
.add_validation_feature_enable (VkValidationFeatureEnableEXT::VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT)
.add_validation_feature_disable (
VkValidationFeatureDisableEXT::VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT)
.add_validation_disable (VkValidationCheckEXT::VK_VALIDATION_CHECK_SHADERS_EXT)
.build ();
REQUIRE (instance_ret.has_value ());
vkb::destroy_instance (instance_ret.value ());
}
}
TEST_CASE ("Headless Vulkan") {
vkb::InstanceBuilder builder;
auto instance_ret =
builder.request_validation_layers ()
.set_app_name ("test")
.set_debug_callback ([] (VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void *
/*pUserData*/) -> VkBool32 {
auto ms = vkb::to_string_message_severity (messageSeverity);
auto mt = vkb::to_string_message_type (messageType);
printf ("[%s: %s](user defined)\n%s\n", ms, mt, pCallbackData->pMessage);
return VK_FALSE;
})
.require_api_version (1, 2, 111)
.build ();
auto instance_ret = builder.request_validation_layers ().set_headless ().build ();
REQUIRE (instance_ret.has_value ());
vkb::PhysicalDeviceSelector phys_device_selector (instance_ret.value ());
auto phys_device_ret = phys_device_selector.select ();
REQUIRE (phys_device_ret.has_value ());
auto phys_device = phys_device_ret.value ();
vkb::DeviceBuilder device_builder (phys_device);
auto device_ret = device_builder.build ();
REQUIRE (device_ret.has_value ());
vkb::destroy_device (device_ret.value ());
vkb::destroy_instance (instance_ret.value ());
}
TEST_CASE ("headless instance") {
TEST_CASE ("Device Configuration") {
auto window = create_window_glfw ();
vkb::InstanceBuilder builder;
auto instance_ret = builder.request_validation_layers ().build ();
REQUIRE (instance_ret.has_value ());
auto surface = create_surface_glfw (instance_ret.value ().instance, window);
vkb::PhysicalDeviceSelector phys_device_selector (instance_ret.value ());
auto phys_device_ret = phys_device_selector.set_surface (surface).select ();
REQUIRE (phys_device_ret.has_value ());
auto phys_device = phys_device_ret.value ();
SECTION ("Custom queue setup") {
std::vector<vkb::CustomQueueDescription> queue_descriptions;
auto queue_families = phys_device.get_queue_families ();
for (uint32_t i = 0; i < (uint32_t)queue_families.size (); i++) {
if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
queue_descriptions.push_back (vkb::CustomQueueDescription (
i, queue_families[i].queueCount, std::vector<float> (queue_families[i].queueCount, 1.0f)));
}
}
if (phys_device.has_dedicated_compute_queue ()) {
for (uint32_t i = 0; i < (uint32_t)queue_families.size (); i++) {
if ((queue_families[i].queueFlags & VK_QUEUE_COMPUTE_BIT) &&
(queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0 &&
(queue_families[i].queueFlags & VK_QUEUE_TRANSFER_BIT) == 0)
queue_descriptions.push_back (vkb::CustomQueueDescription (i,
queue_families[i].queueCount,
std::vector<float> (queue_families[i].queueCount, 1.0f)));
}
} else if (phys_device.has_separate_compute_queue ()) {
for (uint32_t i = 0; i < (uint32_t)queue_families.size (); i++) {
if ((queue_families[i].queueFlags & VK_QUEUE_COMPUTE_BIT) &&
((queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) {
queue_descriptions.push_back (vkb::CustomQueueDescription (i,
queue_families[i].queueCount,
std::vector<float> (queue_families[i].queueCount, 1.0f)));
}
}
}
vkb::DeviceBuilder device_builder (phys_device);
auto device_ret = device_builder.custom_queue_setup (queue_descriptions).build ();
REQUIRE (device_ret.has_value ());
vkb::destroy_device (device_ret.value ());
}
SECTION ("VkPhysicalDeviceFeatures2 in pNext Chain") {
VkPhysicalDeviceFeatures2 phys_dev_feat_2{};
vkb::DeviceBuilder device_builder (phys_device);
auto device_ret = device_builder.add_pNext (&phys_dev_feat_2).build ();
REQUIRE (device_ret.has_value ());
vkb::destroy_device (device_ret.value ());
}
destroy_surface (instance_ret.value ().instance, surface);
vkb::destroy_instance (instance_ret.value ());
}
TEST_CASE ("Swapchain") {
GIVEN ("A working instance, window, surface, and device") {
auto window = create_window_glfw ();
vkb::InstanceBuilder builder;
auto instance_ret = builder.request_validation_layers ().build ();
REQUIRE (instance_ret.has_value ());
auto surface = create_surface_glfw (instance_ret.value ().instance, window);
vkb::PhysicalDeviceSelector phys_device_selector (instance_ret.value ());
auto phys_device_ret = phys_device_selector.set_surface (surface).select ();
REQUIRE (phys_device_ret.has_value ());
auto phys_device = phys_device_ret.value ();
vkb::DeviceBuilder device_builder (phys_device);
auto device_ret = device_builder.build ();
REQUIRE (device_ret.has_value ());
vkb::Device device = device_ret.value ();
THEN ("Swapchain can be made") {
vkb::SwapchainBuilder swapchain_builder (device);
auto swapchain_ret = swapchain_builder.build ();
REQUIRE (swapchain_ret.has_value ());
auto swapchain = swapchain_ret.value ();
THEN ("Acquire swapchain images and views") {
auto images = swapchain.get_images ();
REQUIRE (images.has_value ());
REQUIRE (images.value ().size () > 0);
auto image_views = swapchain.get_image_views ();
REQUIRE (image_views.has_value ());
REQUIRE (image_views.value ().size () > 0);
swapchain.destroy_image_views (image_views.value ());
}
vkb::destroy_swapchain (swapchain_ret.value ());
}
AND_THEN ("Swapchain configuration") {
vkb::SwapchainBuilder swapchain_builder (device);
auto swapchain_ret =
swapchain_builder.set_desired_extent (256, 256)
.set_desired_format ({ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR })
.set_desired_present_mode (VK_PRESENT_MODE_IMMEDIATE_KHR)
.build ();
REQUIRE (swapchain_ret.has_value ());
vkb::destroy_swapchain (swapchain_ret.value ());
}
AND_THEN ("Swapchain defaults can be used") {
vkb::SwapchainBuilder swapchain_builder (device);
auto swapchain_ret = swapchain_builder.use_default_format_selection ()
.use_default_present_mode_selection ()
.build ();
REQUIRE (swapchain_ret.has_value ());
vkb::destroy_swapchain (swapchain_ret.value ());
}
AND_THEN ("Swapchain can be recreated") {
vkb::SwapchainBuilder swapchain_builder (device);
auto swapchain_ret = swapchain_builder.build ();
REQUIRE (swapchain_ret.has_value ());
auto swapchain = swapchain_ret.value ();
auto recreated_swapchain_ret = swapchain_builder.recreate (swapchain);
REQUIRE (recreated_swapchain_ret.has_value ());
vkb::destroy_swapchain (recreated_swapchain_ret.value ());
}
vkb::destroy_device (device_ret.value ());
destroy_surface (instance_ret.value ().instance, surface);
vkb::destroy_instance (instance_ret.value ());
}
}
void* VKAPI_PTR shim_vkAllocationFunction (
void* /*pUserData*/, size_t size, size_t /*alignment*/, VkSystemAllocationScope /*allocationScope*/) {
return malloc (size);
}
void* VKAPI_PTR shim_vkReallocationFunction (
void* /*pUserData*/, void* pOriginal, size_t size, size_t /*alignment*/, VkSystemAllocationScope /*allocationScope*/) {
return realloc (pOriginal, size);
}
void VKAPI_PTR shim_vkFreeFunction (void* /*pUserData*/, void* pMemory) { return free (pMemory); }
TEST_CASE ("Allocation Callbacks") {
VkAllocationCallbacks allocation_callbacks{};
allocation_callbacks.pfnAllocation = &shim_vkAllocationFunction;
allocation_callbacks.pfnReallocation = &shim_vkReallocationFunction;
allocation_callbacks.pfnFree = &shim_vkFreeFunction;
auto window = create_window_glfw ();
vkb::InstanceBuilder builder;
auto instance_ret =
builder.request_validation_layers ()
.set_headless ()
.set_app_version (4, 5, 6)
.set_app_name ("headless")
.set_engine_name ("nick")
.require_api_version (1, 0, 34)
.use_default_debug_messenger ()
.add_validation_feature_enable (VkValidationFeatureEnableEXT::VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT)
.add_validation_feature_disable (VkValidationFeatureDisableEXT::VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT)
.add_validation_disable (VkValidationCheckEXT::VK_VALIDATION_CHECK_SHADERS_EXT)
.build ();
builder.request_validation_layers ().set_allocation_callbacks (&allocation_callbacks).build ();
REQUIRE (instance_ret.has_value ());
auto surface = create_surface_glfw (instance_ret.value ().instance, window);
vkb::PhysicalDeviceSelector phys_device_selector (instance_ret.value ());
auto phys_device_ret = phys_device_selector.set_surface (surface).select ();
REQUIRE (phys_device_ret.has_value ());
auto phys_device = phys_device_ret.value ();
vkb::DeviceBuilder device_builder (phys_device);
auto device_ret = device_builder.set_allocation_callbacks (&allocation_callbacks).build ();
REQUIRE (device_ret.has_value ());
vkb::Device device = device_ret.value ();
vkb::SwapchainBuilder swapchain_builder (device);
auto swapchain_ret = swapchain_builder.set_allocation_callbacks (&allocation_callbacks).build ();
REQUIRE (swapchain_ret.has_value ());
auto swapchain = swapchain_ret.value ();
vkb::destroy_swapchain (swapchain_ret.value ());
vkb::destroy_device (device_ret.value ());
destroy_surface (instance_ret.value ().instance, surface);
vkb::destroy_instance (instance_ret.value ());
}

View File

@ -1,8 +0,0 @@
#include <stdio.h>
#include "common.h"
#include <catch2/catch.hpp>
#define CATCH_CONFIG_MAIN