diff --git a/CMakeLists.txt b/CMakeLists.txt index 611b162..48ba590 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() \ No newline at end of file diff --git a/src/VkBootstrap.cpp b/src/VkBootstrap.cpp index 61fbe6d..17cc198 100644 --- a/src/VkBootstrap.cpp +++ b/src/VkBootstrap.cpp @@ -160,21 +160,17 @@ detail::Expected 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 layers; @@ -188,7 +184,7 @@ detail::Expected InstanceBuilder::build () bool all_layers_supported = detail::check_layers_supported (layers); if (!all_layers_supported) { - return detail::Error{ 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 InstanceBuilder::build () Instance instance; VkResult res = vkCreateInstance (&instance_create_info, nullptr, &instance.instance); - if (res != VK_SUCCESS) return detail::Error{ 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 InstanceBuilder::build () &instance.debug_messenger); if (res != VK_SUCCESS) { - return detail::Error{ res, "Failed to create setup debug callback" }; + return detail::Error{ res, "Failed to create setup debug callback" }; } } @@ -370,7 +366,7 @@ Expected query_surface_support_details ( VkPhysicalDevice phys_device, VkSurfaceKHR surface) { if (surface == VK_NULL_HANDLE) - return Error{ 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 query_surface_support_details ( auto formats = detail::get_vector ( vkGetPhysicalDeviceSurfaceFormatsKHR, phys_device, surface); if (!formats.has_value ()) - return detail::Error{ 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 ( vkGetPhysicalDeviceSurfacePresentModesKHR, phys_device, surface); if (!present_modes.has_value ()) - return detail::Error{ 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 extensions) +std::vector check_device_extension_support ( + VkPhysicalDevice device, std::vector desired_extensions) { auto available_extensions = detail::get_vector (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 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 PhysicalDeviceSelector::select () auto physical_devices = detail::get_vector (vkEnumeratePhysicalDevices, info.instance); if (!physical_devices.has_value ()) { - return detail::Error{ 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 PhysicalDeviceSelector::select () if (physical_device.phys_device == VK_NULL_HANDLE) { - return detail::Error{ 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 DeviceBuilder::build () { @@ -787,8 +799,8 @@ detail::Expected DeviceBuilder::build () std::vector 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 DeviceBuilder::build () vkCreateDevice (info.physical_device.phys_device, &device_create_info, nullptr, &device.device); if (res != VK_SUCCESS) { - return detail::Error{ 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 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 get_queue_present (Device const& device) detail::Expected get_queue_graphics (Device const& device, uint32_t index) { if (index >= device.physical_device.queue_family_properties.count_graphics) - return detail::Error{ 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 get_queue_compute (Device const& device, uint32_t index) { if (index >= device.physical_device.queue_family_properties.count_compute) - return detail::Error{ 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 get_queue_transfer (Device const& device, uint32_t index) { if (index >= device.physical_device.queue_family_properties.count_transfer) - return detail::Error{ 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 get_queue_sparse (Device const& device, uint32_t index) { if (index >= device.physical_device.queue_family_properties.count_sparse) - return detail::Error{ 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 const& availableFormats) +VkSurfaceFormatKHR find_surface_format (std::vector const& available_formats, + std::vector 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 const& availablePresentModes) +VkPresentModeKHR find_present_mode (std::vector const& available_resent_modes, + std::vector 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 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{ 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 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 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{ res, "Failed to create swapchain" }; + return detail::Error{ res, "Failed to create swapchain" }; } auto swapchain_images = detail::get_vector (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 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 \ No newline at end of file diff --git a/src/VkBootstrap.h b/src/VkBootstrap.h index f4f0788..8b2ef79 100644 --- a/src/VkBootstrap.h +++ b/src/VkBootstrap.h @@ -14,9 +14,14 @@ namespace vkb namespace detail { -template struct Error +enum class BootstrapErrorType : uint32_t { - T error_code; + +}; + +struct Error +{ + VkResult error_code; const char* msg; }; template class Expected @@ -24,22 +29,22 @@ template 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& error) : m_error{ error }, m_init{ false } {} - Expected (Error&& 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{ 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{ std::move (expected.m_error) }; + new (&m_error) Error{ std::move (expected.m_error) }; expected.destroy (); } @@ -57,18 +62,18 @@ template class Expected new (&m_expect) E{ std::move (expect) }; return *this; } - Expected& operator= (const Error& error) + Expected& operator= (const Error& error) { destroy (); m_init = false; - new (&m_error) Error{ error }; + new (&m_error) Error{ error }; return *this; } - Expected& operator= (Error&& error) + Expected& operator= (Error&& error) { destroy (); m_init = false; - new (&m_error) Error{ std::move (error) }; + new (&m_error) Error{ std::move (error) }; return *this; } // clang-format off @@ -81,10 +86,10 @@ template 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& 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 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 class Expected if (m_init) m_expect.~E (); else - m_error.~Error (); + m_error.~Error (); } union { E m_expect; - Error m_error; + Error m_error; }; bool m_init; }; @@ -120,14 +125,14 @@ auto get_vector_init (F&& f, T init, Ts&&... ts) -> Expected, VkR err = f (ts..., &count, nullptr); if (err) { - return Error{ err, "" }; + return Error{ err, "" }; }; results.resize (count, init); err = f (ts..., &count, results.data ()); } while (err == VK_INCOMPLETE); if (err) { - return Error{ 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 extensions); +std::vector device_extension_support (VkPhysicalDevice device, std::vector 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 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 get_queue_present (Device const& device); detail::Expected get_queue_graphics (Device const& device, uint32_t index = 0); detail::Expected get_queue_compute (Device const& device, uint32_t index = 0); @@ -430,9 +443,10 @@ detail::Expected get_queue_sparse (Device const& device, uint namespace detail { -VkSurfaceFormatKHR choose_swapchain_surface_format (std::vector const& availableFormats); -VkPresentModeKHR choose_swap_present_mode (std::vector const& availablePresentModes); -VkExtent2D choose_swap_extent ( +VkSurfaceFormatKHR find_surface_format (std::vector const& available_formats); +VkPresentModeKHR find_present_mode (std::vector const& available_present_modes, + std::vector 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 build (); detail::Expected 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 acceptable_present_modes; + std::vector desired_formats; + std::vector desired_present_modes; uint32_t desired_width = 256; uint32_t desired_height = 256; } info; diff --git a/tests/common.h b/tests/common.h new file mode 100644 index 0000000..1986b18 --- /dev/null +++ b/tests/common.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +#include +#include + +#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; +} \ No newline at end of file diff --git a/tests/run_tests.cpp b/tests/run_tests.cpp index bc4bc16..c38690e 100644 --- a/tests/run_tests.cpp +++ b/tests/run_tests.cpp @@ -1,36 +1,5 @@ -#include +#include "common.h" -#include -#include - -#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 (); diff --git a/tests/shaders/frag.glsl b/tests/shaders/frag.glsl new file mode 100644 index 0000000..e939750 --- /dev/null +++ b/tests/shaders/frag.glsl @@ -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); } diff --git a/tests/shaders/frag.spv b/tests/shaders/frag.spv new file mode 100644 index 0000000..1331eb4 Binary files /dev/null and b/tests/shaders/frag.spv differ diff --git a/tests/shaders/vert.glsl b/tests/shaders/vert.glsl new file mode 100644 index 0000000..8b5aee3 --- /dev/null +++ b/tests/shaders/vert.glsl @@ -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]; +} \ No newline at end of file diff --git a/tests/shaders/vert.spv b/tests/shaders/vert.spv new file mode 100644 index 0000000..c4d9925 Binary files /dev/null and b/tests/shaders/vert.spv differ diff --git a/tests/test.cpp b/tests/test.cpp index f4e4ac9..74242ee 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -1,11 +1,8 @@ #include -#define GLFW_INCLUDE_VULKAN -#include "GLFW/glfw3.h" +#include "common.h" #include -#include "VkBootstrap.h" - #define CATCH_CONFIG_MAIN diff --git a/tests/triangle.cpp b/tests/triangle.cpp new file mode 100644 index 0000000..f220a91 --- /dev/null +++ b/tests/triangle.cpp @@ -0,0 +1,542 @@ +#include + +#include +#include +#include + +#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 swapChainImageViews; + std::vector swapChainFramebuffers; + + VkRenderPass renderPass; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + + VkCommandPool commandPool; + std::vector commandBuffers; + + std::vector imageAvailableSemaphores; + std::vector renderFinishedSemaphores; + std::vector inFlightFences; + std::vector 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 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 buffer (fileSize); + + file.seekg (0); + file.read (buffer.data (), fileSize); + + file.close (); + + return buffer; +} + +VkShaderModule createShaderModule (Init& init, const std::vector& code) +{ + VkShaderModuleCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size (); + createInfo.pCode = reinterpret_cast (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; +} \ No newline at end of file