Fixed up swapchain, added an example based on vulkan-tutorial

Now there is a concise example of the library in use all the way to presenting.

Needs to be rewritten to remove exceptions, but for first pass its good enough.
This commit is contained in:
Charles Giessen 2020-02-03 16:23:47 -07:00
parent 37bc741658
commit 39a3c37465
11 changed files with 809 additions and 152 deletions

View File

@ -25,4 +25,13 @@ target_link_libraries(vk-bootstrap-test vk-bootstrap)
target_link_libraries(vk-bootstrap-test glfw)
target_link_libraries(vk-bootstrap-test Catch2)
add_executable(vk-bootstrap-triangle tests/triangle.cpp)
target_link_libraries(vk-bootstrap-triangle vk-bootstrap)
target_link_libraries(vk-bootstrap-triangle glfw)
add_custom_command(
TARGET vk-bootstrap-triangle POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/tests/shaders ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}
)
endif()

View File

@ -160,21 +160,17 @@ detail::Expected<Instance, VkResult> InstanceBuilder::build ()
{
extensions.push_back (VK_KHR_SURFACE_EXTENSION_NAME);
#if defined(_WIN32)
extensions.push_back (VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
extensions.push_back ("VK_KHR_win32_surface");
#elif defined(__ANDROID__)
extensions.push_back (VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
#elif defined(_DIRECT2DISPLAY)
extensions.push_back (VK_KHR_DISPLAY_EXTENSION_NAME);
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
extensions.push_back (VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
#elif defined(VK_USE_PLATFORM_XCB_KHR)
extensions.push_back (VK_KHR_XCB_SURFACE_EXTENSION_NAME);
#elif defined(VK_USE_PLATFORM_X11_HKR)
extensions.push_back (VK_KHR_X11_SURFACE_EXTENSION_NAME);
#elif defined(VK_USE_PLATFORM_IOS_MVK)
extensions.push_back (VK_MVK_IOS_SURFACE_EXTENSION_NAME);
#elif defined(VK_USE_PLATFORM_MACOS_MVK)
extensions.push_back (VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
#elif defined(__linux__)
extensions.push_back ("VK_KHR_xcb_surface");
extensions.push_back ("VK_KHR_xlib_surface");
extensions.push_back ("VK_KHR_wayland_surface");
#elif defined(__APPLE__)
extensions.push_back ("VK_KHR_metal_surface");
#endif
}
std::vector<const char*> layers;
@ -188,7 +184,7 @@ detail::Expected<Instance, VkResult> InstanceBuilder::build ()
bool all_layers_supported = detail::check_layers_supported (layers);
if (!all_layers_supported)
{
return detail::Error<VkResult>{ VK_ERROR_LAYER_NOT_PRESENT, "Not all layers supported!" };
return detail::Error{ VK_ERROR_LAYER_NOT_PRESENT, "Not all layers supported!" };
}
VkDebugUtilsMessengerCreateInfoEXT messengerCreateInfo = {};
@ -236,7 +232,7 @@ detail::Expected<Instance, VkResult> InstanceBuilder::build ()
Instance instance;
VkResult res = vkCreateInstance (&instance_create_info, nullptr, &instance.instance);
if (res != VK_SUCCESS) return detail::Error<VkResult>{ res, "Failed to create instance" };
if (res != VK_SUCCESS) return detail::Error{ res, "Failed to create instance" };
if (info.use_debug_messenger)
{
@ -248,7 +244,7 @@ detail::Expected<Instance, VkResult> InstanceBuilder::build ()
&instance.debug_messenger);
if (res != VK_SUCCESS)
{
return detail::Error<VkResult>{ res, "Failed to create setup debug callback" };
return detail::Error{ res, "Failed to create setup debug callback" };
}
}
@ -370,7 +366,7 @@ Expected<SurfaceSupportDetails, VkResult> query_surface_support_details (
VkPhysicalDevice phys_device, VkSurfaceKHR surface)
{
if (surface == VK_NULL_HANDLE)
return Error<VkResult>{ VK_ERROR_INITIALIZATION_FAILED, "surface handle was null" };
return Error{ VK_ERROR_INITIALIZATION_FAILED, "surface handle was null" };
VkSurfaceCapabilitiesKHR capabilities;
VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR (phys_device, surface, &capabilities);
@ -386,11 +382,11 @@ Expected<SurfaceSupportDetails, VkResult> query_surface_support_details (
auto formats = detail::get_vector<VkSurfaceFormatKHR> (
vkGetPhysicalDeviceSurfaceFormatsKHR, phys_device, surface);
if (!formats.has_value ())
return detail::Error<VkResult>{ formats.error ().error_code, "Couldn't get surface formats" };
return detail::Error{ formats.error ().error_code, "Couldn't get surface formats" };
auto present_modes = detail::get_vector<VkPresentModeKHR> (
vkGetPhysicalDeviceSurfacePresentModesKHR, phys_device, surface);
if (!present_modes.has_value ())
return detail::Error<VkResult>{ formats.error ().error_code, "Couldn't get surface present modes" };
return detail::Error{ formats.error ().error_code, "Couldn't get surface present modes" };
return SurfaceSupportDetails{ capabilities, formats.value (), present_modes.value () };
}
@ -417,24 +413,23 @@ VkFormat find_supported_format (
return VK_FORMAT_UNDEFINED;
}
bool check_device_extension_support (VkPhysicalDevice device, std::vector<std::string> extensions)
std::vector<std::string> check_device_extension_support (
VkPhysicalDevice device, std::vector<std::string> desired_extensions)
{
auto available_extensions =
detail::get_vector<VkExtensionProperties> (vkEnumerateDeviceExtensionProperties, device, nullptr);
if (!available_extensions.has_value ()) return false; // maybe handle error
if (!available_extensions.has_value ()) return {};
bool all_available = true;
std::vector<std::string> extensions_to_enable;
for (const auto& extension : available_extensions.value ())
{
bool found = false;
for (auto& req_ext : extensions)
for (auto& req_ext : desired_extensions)
{
if (req_ext == extension.extensionName) found = true;
if (req_ext == extension.extensionName) extensions_to_enable.push_back (req_ext);
break;
}
if (!found) all_available = false;
}
return all_available;
return extensions_to_enable;
}
detail::QueueFamilies find_queue_families (VkPhysicalDevice phys_device, VkSurfaceKHR surface)
@ -571,13 +566,15 @@ PhysicalDeviceSelector::Suitable PhysicalDeviceSelector::is_device_suitable (VkP
suitable = Suitable::no;
if (criteria.require_present && indices.present == -1) suitable = Suitable::no;
bool required_extensions_supported =
auto required_extensions_supported =
detail::check_device_extension_support (phys_device, criteria.required_extensions);
if (!required_extensions_supported) suitable = Suitable::no;
if (required_extensions_supported.size () != criteria.required_extensions.size ())
suitable = Suitable::no;
bool desired_extensions_supported =
auto desired_extensions_supported =
detail::check_device_extension_support (phys_device, criteria.desired_extensions);
if (!desired_extensions_supported) suitable = Suitable::partial;
if (desired_extensions_supported.size () != criteria.desired_extensions.size ())
suitable = Suitable::partial;
bool swapChainAdequate = false;
@ -647,7 +644,7 @@ detail::Expected<PhysicalDevice, VkResult> PhysicalDeviceSelector::select ()
auto physical_devices = detail::get_vector<VkPhysicalDevice> (vkEnumeratePhysicalDevices, info.instance);
if (!physical_devices.has_value ())
{
return detail::Error<VkResult>{ physical_devices.error ().error_code, "Failed to find physical devices" };
return detail::Error{ physical_devices.error ().error_code, "Failed to find physical devices" };
}
PhysicalDevice physical_device;
@ -667,13 +664,24 @@ detail::Expected<PhysicalDevice, VkResult> PhysicalDeviceSelector::select ()
if (physical_device.phys_device == VK_NULL_HANDLE)
{
return detail::Error<VkResult>{ VK_ERROR_INITIALIZATION_FAILED, "Failed to find a suitable GPU!" };
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "Failed to find a suitable GPU!" };
}
detail::populate_physical_device_details (physical_device);
physical_device.surface = info.surface;
physical_device.physical_device_features = criteria.required_features;
physical_device.queue_family_properties =
detail::find_queue_families (physical_device.phys_device, info.surface);
physical_device.extensions_to_enable.insert (physical_device.extensions_to_enable.end (),
criteria.required_extensions.begin (),
criteria.required_extensions.end ());
auto desired_extensions_supported =
detail::check_device_extension_support (physical_device.phys_device, criteria.desired_extensions);
physical_device.extensions_to_enable.insert (physical_device.extensions_to_enable.end (),
desired_extensions_supported.begin (),
desired_extensions_supported.end ());
return physical_device;
}
@ -758,7 +766,11 @@ struct QueueFamily
int32_t family;
uint32_t count;
};
DeviceBuilder::DeviceBuilder (PhysicalDevice device) { info.physical_device = device; }
DeviceBuilder::DeviceBuilder (PhysicalDevice phys_device)
{
info.physical_device = phys_device;
info.extensions = phys_device.extensions_to_enable;
}
detail::Expected<Device, VkResult> DeviceBuilder::build ()
{
@ -787,8 +799,8 @@ detail::Expected<Device, VkResult> DeviceBuilder::build ()
std::vector<const char*> extensions;
for (auto& ext : info.extensions)
extensions.push_back (ext.c_str ());
// if (info.physical_device.surface != VK_NULL_HANDLE)
// extensions.push_back ({ VK_KHR_SWAPCHAIN_EXTENSION_NAME });
if (info.physical_device.surface != VK_NULL_HANDLE)
extensions.push_back ({ VK_KHR_SWAPCHAIN_EXTENSION_NAME });
VkDeviceCreateInfo device_create_info = {};
device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
@ -805,8 +817,11 @@ detail::Expected<Device, VkResult> DeviceBuilder::build ()
vkCreateDevice (info.physical_device.phys_device, &device_create_info, nullptr, &device.device);
if (res != VK_SUCCESS)
{
return detail::Error<VkResult>{ res, "Couldn't create device" };
return detail::Error{ res, "Couldn't create device" };
}
device.allocator = info.allocator;
device.physical_device = info.physical_device;
device.surface = info.physical_device.surface;
return device;
}
@ -818,6 +833,27 @@ template <typename T> DeviceBuilder& DeviceBuilder::add_pNext (T* structure)
// ---- Queue ---- //
uint32_t get_queue_index_present (Device const& device)
{
return device.physical_device.queue_family_properties.present;
}
uint32_t get_queue_index_graphics (Device const& device)
{
return device.physical_device.queue_family_properties.graphics;
}
uint32_t get_queue_index_compute (Device const& device)
{
return device.physical_device.queue_family_properties.compute;
}
uint32_t get_queue_index_transfer (Device const& device)
{
return device.physical_device.queue_family_properties.transfer;
}
uint32_t get_queue_index_sparse (Device const& device)
{
return device.physical_device.queue_family_properties.sparse;
}
namespace detail
{
VkQueue get_queue (Device const& device, uint32_t family, uint32_t index)
@ -834,63 +870,67 @@ detail::Expected<VkQueue, VkResult> get_queue_present (Device const& device)
detail::Expected<VkQueue, VkResult> get_queue_graphics (Device const& device, uint32_t index)
{
if (index >= device.physical_device.queue_family_properties.count_graphics)
return detail::Error<VkResult>{ VK_ERROR_INITIALIZATION_FAILED,
"requested graphics queue index is out of bounds" };
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "requested graphics queue index is out of bounds" };
return detail::get_queue (device, device.physical_device.queue_family_properties.graphics, index);
}
detail::Expected<VkQueue, VkResult> get_queue_compute (Device const& device, uint32_t index)
{
if (index >= device.physical_device.queue_family_properties.count_compute)
return detail::Error<VkResult>{ VK_ERROR_INITIALIZATION_FAILED,
"requested compute queue index is out of bounds" };
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "requested compute queue index is out of bounds" };
return detail::get_queue (device, device.physical_device.queue_family_properties.compute, index);
}
detail::Expected<VkQueue, VkResult> get_queue_transfer (Device const& device, uint32_t index)
{
if (index >= device.physical_device.queue_family_properties.count_transfer)
return detail::Error<VkResult>{ VK_ERROR_INITIALIZATION_FAILED,
"requested transfer queue index is out of bounds" };
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "requested transfer queue index is out of bounds" };
return detail::get_queue (device, device.physical_device.queue_family_properties.transfer, index);
}
detail::Expected<VkQueue, VkResult> get_queue_sparse (Device const& device, uint32_t index)
{
if (index >= device.physical_device.queue_family_properties.count_sparse)
return detail::Error<VkResult>{ VK_ERROR_INITIALIZATION_FAILED,
"requested sparse queue index is out of bounds" };
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "requested sparse queue index is out of bounds" };
return detail::get_queue (device, device.physical_device.queue_family_properties.sparse, index);
}
namespace detail
{
VkSurfaceFormatKHR choose_swapchain_surface_format (std::vector<VkSurfaceFormatKHR> const& availableFormats)
VkSurfaceFormatKHR find_surface_format (std::vector<VkSurfaceFormatKHR> const& available_formats,
std::vector<VkSurfaceFormatKHR> const& desired_formats)
{
for (const auto& availableFormat : availableFormats)
for (auto const& desired_format : desired_formats)
{
if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
for (auto const& available_format : available_formats)
{
return availableFormat;
// finds the first format that is desired and available
if (desired_format.format == available_format.format &&
desired_format.colorSpace == available_format.colorSpace)
{
return desired_format;
}
}
}
return availableFormats[0];
// use the first available one if any desired formats aren't found
return available_formats[0];
}
VkPresentModeKHR choose_swap_present_mode (std::vector<VkPresentModeKHR> const& availablePresentModes)
VkPresentModeKHR find_present_mode (std::vector<VkPresentModeKHR> const& available_resent_modes,
std::vector<VkPresentModeKHR> const& desired_present_modes)
{
for (const auto& availablePresentMode : availablePresentModes)
for (auto const& desired_pm : desired_present_modes)
{
if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR)
for (auto const& available_pm : available_resent_modes)
{
return availablePresentMode;
// finds the first present mode that is desired and available
if (desired_pm == available_pm) return desired_pm;
}
}
// only present mode required, use as a fallback
return VK_PRESENT_MODE_FIFO_KHR;
}
VkExtent2D choose_swap_extent (
VkSurfaceCapabilitiesKHR const& capabilities, uint32_t desired_width, uint32_t desired_height)
VkExtent2D find_extent (VkSurfaceCapabilitiesKHR const& capabilities, uint32_t desired_width, uint32_t desired_height)
{
if (capabilities.currentExtent.width != UINT32_MAX)
{
@ -923,12 +963,12 @@ detail::Expected<Swapchain, VkResult> SwapchainBuilder::build ()
{
auto surface_support =
detail::query_surface_support_details (info.physical_device.phys_device, info.surface);
if (!surface_support.has_value ())
return detail::Error<VkResult>{ surface_support.error ().error_code, "can't get surface support" };
VkSurfaceFormatKHR surfaceFormat =
detail::choose_swapchain_surface_format (surface_support.value ().formats);
VkPresentModeKHR presentMode = detail::choose_swap_present_mode (surface_support.value ().present_modes);
VkExtent2D extent = detail::choose_swap_extent (
if (!surface_support.has_value ()) return surface_support.error ();
VkSurfaceFormatKHR surface_format =
detail::find_surface_format (surface_support.value ().formats, info.desired_formats);
VkPresentModeKHR present_mode =
detail::find_present_mode (surface_support.value ().present_modes, info.desired_present_modes);
VkExtent2D extent = detail::find_extent (
surface_support.value ().capabilities, info.desired_width, info.desired_height);
uint32_t imageCount = surface_support.value ().capabilities.minImageCount + 1;
@ -943,8 +983,8 @@ detail::Expected<Swapchain, VkResult> SwapchainBuilder::build ()
swapchain_create_info.surface = info.surface;
swapchain_create_info.minImageCount = imageCount;
swapchain_create_info.imageFormat = surfaceFormat.format;
swapchain_create_info.imageColorSpace = surfaceFormat.colorSpace;
swapchain_create_info.imageFormat = surface_format.format;
swapchain_create_info.imageColorSpace = surface_format.colorSpace;
swapchain_create_info.imageExtent = extent;
swapchain_create_info.imageArrayLayers = 1;
swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
@ -967,19 +1007,23 @@ detail::Expected<Swapchain, VkResult> SwapchainBuilder::build ()
swapchain_create_info.preTransform = surface_support.value ().capabilities.currentTransform;
swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
swapchain_create_info.presentMode = presentMode;
swapchain_create_info.presentMode = present_mode;
swapchain_create_info.clipped = VK_TRUE;
swapchain_create_info.oldSwapchain = info.old_swapchain;
Swapchain swapchain;
VkResult res = vkCreateSwapchainKHR (info.device, &swapchain_create_info, nullptr, &swapchain.swapchain);
if (res != VK_SUCCESS)
{
return detail::Error<VkResult>{ res, "Failed to create swapchain" };
return detail::Error{ res, "Failed to create swapchain" };
}
auto swapchain_images =
detail::get_vector<VkImage> (vkGetSwapchainImagesKHR, info.device, swapchain.swapchain);
swapchain.image_format = surfaceFormat.format;
if (!swapchain_images)
{
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "Failed to get swapchain Images" };
}
swapchain.images = swapchain_images.value ();
swapchain.image_format = surface_format.format;
swapchain.extent = extent;
return swapchain;
@ -990,33 +1034,48 @@ detail::Expected<Swapchain, VkResult> SwapchainBuilder::recreate (Swapchain cons
return build ();
}
void SwapchainBuilder::destroy (Swapchain const& swapchain)
void destroy_swapchain (Swapchain const& swapchain)
{
vkDestroySwapchainKHR (swapchain.device, swapchain.swapchain, swapchain.allocator);
if (swapchain.device != VK_NULL_HANDLE && swapchain.swapchain != VK_NULL_HANDLE &&
swapchain.allocator != VK_NULL_HANDLE)
vkDestroySwapchainKHR (swapchain.device, swapchain.swapchain, swapchain.allocator);
}
SwapchainBuilder& SwapchainBuilder::set_desired_format (VkSurfaceFormatKHR format)
{
info.desired_formats.insert (info.desired_formats.begin (), format);
return *this;
}
SwapchainBuilder& SwapchainBuilder::add_fallback_format (VkSurfaceFormatKHR format)
{
info.desired_formats.push_back (format);
return *this;
}
SwapchainBuilder& SwapchainBuilder::use_default_format_selection ()
{
info.desired_formats.push_back ({ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR });
info.desired_formats.push_back ({ VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR });
return *this;
}
SwapchainBuilder& SwapchainBuilder::set_desired_format (VkFormat format)
{
info.desired_format = format;
return *this;
}
SwapchainBuilder& SwapchainBuilder::set_fallback_format (VkFormat format)
{
info.fallback_format = format;
return *this;
}
SwapchainBuilder& SwapchainBuilder::set_desired_present_mode (VkPresentModeKHR present_mode)
{
info.desired_present_mode = present_mode;
info.desired_present_modes.insert (info.desired_present_modes.begin (), present_mode);
return *this;
}
SwapchainBuilder& SwapchainBuilder::set_fallback_present_mode (VkPresentModeKHR present_mode)
SwapchainBuilder& SwapchainBuilder::add_fallback_present_mode (VkPresentModeKHR present_mode)
{
info.fallback_present_mode = present_mode;
info.desired_present_modes.push_back (present_mode);
return *this;
}
SwapchainBuilder& SwapchainBuilder::use_default_present_mode_selection ()
{
info.desired_present_modes.push_back (VK_PRESENT_MODE_MAILBOX_KHR);
info.desired_present_modes.push_back (VK_PRESENT_MODE_FIFO_KHR);
info.desired_present_modes.push_back (VK_PRESENT_MODE_FIFO_RELAXED_KHR);
info.desired_present_modes.push_back (VK_PRESENT_MODE_IMMEDIATE_KHR);
return *this;
}
} // namespace vkb

View File

@ -14,9 +14,14 @@ namespace vkb
namespace detail
{
template <typename T> struct Error
enum class BootstrapErrorType : uint32_t
{
T error_code;
};
struct Error
{
VkResult error_code;
const char* msg;
};
template <typename E, typename U> class Expected
@ -24,22 +29,22 @@ template <typename E, typename U> class Expected
public:
Expected (const E& expect) : m_expect{ expect }, m_init{ true } {}
Expected (E&& expect) : m_expect{ std::move (expect) }, m_init{ true } {}
Expected (const Error<U>& error) : m_error{ error }, m_init{ false } {}
Expected (Error<U>&& error) : m_error{ std::move (error) }, m_init{ false } {}
Expected (const Error& error) : m_error{ error }, m_init{ false } {}
Expected (Error&& error) : m_error{ std::move (error) }, m_init{ false } {}
~Expected () { destroy (); }
Expected (Expected const& expected) : m_init (expected.m_init)
{
if (m_init)
new (&m_expect) E{ expected.m_expect };
else
new (&m_error) Error<U>{ expected.m_error };
new (&m_error) Error{ expected.m_error };
}
Expected (Expected&& expected) : m_init (expected.m_init)
{
if (m_init)
new (&m_expect) E{ std::move (expected.m_expect) };
else
new (&m_error) Error<U>{ std::move (expected.m_error) };
new (&m_error) Error{ std::move (expected.m_error) };
expected.destroy ();
}
@ -57,18 +62,18 @@ template <typename E, typename U> class Expected
new (&m_expect) E{ std::move (expect) };
return *this;
}
Expected& operator= (const Error<U>& error)
Expected& operator= (const Error& error)
{
destroy ();
m_init = false;
new (&m_error) Error<U>{ error };
new (&m_error) Error{ error };
return *this;
}
Expected& operator= (Error<U>&& error)
Expected& operator= (Error&& error)
{
destroy ();
m_init = false;
new (&m_error) Error<U>{ std::move (error) };
new (&m_error) Error{ std::move (error) };
return *this;
}
// clang-format off
@ -81,10 +86,10 @@ template <typename E, typename U> class Expected
E& value () & { assert (m_init); return m_expect; }
const E&& value () const&& { assert (m_init); return std::move (m_expect); }
E&& value () && { assert (m_init); return std::move (m_expect); }
const Error<U>& error () const& { assert (!m_init); return m_error; }
Error<U>& error () & { assert (!m_init); return m_error; }
const Error<U>&& error () const&& { assert (!m_init); return std::move (m_error); }
Error<U>&& error () && { assert (!m_init); return move (m_error); }
const Error& error () const& { assert (!m_init); return m_error; }
Error& error () & { assert (!m_init); return m_error; }
const Error&& error () const&& { assert (!m_init); return std::move (m_error); }
Error&& error () && { assert (!m_init); return std::move (m_error); }
// clang-format on
bool has_value () const { return m_init; }
explicit operator bool () const { return m_init; }
@ -95,12 +100,12 @@ template <typename E, typename U> class Expected
if (m_init)
m_expect.~E ();
else
m_error.~Error<U> ();
m_error.~Error ();
}
union
{
E m_expect;
Error<U> m_error;
Error m_error;
};
bool m_init;
};
@ -120,14 +125,14 @@ auto get_vector_init (F&& f, T init, Ts&&... ts) -> Expected<std::vector<T>, VkR
err = f (ts..., &count, nullptr);
if (err)
{
return Error<VkResult>{ err, "" };
return Error{ err, "" };
};
results.resize (count, init);
err = f (ts..., &count, results.data ());
} while (err == VK_INCOMPLETE);
if (err)
{
return Error<VkResult>{ err, "" };
return Error{ err, "" };
};
return results;
}
@ -289,7 +294,7 @@ VkFormat find_supported_format (VkPhysicalDevice physical_device,
VkImageTiling tiling,
VkFormatFeatureFlags features);
bool check_device_extension_support (VkPhysicalDevice device, std::vector<std::string> extensions);
std::vector<std::string> device_extension_support (VkPhysicalDevice device, std::vector<std::string> extensions);
detail::QueueFamilies find_queue_families (VkPhysicalDevice physDevice, VkSurfaceKHR windowSurface);
@ -304,11 +309,13 @@ struct PhysicalDevice
VkPhysicalDevice phys_device = VK_NULL_HANDLE;
VkSurfaceKHR surface = VK_NULL_HANDLE;
VkPhysicalDeviceProperties physical_device_properties{};
VkPhysicalDeviceFeatures physical_device_features{};
VkPhysicalDeviceProperties physical_device_properties{};
VkPhysicalDeviceMemoryProperties memory_properties{};
detail::QueueFamilies queue_family_properties;
std::vector<std::string> extensions_to_enable;
};
namespace detail
@ -421,6 +428,12 @@ namespace detail
VkQueue get_queue (Device const& device, uint32_t family, uint32_t index = 0);
}
uint32_t get_queue_index_present (Device const& device);
uint32_t get_queue_index_graphics (Device const& device);
uint32_t get_queue_index_compute (Device const& device);
uint32_t get_queue_index_transfer (Device const& device);
uint32_t get_queue_index_sparse (Device const& device);
detail::Expected<VkQueue, VkResult> get_queue_present (Device const& device);
detail::Expected<VkQueue, VkResult> get_queue_graphics (Device const& device, uint32_t index = 0);
detail::Expected<VkQueue, VkResult> get_queue_compute (Device const& device, uint32_t index = 0);
@ -430,9 +443,10 @@ detail::Expected<VkQueue, VkResult> get_queue_sparse (Device const& device, uint
namespace detail
{
VkSurfaceFormatKHR choose_swapchain_surface_format (std::vector<VkSurfaceFormatKHR> const& availableFormats);
VkPresentModeKHR choose_swap_present_mode (std::vector<VkPresentModeKHR> const& availablePresentModes);
VkExtent2D choose_swap_extent (
VkSurfaceFormatKHR find_surface_format (std::vector<VkFormat> const& available_formats);
VkPresentModeKHR find_present_mode (std::vector<VkPresentModeKHR> const& available_present_modes,
std::vector<VkPresentModeKHR> const& desired_present_modes);
VkExtent2D find_extent (
VkSurfaceCapabilitiesKHR const& capabilities, uint32_t desired_width, uint32_t desired_height);
} // namespace detail
@ -446,6 +460,8 @@ struct Swapchain
VkExtent2D extent = { 0, 0 };
};
void destroy_swapchain (Swapchain const& swapchain);
class SwapchainBuilder
{
public:
@ -453,13 +469,18 @@ class SwapchainBuilder
detail::Expected<Swapchain, VkResult> build ();
detail::Expected<Swapchain, VkResult> recreate (Swapchain const& swapchain);
void destroy (Swapchain const& swapchain);
SwapchainBuilder& set_desired_format (VkFormat format);
SwapchainBuilder& set_fallback_format (VkFormat format);
// SwapchainBuilder& set_desired_image_count (uint32_t count);
// SwapchainBuilder& set_maximum_image_count (uint32_t count);
SwapchainBuilder& set_desired_format (VkSurfaceFormatKHR format);
SwapchainBuilder& add_fallback_format (VkSurfaceFormatKHR format);
SwapchainBuilder& use_default_format_selection ();
SwapchainBuilder& set_desired_present_mode (VkPresentModeKHR present_mode);
SwapchainBuilder& set_fallback_present_mode (VkPresentModeKHR present_mode);
SwapchainBuilder& add_fallback_present_mode (VkPresentModeKHR present_mode);
SwapchainBuilder& use_default_present_mode_selection ();
private:
@ -469,11 +490,8 @@ class SwapchainBuilder
PhysicalDevice physical_device;
VkSurfaceKHR surface = VK_NULL_HANDLE;
VkSwapchainKHR old_swapchain = VK_NULL_HANDLE;
VkFormat desired_format = VK_FORMAT_R8G8B8A8_UNORM;
VkFormat fallback_format = VK_FORMAT_R8G8B8A8_UNORM;
VkPresentModeKHR desired_present_mode = VK_PRESENT_MODE_FIFO_KHR;
VkPresentModeKHR fallback_present_mode = VK_PRESENT_MODE_FIFO_KHR;
std::vector<VkPresentModeKHR> acceptable_present_modes;
std::vector<VkSurfaceFormatKHR> desired_formats;
std::vector<VkPresentModeKHR> desired_present_modes;
uint32_t desired_width = 256;
uint32_t desired_height = 256;
} info;

41
tests/common.h Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#include <stdio.h>
#include <memory>
#include <iostream>
#define GLFW_INCLUDE_VULKAN
#include "GLFW/glfw3.h"
#include "../src/VkBootstrap.h"
GLFWwindow* create_window_glfw ()
{
glfwInit ();
glfwWindowHint (GLFW_CLIENT_API, GLFW_NO_API);
return glfwCreateWindow (640, 480, "Vulkan Triangle", NULL, NULL);
}
void destroy_window_glfw (GLFWwindow* window)
{
glfwDestroyWindow (window);
glfwTerminate ();
}
VkSurfaceKHR create_surface_glfw (VkInstance instance, GLFWwindow* window)
{
VkSurfaceKHR surface = nullptr;
VkResult err = glfwCreateWindowSurface (instance, window, NULL, &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 = nullptr;
}
return surface;
}

View File

@ -1,36 +1,5 @@
#include <stdio.h>
#include "common.h"
#include <memory>
#include <iostream>
#include "VkBootstrap.h"
#define GLFW_INCLUDE_VULKAN
#include "GLFW/glfw3.h"
#include "catch2/catch.hpp"
GLFWwindow* create_window_glfw ()
{
glfwInit ();
glfwWindowHint (GLFW_CLIENT_API, GLFW_NO_API);
return glfwCreateWindow (640, 480, "Window Title", NULL, NULL);
}
void destroy_window_glfw (GLFWwindow* window)
{
glfwDestroyWindow (window);
glfwTerminate ();
}
VkSurfaceKHR create_surface_glfw (VkInstance instance, GLFWwindow* window)
{
VkSurfaceKHR surface = nullptr;
VkResult err = glfwCreateWindowSurface (instance, window, NULL, &surface);
if (err)
{
surface = nullptr;
}
return surface;
}
int test_happy_path ()
{
auto window = create_window_glfw ();

8
tests/shaders/frag.glsl Normal file
View File

@ -0,0 +1,8 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout (location = 0) in vec3 fragColor;
layout (location = 0) out vec4 outColor;
void main () { outColor = vec4 (fragColor, 1.0); }

BIN
tests/shaders/frag.spv Normal file

Binary file not shown.

14
tests/shaders/vert.glsl Normal file
View File

@ -0,0 +1,14 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout (location = 0) out vec3 fragColor;
vec2 positions[3] = vec2[](vec2 (0.0, -0.5), vec2 (0.5, 0.5), vec2 (-0.5, 0.5));
vec3 colors[3] = vec3[](vec3 (1.0, 0.0, 0.0), vec3 (0.0, 1.0, 0.0), vec3 (0.0, 0.0, 1.0));
void main ()
{
gl_Position = vec4 (positions[gl_VertexIndex], 0.0, 1.0);
fragColor = colors[gl_VertexIndex];
}

BIN
tests/shaders/vert.spv Normal file

Binary file not shown.

View File

@ -1,11 +1,8 @@
#include <stdio.h>
#define GLFW_INCLUDE_VULKAN
#include "GLFW/glfw3.h"
#include "common.h"
#include <catch2/catch.hpp>
#include "VkBootstrap.h"
#define CATCH_CONFIG_MAIN

542
tests/triangle.cpp Normal file
View File

@ -0,0 +1,542 @@
#include <stdio.h>
#include <memory>
#include <iostream>
#include <fstream>
#include "common.h"
struct Init
{
GLFWwindow* window;
vkb::Instance instance;
VkSurfaceKHR surface;
vkb::Device device;
vkb::Swapchain swapchain;
};
struct RenderData
{
VkQueue graphicsQueue;
VkQueue presentQueue;
std::vector<VkImageView> swapChainImageViews;
std::vector<VkFramebuffer> swapChainFramebuffers;
VkRenderPass renderPass;
VkPipelineLayout pipelineLayout;
VkPipeline graphicsPipeline;
VkCommandPool commandPool;
std::vector<VkCommandBuffer> commandBuffers;
std::vector<VkSemaphore> imageAvailableSemaphores;
std::vector<VkSemaphore> renderFinishedSemaphores;
std::vector<VkFence> inFlightFences;
std::vector<VkFence> imagesInFlight;
size_t currentFrame = 0;
};
void device_initialization (Init& init)
{
init.window = create_window_glfw ();
vkb::InstanceBuilder instance_builder;
auto instance_ret = instance_builder.set_default_debug_messenger ().setup_validation_layers ().build ();
if (!instance_ret)
{
std::cout << instance_ret.error ().msg << "\n";
}
init.instance = instance_ret.value ();
init.surface = create_surface_glfw (init.instance.instance, init.window);
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 ().msg << "\n";
}
vkb::PhysicalDevice physical_device = phys_device_ret.value ();
vkb::DeviceBuilder device_builder (physical_device);
auto device_ret = device_builder.build ();
if (!device_ret)
{
std::cout << device_ret.error ().msg << "\n";
}
init.device = device_ret.value ();
vkb::SwapchainBuilder swapchain_builder (init.device);
auto swap_ret =
swapchain_builder.use_default_format_selection ().use_default_present_mode_selection ().build ();
if (!swap_ret)
{
std::cout << swap_ret.error ().msg << "\n";
}
init.swapchain = swap_ret.value ();
}
void get_queues (Init& init, RenderData& data)
{
data.graphicsQueue = vkb::get_queue_graphics (init.device).value ();
data.presentQueue = vkb::get_queue_graphics (init.device).value ();
}
void createImageViews (Init& init, RenderData& data)
{
data.swapChainImageViews.resize (init.swapchain.images.size ());
for (size_t i = 0; i < init.swapchain.images.size (); i++)
{
VkImageViewCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.image = init.swapchain.images[i];
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
createInfo.format = init.swapchain.image_format;
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
createInfo.subresourceRange.baseMipLevel = 0;
createInfo.subresourceRange.levelCount = 1;
createInfo.subresourceRange.baseArrayLayer = 0;
createInfo.subresourceRange.layerCount = 1;
if (vkCreateImageView (init.device.device, &createInfo, nullptr, &data.swapChainImageViews[i]) != VK_SUCCESS)
{
throw std::runtime_error ("failed to create image views!");
}
}
}
void createRenderPass (Init& init, RenderData& data)
{
VkAttachmentDescription colorAttachment = {};
colorAttachment.format = init.swapchain.image_format;
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
VkSubpassDependency dependency = {};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
renderPassInfo.dependencyCount = 1;
renderPassInfo.pDependencies = &dependency;
if (vkCreateRenderPass (init.device.device, &renderPassInfo, nullptr, &data.renderPass) != VK_SUCCESS)
{
throw std::runtime_error ("failed to create render pass!");
}
}
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!");
}
size_t fileSize = (size_t)file.tellg ();
std::vector<char> buffer (fileSize);
file.seekg (0);
file.read (buffer.data (), fileSize);
file.close ();
return buffer;
}
VkShaderModule createShaderModule (Init& init, const std::vector<char>& code)
{
VkShaderModuleCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.codeSize = code.size ();
createInfo.pCode = reinterpret_cast<const uint32_t*> (code.data ());
VkShaderModule shaderModule;
if (vkCreateShaderModule (init.device.device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS)
{
throw std::runtime_error ("failed to create shader module!");
}
return shaderModule;
}
void createGraphicsPipeline (Init& init, RenderData& data)
{
auto vertShaderCode = readFile ("vert.spv");
auto fragShaderCode = readFile ("frag.spv");
VkShaderModule vertShaderModule = createShaderModule (init, vertShaderCode);
VkShaderModule fragShaderModule = createShaderModule (init, fragShaderCode);
VkPipelineShaderStageCreateInfo vertShaderStageInfo = {};
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertShaderStageInfo.module = vertShaderModule;
vertShaderStageInfo.pName = "main";
VkPipelineShaderStageCreateInfo fragShaderStageInfo = {};
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragShaderStageInfo.module = fragShaderModule;
fragShaderStageInfo.pName = "main";
VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 0;
vertexInputInfo.vertexAttributeDescriptionCount = 0;
VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssembly.primitiveRestartEnable = VK_FALSE;
VkViewport viewport = {};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float)init.swapchain.extent.width;
viewport.height = (float)init.swapchain.extent.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor = {};
scissor.offset = { 0, 0 };
scissor.extent = init.swapchain.extent;
VkPipelineViewportStateCreateInfo viewportState = {};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.scissorCount = 1;
viewportState.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizer = {};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE;
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
VkPipelineMultisampleStateCreateInfo multisampling = {};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
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.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo colorBlending = {};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
colorBlending.blendConstants[0] = 0.0f;
colorBlending.blendConstants[1] = 0.0f;
colorBlending.blendConstants[2] = 0.0f;
colorBlending.blendConstants[3] = 0.0f;
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 0;
pipelineLayoutInfo.pushConstantRangeCount = 0;
if (vkCreatePipelineLayout (init.device.device, &pipelineLayoutInfo, nullptr, &data.pipelineLayout) != VK_SUCCESS)
{
throw std::runtime_error ("failed to create pipeline layout!");
}
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = shaderStages;
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.layout = data.pipelineLayout;
pipelineInfo.renderPass = data.renderPass;
pipelineInfo.subpass = 0;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
if (vkCreateGraphicsPipelines (
init.device.device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &data.graphicsPipeline) != VK_SUCCESS)
{
throw std::runtime_error ("failed to create graphics pipeline!");
}
vkDestroyShaderModule (init.device.device, fragShaderModule, nullptr);
vkDestroyShaderModule (init.device.device, vertShaderModule, nullptr);
}
void createFramebuffers (Init& init, RenderData& data)
{
data.swapChainFramebuffers.resize (data.swapChainImageViews.size ());
for (size_t i = 0; i < data.swapChainImageViews.size (); i++)
{
VkImageView attachments[] = { data.swapChainImageViews[i] };
VkFramebufferCreateInfo framebufferInfo = {};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = data.renderPass;
framebufferInfo.attachmentCount = 1;
framebufferInfo.pAttachments = attachments;
framebufferInfo.width = init.swapchain.extent.width;
framebufferInfo.height = init.swapchain.extent.height;
framebufferInfo.layers = 1;
if (vkCreateFramebuffer (
init.device.device, &framebufferInfo, nullptr, &data.swapChainFramebuffers[i]) != VK_SUCCESS)
{
throw std::runtime_error ("failed to create framebuffer!");
}
}
}
void createCommandPool (Init& init, RenderData& data)
{
VkCommandPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.queueFamilyIndex = vkb::get_queue_index_graphics (init.device);
if (vkCreateCommandPool (init.device.device, &poolInfo, nullptr, &data.commandPool) != VK_SUCCESS)
{
throw std::runtime_error ("failed to create command pool!");
}
}
void createCommandBuffers (Init& init, RenderData& data)
{
data.commandBuffers.resize (data.swapChainFramebuffers.size ());
VkCommandBufferAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = data.commandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = (uint32_t)data.commandBuffers.size ();
if (vkAllocateCommandBuffers (init.device.device, &allocInfo, data.commandBuffers.data ()) != VK_SUCCESS)
{
throw std::runtime_error ("failed to allocate command buffers!");
}
for (size_t i = 0; i < data.commandBuffers.size (); i++)
{
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
if (vkBeginCommandBuffer (data.commandBuffers[i], &beginInfo) != VK_SUCCESS)
{
throw std::runtime_error ("failed to begin recording command buffer!");
}
VkRenderPassBeginInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = data.renderPass;
renderPassInfo.framebuffer = data.swapChainFramebuffers[i];
renderPassInfo.renderArea.offset = { 0, 0 };
renderPassInfo.renderArea.extent = init.swapchain.extent;
VkClearValue clearColor = { 0.0f, 0.0f, 0.0f, 1.0f };
renderPassInfo.clearValueCount = 1;
renderPassInfo.pClearValues = &clearColor;
vkCmdBeginRenderPass (data.commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline (data.commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, data.graphicsPipeline);
vkCmdDraw (data.commandBuffers[i], 3, 1, 0, 0);
vkCmdEndRenderPass (data.commandBuffers[i]);
if (vkEndCommandBuffer (data.commandBuffers[i]) != VK_SUCCESS)
{
throw std::runtime_error ("failed to record command buffer!");
}
}
}
void createSyncObjects (Init& init, RenderData& data)
{
data.imageAvailableSemaphores.resize (init.swapchain.images.size ());
data.renderFinishedSemaphores.resize (init.swapchain.images.size ());
data.inFlightFences.resize (init.swapchain.images.size ());
data.imagesInFlight.resize (init.swapchain.images.size (), VK_NULL_HANDLE);
VkSemaphoreCreateInfo semaphoreInfo = {};
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
for (size_t i = 0; i < init.swapchain.images.size (); i++)
{
if (vkCreateSemaphore (
init.device.device, &semaphoreInfo, nullptr, &data.imageAvailableSemaphores[i]) != VK_SUCCESS ||
vkCreateSemaphore (
init.device.device, &semaphoreInfo, nullptr, &data.renderFinishedSemaphores[i]) != VK_SUCCESS ||
vkCreateFence (init.device.device, &fenceInfo, nullptr, &data.inFlightFences[i]) != VK_SUCCESS)
{
throw std::runtime_error ("failed to create synchronization objects for a frame!");
}
}
}
void drawFrame (Init& init, RenderData& data)
{
vkWaitForFences (init.device.device, 1, &data.inFlightFences[data.currentFrame], VK_TRUE, UINT64_MAX);
uint32_t imageIndex;
vkAcquireNextImageKHR (init.device.device,
init.swapchain.swapchain,
UINT64_MAX,
data.imageAvailableSemaphores[data.currentFrame],
VK_NULL_HANDLE,
&imageIndex);
if (data.imagesInFlight[imageIndex] != VK_NULL_HANDLE)
{
vkWaitForFences (init.device.device, 1, &data.imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
}
data.imagesInFlight[imageIndex] = data.inFlightFences[data.currentFrame];
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore waitSemaphores[] = { data.imageAvailableSemaphores[data.currentFrame] };
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &data.commandBuffers[imageIndex];
VkSemaphore signalSemaphores[] = { data.renderFinishedSemaphores[data.currentFrame] };
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
vkResetFences (init.device.device, 1, &data.inFlightFences[data.currentFrame]);
if (vkQueueSubmit (data.graphicsQueue, 1, &submitInfo, data.inFlightFences[data.currentFrame]) != VK_SUCCESS)
{
throw std::runtime_error ("failed to submit draw command buffer!");
}
VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
VkSwapchainKHR swapChains[] = { init.swapchain.swapchain };
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex;
vkQueuePresentKHR (data.presentQueue, &presentInfo);
data.currentFrame = (data.currentFrame + 1) % init.swapchain.images.size ();
}
void cleanup (Init& init, RenderData& data)
{
for (size_t i = 0; i < init.swapchain.images.size (); i++)
{
vkDestroySemaphore (init.device.device, data.renderFinishedSemaphores[i], nullptr);
vkDestroySemaphore (init.device.device, data.imageAvailableSemaphores[i], nullptr);
vkDestroyFence (init.device.device, data.inFlightFences[i], nullptr);
}
vkDestroyCommandPool (init.device.device, data.commandPool, nullptr);
for (auto framebuffer : data.swapChainFramebuffers)
{
vkDestroyFramebuffer (init.device.device, framebuffer, nullptr);
}
vkDestroyPipeline (init.device.device, data.graphicsPipeline, nullptr);
vkDestroyPipelineLayout (init.device.device, data.pipelineLayout, nullptr);
vkDestroyRenderPass (init.device.device, data.renderPass, nullptr);
for (auto imageView : data.swapChainImageViews)
{
vkDestroyImageView (init.device.device, imageView, nullptr);
}
vkb::destroy_swapchain (init.swapchain);
vkb::destroy_device (init.device);
vkDestroySurfaceKHR (init.instance.instance, init.surface, nullptr);
vkb::destroy_instance (init.instance);
destroy_window_glfw (init.window);
}
int main ()
{
Init init;
RenderData render_data;
device_initialization (init);
get_queues (init, render_data);
createImageViews (init, render_data);
createRenderPass (init, render_data);
createGraphicsPipeline (init, render_data);
createFramebuffers (init, render_data);
createCommandPool (init, render_data);
createCommandBuffers (init, render_data);
createSyncObjects (init, render_data);
while (!glfwWindowShouldClose (init.window))
{
glfwPollEvents ();
drawFrame (init, render_data);
}
vkDeviceWaitIdle (init.device.device);
cleanup (init, render_data);
return 0;
}