diff --git a/config/Sources.cmake b/config/Sources.cmake index 80fa3a09d163edf3277eb69f91e7b10e57b72c5d..f00cf4b3c65c9b553b9062210477aff594c27938 100644 --- a/config/Sources.cmake +++ b/config/Sources.cmake @@ -12,4 +12,7 @@ set(vkcv_sources ${vkcv_include}/vkcv/Window.hpp ${vkcv_source}/vkcv/Window.cpp + + ${vkcv_include}/vkcv/SwapChain.hpp + ${vkcv_source}/vkcv/SwapChain.cpp ) diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp index 7a7f3be7d61995462da1f65bd58af098b1dd1f4e..8dfb4593c00ed5a8dc07d90df3fdef9eb43f14e2 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -6,6 +6,8 @@ #include <vulkan/vulkan.hpp> #include "vkcv/Context.hpp" +#include "vkcv/SwapChain.hpp" +#include "vkcv/Window.hpp" #include "vkcv/Handles.hpp" namespace vkcv @@ -24,16 +26,20 @@ namespace vkcv * * @param context encapsulates various Vulkan objects */ - explicit Core(Context &&context) noexcept; + Core(Context &&context, const Window &window, SwapChain swapChain, std::vector<vk::ImageView> imageViews) noexcept; // explicit destruction of default constructor Core() = delete; Context m_Context; + SwapChain m_swapchain; + std::vector<vk::ImageView> m_swapchainImageViews; + const Window& m_window; + public: /** * Destructor of #Core destroys the Vulkan objects contained in the core's context. */ - ~Core() noexcept = default; + ~Core(); /** * Copy-constructor of #Core is deleted! @@ -78,15 +84,14 @@ namespace vkcv * * @param[in] applicationName Name of the application * @param[in] applicationVersion Version of the application - * @param[in] queueCount (optional) Amount of queues which is requested * @param[in] queueFlags (optional) Requested flags of queues * @param[in] instanceExtensions (optional) Requested instance extensions * @param[in] deviceExtensions (optional) Requested device extensions * @return New instance of #Context */ - static Core create(const char *applicationName, + static Core create(const Window &window, + const char *applicationName, uint32_t applicationVersion, - uint32_t queueCount, std::vector<vk::QueueFlagBits> queueFlags = {}, std::vector<const char*> instanceExtensions = {}, std::vector<const char*> deviceExtensions = {}); diff --git a/include/vkcv/SwapChain.hpp b/include/vkcv/SwapChain.hpp new file mode 100644 index 0000000000000000000000000000000000000000..15633badd4c6a3aef9766049317f3f5c8382bab8 --- /dev/null +++ b/include/vkcv/SwapChain.hpp @@ -0,0 +1,63 @@ +#pragma once +#include "vulkan/vulkan.hpp" +#include "Context.hpp" +#include "vkcv/Window.hpp" +#include <iostream> + +namespace vkcv { + class SwapChain final { + private: + + vk::SurfaceKHR m_surface; + vk::SwapchainKHR m_swapchain; + vk::SurfaceFormatKHR m_format; + + /** + * Constructor of a SwapChain object + * glfw is not initialized in this class because ist must be sure that there exists a context first + * glfw is already initialized by the window class + * @param surface used by the swapchain + * @param swapchain to show images in the window + * @param format + */ + SwapChain(vk::SurfaceKHR surface, vk::SwapchainKHR swapchain, vk::SurfaceFormatKHR format); + + public: + SwapChain(const SwapChain &other) = default; + SwapChain(SwapChain &&other) = default; + + /** + * @return The swapchain linked with the #SwapChain class + * @note The reference to our Swapchain variable is needed for the recreation step + */ + [[nodiscard]] + vk::SwapchainKHR getSwapchain(); + + /** + * gets the current surface object + * @return current surface + */ + [[nodiscard]] + vk::SurfaceKHR getSurface(); + /** + * gets the current surface format + * @return gets the surface format + */ + [[nodiscard]] + vk::SurfaceFormatKHR getSurfaceFormat(); + + /** + * creates a swap chain object out of the given window and the given context + * @param window a wrapper that represents a glfw window + * @param context of the application + * @return returns an object of swapChain + */ + static SwapChain create(const Window &window, const Context &context); + + /** + * Destructor of SwapChain + */ + virtual ~SwapChain(); + }; + +} diff --git a/include/vkcv/Window.hpp b/include/vkcv/Window.hpp index 080e55350ba82ae58da8afcc8758da3fda77f19f..ef5e35cc10a9983440d3d33f7d8dd93a6aef5199 100644 --- a/include/vkcv/Window.hpp +++ b/include/vkcv/Window.hpp @@ -4,19 +4,17 @@ * @file src/vkcv/Window.hpp * @brief Window class to handle a basic rendering surface and input */ - -#define GLFW_INCLUDE_VULKAN #include <GLFW/glfw3.h> #define NOMINMAX #include <algorithm> namespace vkcv { - class Window final { private: GLFWwindow *m_window; + /** * * @param GLFWwindow of the class @@ -32,8 +30,7 @@ namespace vkcv { * @param[in] resizable resize ability of the window (optional) * @return Window class */ - static Window create(const char *windowTitle, int width = -1, int height = -1, bool resizable = false); - + static Window create( const char *windowTitle, int width = -1, int height = -1, bool resizable = false); /** * checks if the window is still open, or the close event was called * This function should be changed/removed later on @@ -54,20 +51,6 @@ namespace vkcv { [[nodiscard]] GLFWwindow *getWindow() const; - /** - * gets the current window width - * @return int with window width - */ - [[nodiscard]] - int getWidth() const; - - /** - * gets the current window height - * @return int with window height - */ - [[nodiscard]] - int getHeight() const; - /** * Copy-operator of #Window is deleted! * @@ -84,10 +67,25 @@ namespace vkcv { */ Window &operator=(Window &&other) = default; + /** + * gets the window width + * @param window glfwWindow + * @return int with window width + */ + [[nodiscard]] + int getWidth() const; + + /** + * gets the window height + * @param window glfwWindow + * @return int with window height + */ + [[nodiscard]] + int getHeight() const; + /** * Destructor of #Window, terminates GLFW */ virtual ~Window(); - }; } \ No newline at end of file diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp index 0c981a25dc52db4363d2bc7835df3c3c9d7c49fa..cc84ce171678c94855b7c102b9af3119311cbd0e 100644 --- a/projects/first_triangle/src/main.cpp +++ b/projects/first_triangle/src/main.cpp @@ -1,20 +1,23 @@ #include <iostream> #include <vkcv/Core.hpp> -#include <vkcv/Window.hpp> int main(int argc, const char** argv) { const char* applicationName = "First Triangle"; - vkcv::Window window = vkcv::Window::create( + + vkcv::Window window = vkcv::Window::create( applicationName, - 800, - 600, - false - ); + 800, + 600, + false + ); + vkcv::Core core = vkcv::Core::create( + window, applicationName, VK_MAKE_VERSION(0, 0, 1), - 20, - {vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eTransfer} + {vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute}, + {}, + {"VK_KHR_swapchain"} ); const auto &context = core.getContext(); diff --git a/src/vkcv/Context.cpp b/src/vkcv/Context.cpp index d5261285557f07310c02acd50b6ccd6ca16cac59..761add2c9c0792bbf098c1f7f6122e84978a6735 100644 --- a/src/vkcv/Context.cpp +++ b/src/vkcv/Context.cpp @@ -1,3 +1,4 @@ +#include <iostream> #include "vkcv/Context.hpp" namespace vkcv diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index 0c32f4e42d1a232a9b66d42e6a16f0c1eda06bbb..9f8036975aead0d4e99b5156fcfe5b6edd1bd30d 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -1,6 +1,6 @@ /** - * @authors Sebastian Gaida - * @file src/vkcv/CoreManager.cpp + * @authors Artur Wasmut + * @file src/vkcv/Core.cpp * @brief Handling of global states regarding dependencies */ @@ -78,49 +78,169 @@ namespace vkcv /** - * @brief Creates a candidate list of queues that all meet the desired flags and then creates the maximum possible number - * of queues. If the number of desired queues is not sufficient, the remaining queues are created from the next - * candidate from the list. - * @param physicalDevice The physical device - * @param queueCount The amount of queues to be created - * @param qPriorities - * @param queueFlags The abilities which have to be supported by any created queue - * @return - */ - std::vector<vk::DeviceQueueCreateInfo> getQueueCreateInfos(vk::PhysicalDevice& physicalDevice, - uint32_t queueCount, - std::vector<float> &qPriorities, - std::vector<vk::QueueFlagBits>& queueFlags) + * Given the @p physicalDevice and the @p queuePriorities, the @p queueCreateInfos are computed. First, the requested + * queues are sorted by priority depending on the availability of queues in the queue families of the given + * @p physicalDevice. Then check, if all requested queues are creatable. If so, the @p queueCreateInfos will be computed. + * Furthermore, lists of index pairs (queueFamilyIndex, queueIndex) for later referencing of the separate queues will + * be computed. + * @param[in] physicalDevice The physical device + * @param[in] queuePriorities The queue priorities used for the computation of @p queueCreateInfos + * @param[in] queueFlags The queue flags requesting the queues + * @param[in,out] queueCreateInfos The queue create info structures to be created + * @param[in,out] queuePairsGraphics The list of index pairs (queueFamilyIndex, queueIndex) of queues of type + * vk::QueueFlagBits::eGraphics + * @param[in,out] queuePairsCompute The list of index pairs (queueFamilyIndex, queueIndex) of queues of type + * vk::QueueFlagBits::eCompute + * @param[in,out] queuePairsTransfer The list of index pairs (queueFamilyIndex, queueIndex) of queues of type + * vk::QueueFlagBits::eTransfer + * @throws std::runtime_error If the requested queues from @p queueFlags are not creatable due to insufficient availability. + */ + void queueCreateInfosQueueHandles(vk::PhysicalDevice &physicalDevice, + std::vector<float> &queuePriorities, + std::vector<vk::QueueFlagBits> &queueFlags, + std::vector<vk::DeviceQueueCreateInfo> &queueCreateInfos, + std::vector<std::pair<int, int>> &queuePairsGraphics, + std::vector<std::pair<int, int>> &queuePairsCompute, + std::vector<std::pair<int, int>> &queuePairsTransfer) { - std::vector<vk::DeviceQueueCreateInfo> queueCreateInfos; + queueCreateInfos = {}; + queuePairsGraphics = {}; + queuePairsCompute = {}; + queuePairsTransfer = {}; std::vector<vk::QueueFamilyProperties> qFamilyProperties = physicalDevice.getQueueFamilyProperties(); - std::vector<vk::QueueFamilyProperties> qFamilyCandidates; - // search for queue families which support the desired queue flag bits - for (auto& qFamily : qFamilyProperties) { - bool supported = true; - for (auto qFlag : queueFlags) { - supported = supported && (static_cast<uint32_t>(qFlag & qFamily.queueFlags) != 0); + // DEBUG + std::cout << "Input queue flags:" << std::endl; + for (auto qFlag : queueFlags) { + std::cout << "\t" << to_string(qFlag) << std::endl; + } + + //check priorities of flags -> the lower prioCount the higher the priority + std::vector<int> prios; + for(auto flag: queueFlags){ + int prioCount = 0; + for (int i = 0; i < qFamilyProperties.size(); i++) { + prioCount += (static_cast<uint32_t>(flag & qFamilyProperties[i].queueFlags) != 0) * qFamilyProperties[i].queueCount; } - if (supported) { - qFamilyCandidates.push_back(qFamily); + prios.push_back(prioCount); + std::cout<< "prio Count: " << prioCount << std::endl; + } + //resort flags with heighest priority before allocating the queues + std::vector<vk::QueueFlagBits> newFlags; + for(int i = 0; i < prios.size(); i++){ + auto minElem = std::min_element(prios.begin(), prios.end()); + int index = minElem - prios.begin(); + std::cout << "index: "<< index << std::endl; + newFlags.push_back(queueFlags[index]); + prios[index] = std::numeric_limits<int>::max(); + } + + std::cout << "Sorted queue flags:" << std::endl; + for (auto qFlag : newFlags) { + std::cout << "\t" << to_string(qFlag) << std::endl; + } + + // create requested queues and check if more requested queues are supported + // herefore: create vector that updates available queues in each queue family + // structure: [qFamily_0, ..., qFamily_n] where + // - qFamily_i = [GraphicsCount, ComputeCount, TransferCount], 0 <= i <= n + std::vector<std::vector<int>> queueFamilyStatus, initialQueueFamilyStatus; + + for (auto qFamily : qFamilyProperties) { + int graphicsCount = int(static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eGraphics) != 0) * qFamily.queueCount; + int computeCount = int(static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eCompute) != 0) * qFamily.queueCount;; + int transferCount = int(static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eTransfer) != 0) * qFamily.queueCount;; + queueFamilyStatus.push_back({graphicsCount, computeCount, transferCount}); + } + + initialQueueFamilyStatus = queueFamilyStatus; + + // check if every queue with the specified queue flag can be created + // this automatically checks for queue flag support! + for (auto qFlag : newFlags) { + bool found; + switch (qFlag) { + case vk::QueueFlagBits::eGraphics: + found = false; + for (int i = 0; i < queueFamilyStatus.size() && !found; i++) { + if (queueFamilyStatus[i][0] > 0) { + queuePairsGraphics.push_back(std::pair(i, initialQueueFamilyStatus[i][0] - queueFamilyStatus[i][0])); + queueFamilyStatus[i][0]--; + queueFamilyStatus[i][1]--; + queueFamilyStatus[i][2]--; + std::cout << "Graphics queue available at queue family #" << i << std::endl; + found = true; + } + } + if (!found) { + throw std::runtime_error("Too many graphics queues were requested than being available!"); + } + break; + case vk::QueueFlagBits::eCompute: + found = false; + for (int i = 0; i < queueFamilyStatus.size() && !found; i++) { + if (queueFamilyStatus[i][1] > 0) { + queuePairsCompute.push_back(std::pair(i, initialQueueFamilyStatus[i][1] - queueFamilyStatus[i][1])); + queueFamilyStatus[i][0]--; + queueFamilyStatus[i][1]--; + queueFamilyStatus[i][2]--; + std::cout << "Compute queue available at queue family #" << i << std::endl; + found = true; + } + } + if (!found) { + throw std::runtime_error("Too many compute queues were requested than being available!"); + } + break; + case vk::QueueFlagBits::eTransfer: + found = false; + for (int i = 0; i < queueFamilyStatus.size() && !found; i++) { + if (queueFamilyStatus[i][2] > 0) { + queuePairsTransfer.push_back(std::pair(i, initialQueueFamilyStatus[i][2] - queueFamilyStatus[i][2])); + queueFamilyStatus[i][0]--; + queueFamilyStatus[i][1]--; + queueFamilyStatus[i][2]--; + std::cout << "Transfer queue available at queue family #" << i << std::endl; + found = true; + } + } + if (!found) { + throw std::runtime_error("Too many transfer queues were requested than being available!"); + } + break; + default: + throw std::runtime_error("Invalid input for queue flag bits. Valid inputs are 'vk::QueueFlagBits::eGraphics', 'vk::QueueFlagBits::eCompute' and 'vk::QueueFlagBits::eTransfer'."); } } - uint32_t create = queueCount; - for (uint32_t i = 0; i < qFamilyCandidates.size() && create > 0; i++) { - const uint32_t maxCreatableQueues = std::min(create, qFamilyCandidates[i].queueCount); - vk::DeviceQueueCreateInfo qCreateInfo( - vk::DeviceQueueCreateFlags(), - i, - maxCreatableQueues, - qPriorities.data() - ); - queueCreateInfos.push_back(qCreateInfo); - create -= maxCreatableQueues; + std::cout << "Initial queue status:" << std::endl; + int x = 0; + for (std::vector<int> e : initialQueueFamilyStatus) { + std::cout << "#" << x << ":\t[" << e[0] << ", " << e[1] << ", " << e[2] << "]" << std::endl; + x++; + } + + std::cout << "Actual queue status:" << std::endl; + x = 0; + for (std::vector<int> e : queueFamilyStatus) { + std::cout << "#" << x << ":\t[" << e[0] << ", " << e[1] << ", " << e[2] << "]" << std::endl; + x++; } - return queueCreateInfos; + // create all requested queues + for (int i = 0; i < qFamilyProperties.size(); i++) { + uint32_t create = std::abs(initialQueueFamilyStatus[i][0] - queueFamilyStatus[i][0]); + std::cout << "For Queue Family #" << i << " create " << create << " queues" << std::endl; + if (create > 0) { + vk::DeviceQueueCreateInfo qCreateInfo( + vk::DeviceQueueCreateFlags(), + i, + create, + queuePriorities.data() + ); + queueCreateInfos.push_back(qCreateInfo); + } + } } /** @@ -146,14 +266,42 @@ namespace vkcv return true; } - Core Core::create(const char *applicationName, + + std::vector<const char*> getRequiredExtensions() { + uint32_t glfwExtensionCount = 0; + const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); + +#ifndef NDEBUG + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); +#endif + + return extensions; + } + + /** + * Computes the queue handles from @p queuePairs + * @param queuePairs The queuePairs that were created separately for each queue type (e.g., vk::QueueFlagBits::eGraphics) + * @param device The device + * @return An array of queue handles based on the @p queuePairs + */ + std::vector<vk::Queue> getQueueHandles(const std::vector<std::pair<int, int>> queuePairs, const vk::Device device) { + std::vector<vk::Queue> queueHandles; + for (auto q : queuePairs) { + int queueFamilyIndex = q.first; // the queueIndex of the queue family + int queueIndex = q.second; // the queueIndex within a queue family + queueHandles.push_back(device.getQueue(queueFamilyIndex, queueIndex)); + } + return queueHandles; + } + + Core Core::create(const Window &window, + const char *applicationName, uint32_t applicationVersion, - uint32_t queueCount, std::vector<vk::QueueFlagBits> queueFlags, std::vector<const char *> instanceExtensions, std::vector<const char *> deviceExtensions) { - // check for layer support const std::vector<vk::LayerProperties>& layerProperties = vk::enumerateInstanceLayerProperties(); @@ -190,9 +338,9 @@ namespace vkcv throw std::runtime_error("The requested instance extensions are not supported!"); } -#ifndef NDEBUG - instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); -#endif + // for GLFW: get all required extensions + std::vector<const char*> requiredExtensions = getRequiredExtensions(); + instanceExtensions.insert(instanceExtensions.end(), requiredExtensions.begin(), requiredExtensions.end()); const vk::ApplicationInfo applicationInfo( applicationName, @@ -233,10 +381,12 @@ namespace vkcv //vector to define the queue priorities std::vector<float> qPriorities; - qPriorities.resize(queueCount, 1.f); // all queues have the same priorities + qPriorities.resize(queueFlags.size(), 1.f); // all queues have the same priorities // create required queues - std::vector<vk::DeviceQueueCreateInfo> qCreateInfos = getQueueCreateInfos(physicalDevice, queueCount, qPriorities,queueFlags); + std::vector<vk::DeviceQueueCreateInfo> qCreateInfos; + std::vector<std::pair<int, int>> queuePairsGraphics, queuePairsCompute, queuePairsTransfer; + queueCreateInfosQueueHandles(physicalDevice, qPriorities, queueFlags, qCreateInfos, queuePairsGraphics, queuePairsCompute, queuePairsTransfer); vk::DeviceCreateInfo deviceCreateInfo( vk::DeviceCreateFlags(), @@ -256,10 +406,48 @@ namespace vkcv vk::Device device = physicalDevice.createDevice(deviceCreateInfo); - // TODO: implement device.getQueue() to access the queues, if needed + + // maybe it can be useful to store these lists as member variable of Core + std::vector<vk::Queue> graphicsQueues = getQueueHandles(queuePairsGraphics, device); + std::vector<vk::Queue> computeQueues = getQueueHandles(queuePairsCompute, device); + std::vector<vk::Queue> transferQueues = getQueueHandles(queuePairsTransfer, device); + + // examples for accessing queues + vk::Queue graphicsQueue = graphicsQueues[0]; + vk::Queue computeQueue = computeQueues[0]; + vk::Queue transferQueue = transferQueues[0]; + Context context(instance, physicalDevice, device); - return Core(std::move(context)); + SwapChain swapChain = SwapChain::create(window, context); + + std::vector<vk::Image> swapChainImages = device.getSwapchainImagesKHR(swapChain.getSwapchain()); + std::vector<vk::ImageView> imageViews; + imageViews.reserve( swapChainImages.size() ); + //here can be swizzled with vk::ComponentSwizzle if needed + vk::ComponentMapping componentMapping( + vk::ComponentSwizzle::eR, + vk::ComponentSwizzle::eG, + vk::ComponentSwizzle::eB, + vk::ComponentSwizzle::eA ); + + vk::ImageSubresourceRange subResourceRange( vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 ); + + for ( auto image : swapChainImages ) + { + vk::ImageViewCreateInfo imageViewCreateInfo( + vk::ImageViewCreateFlags(), + image, + vk::ImageViewType::e2D, + swapChain.getSurfaceFormat().format, + componentMapping, + subResourceRange + ); + + imageViews.push_back( device.createImageView( imageViewCreateInfo ) ); + } + + return Core(std::move(context) , window, swapChain, imageViews); } const Context &Core::getContext() const @@ -267,7 +455,19 @@ namespace vkcv return m_Context; } - Core::Core(Context &&context) noexcept : - m_Context(std::move(context)) + Core::Core(Context &&context, const Window &window , SwapChain swapChain, std::vector<vk::ImageView> imageViews) noexcept : + m_Context(std::move(context)), + m_window(window), + m_swapchain(swapChain), + m_swapchainImageViews(imageViews) {} + + Core::~Core() { + for( auto image: m_swapchainImageViews ){ + m_Context.getDevice().destroyImageView(image); + } + + m_Context.getDevice().destroySwapchainKHR(m_swapchain.getSwapchain()); + m_Context.getInstance().destroySurfaceKHR( m_swapchain.getSurface() ); + } } diff --git a/src/vkcv/SwapChain.cpp b/src/vkcv/SwapChain.cpp new file mode 100644 index 0000000000000000000000000000000000000000..85f67b8d0fe252a2ab65d4e0d5baf388a7cfa862 --- /dev/null +++ b/src/vkcv/SwapChain.cpp @@ -0,0 +1,192 @@ + +#include <vkcv/SwapChain.hpp> + +namespace vkcv { + + SwapChain::SwapChain(vk::SurfaceKHR surface, vk::SwapchainKHR swapchain, vk::SurfaceFormatKHR format ) + : m_surface(surface), m_swapchain(swapchain), m_format( format) + {} + + vk::SwapchainKHR SwapChain::getSwapchain() { + return m_swapchain; + } + + /** + * gets surface of the swapchain + * @return current surface + */ + vk::SurfaceKHR SwapChain::getSurface() { + return m_surface; + } + + /** + * gets the surface of the swapchain + * @return chosen format + */ + vk::SurfaceFormatKHR SwapChain::getSurfaceFormat(){ + return m_format; + } + + /** + * creates surface and checks availability + * @param window current window for the surface + * @param instance Vulkan-Instance + * @param physicalDevice Vulkan-PhysicalDevice + * @return created surface + */ + vk::SurfaceKHR createSurface(GLFWwindow *window, const vk::Instance &instance, const vk::PhysicalDevice& physicalDevice) { + //create surface + VkSurfaceKHR surface; + if (glfwCreateWindowSurface(VkInstance(instance), window, nullptr, &surface) != VK_SUCCESS) { + throw std::runtime_error("failed to create a window surface!"); + } + vk::Bool32 surfaceSupport = false; + if (physicalDevice.getSurfaceSupportKHR(0, vk::SurfaceKHR(surface), &surfaceSupport) != vk::Result::eSuccess && surfaceSupport != true) { + throw std::runtime_error("surface is not supported by the device!"); + } + + return vk::SurfaceKHR(surface); + } + + /** + * chooses Extent and clapms values to the available + * @param physicalDevice Vulkan-PhysicalDevice + * @param surface of the swapchain + * @param window of the current application + * @return chosen Extent for the surface + */ + vk::Extent2D chooseSwapExtent(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface, const Window &window){ + vk::SurfaceCapabilitiesKHR surfaceCapabilities; + if(physicalDevice.getSurfaceCapabilitiesKHR(surface,&surfaceCapabilities) != vk::Result::eSuccess){ + throw std::runtime_error("cannot get surface capabilities. There is an issue with the surface."); + } + + VkExtent2D extent2D = { + static_cast<uint32_t>(window.getWidth()), + static_cast<uint32_t>(window.getHeight()) + }; + extent2D.width = std::max(surfaceCapabilities.minImageExtent.width, std::min(surfaceCapabilities.maxImageExtent.width, extent2D.width)); + extent2D.height = std::max(surfaceCapabilities.minImageExtent.height, std::min(surfaceCapabilities.maxImageExtent.height, extent2D.height)); + + if (extent2D.width > surfaceCapabilities.maxImageExtent.width || + extent2D.width < surfaceCapabilities.minImageExtent.width || + extent2D.height > surfaceCapabilities.maxImageExtent.height || + extent2D.height < surfaceCapabilities.minImageExtent.height) { + std::printf("Surface size not matching. Resizing to allowed value."); + } + return extent2D; + } + + /** + * chooses Surface Format for the current surface + * @param physicalDevice Vulkan-PhysicalDevice + * @param surface of the swapchain + * @return available Format + */ + vk::SurfaceFormatKHR chooseSwapSurfaceFormat(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) { + uint32_t formatCount; + physicalDevice.getSurfaceFormatsKHR(surface, &formatCount, nullptr); + std::vector<vk::SurfaceFormatKHR> availableFormats(formatCount); + if (physicalDevice.getSurfaceFormatsKHR(surface, &formatCount, &availableFormats[0]) != vk::Result::eSuccess) { + throw std::runtime_error("Failed to get surface formats"); + } + + for (const auto& availableFormat : availableFormats) { + if (availableFormat.format == vk::Format::eB8G8R8A8Unorm && availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) { + return availableFormat; + } + } + return availableFormats[0]; + } + + /** + * returns vk::PresentModeKHR::eMailbox if available or vk::PresentModeKHR::eFifo otherwise + * @param physicalDevice Vulkan-PhysicalDevice + * @param surface of the swapchain + * @return available PresentationMode + */ + vk::PresentModeKHR choosePresentMode(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) { + uint32_t modeCount; + physicalDevice.getSurfacePresentModesKHR( surface, &modeCount, nullptr ); + std::vector<vk::PresentModeKHR> availablePresentModes(modeCount); + if (physicalDevice.getSurfacePresentModesKHR(surface, &modeCount, &availablePresentModes[0]) != vk::Result::eSuccess) { + throw std::runtime_error("Failed to get presentation modes"); + } + + for (const auto& availablePresentMode : availablePresentModes) { + if (availablePresentMode == vk::PresentModeKHR::eMailbox) { + return availablePresentMode; + } + } + // The FIFO present mode is guaranteed by the spec to be supported + return vk::PresentModeKHR::eFifo; + } + + /** + * returns the minImageCount +1 for at least doublebuffering, if it's greater than maxImageCount return maxImageCount + * @param physicalDevice Vulkan-PhysicalDevice + * @param surface of the swapchain + * @return available ImageCount + */ + uint32_t chooseImageCount(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) { + vk::SurfaceCapabilitiesKHR surfaceCapabilities; + if(physicalDevice.getSurfaceCapabilitiesKHR(surface, &surfaceCapabilities) != vk::Result::eSuccess){ + throw std::runtime_error("cannot get surface capabilities. There is an issue with the surface."); + } + + uint32_t imageCount = surfaceCapabilities.minImageCount + 1; // minImageCount should always be at least 2; set to 3 for triple buffering + // check if requested image count is supported + if (surfaceCapabilities.maxImageCount > 0 && imageCount > surfaceCapabilities.maxImageCount) { + imageCount = surfaceCapabilities.maxImageCount; + } + + return imageCount; + } + /** + * creates and returns a swapchain with default specs + * @param window of the current application + * @param context that keeps instance, physicalDevice and a device. + * @return swapchain + */ + SwapChain SwapChain::create(const Window &window, const Context &context) { + const vk::Instance& instance = context.getInstance(); + const vk::PhysicalDevice& physicalDevice = context.getPhysicalDevice(); + const vk::Device& device = context.getDevice(); + + vk::SurfaceKHR surface = createSurface(window.getWindow(),instance,physicalDevice); + + vk::Extent2D extent2D = chooseSwapExtent(physicalDevice, surface, window); + vk::SurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(physicalDevice, surface); + vk::PresentModeKHR presentMode = choosePresentMode(physicalDevice, surface); + uint32_t imageCount = chooseImageCount(physicalDevice, surface); + + vk::SwapchainCreateInfoKHR swapchainCreateInfo( + vk::SwapchainCreateFlagsKHR(), //flags + surface, // surface + imageCount, // minImageCount TODO: how many do we need for our application?? "must be less than or equal to the value returned in maxImageCount" -> 3 for Triple Buffering, else 2 for Double Buffering (should be the standard) + surfaceFormat.format, // imageFormat + surfaceFormat.colorSpace, // imageColorSpace + extent2D, // imageExtent + 1, // imageArrayLayers TODO: should we only allow non-stereoscopic applications? yes -> 1, no -> ? "must be greater than 0, less or equal to maxImageArrayLayers" + vk::ImageUsageFlagBits::eColorAttachment, // imageUsage TODO: what attachments? only color? depth? + vk::SharingMode::eExclusive, // imageSharingMode TODO: which sharing mode? "VK_SHARING_MODE_EXCLUSIV access exclusive to a single queue family, better performance", "VK_SHARING_MODE_CONCURRENT access from multiple queues" + 0, // queueFamilyIndexCount, the number of queue families having access to the image(s) of the swapchain when imageSharingMode is VK_SHARING_MODE_CONCURRENT + nullptr, // pQueueFamilyIndices, the pointer to an array of queue family indices having access to the images(s) of the swapchain when imageSharingMode is VK_SHARING_MODE_CONCURRENT + vk::SurfaceTransformFlagBitsKHR::eIdentity, // preTransform, transformations applied onto the image before display + vk::CompositeAlphaFlagBitsKHR::eOpaque, // compositeAlpha, TODO: how to handle transparent pixels? do we need transparency? If no -> opaque + presentMode, // presentMode + true, // clipped + nullptr // oldSwapchain + ); + + vk::SwapchainKHR swapchain = device.createSwapchainKHR(swapchainCreateInfo); + + return SwapChain(surface, swapchain, surfaceFormat); + } + + + SwapChain::~SwapChain() { + // needs to be destroyed by creator + } + +} diff --git a/src/vkcv/Window.cpp b/src/vkcv/Window.cpp index 8814a5abcf7977386490d9783d5b121b7f986651..ce05d67cb216215c625b468acdb85be2fc2a8b2e 100644 --- a/src/vkcv/Window.cpp +++ b/src/vkcv/Window.cpp @@ -6,6 +6,7 @@ #include "vkcv/Window.hpp" + namespace vkcv { static uint32_t s_WindowCount = 0; @@ -18,14 +19,15 @@ namespace vkcv { glfwDestroyWindow(m_window); s_WindowCount--; - if(s_WindowCount == 0) + if(s_WindowCount == 0) { glfwTerminate(); + } } - Window Window::create(const char *windowTitle, int width, int height, bool resizable) { - if(s_WindowCount == 0) + Window Window::create( const char *windowTitle, int width, int height, bool resizable) { + if(s_WindowCount == 0) { glfwInit(); - + } s_WindowCount++; width = std::max(width, 1); @@ -35,9 +37,8 @@ namespace vkcv { glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE); GLFWwindow *window; window = glfwCreateWindow(width, height, windowTitle, nullptr, nullptr); - return Window(window); - + return Window(window); } bool Window::isWindowOpen() const { @@ -48,10 +49,6 @@ namespace vkcv { glfwPollEvents(); } - GLFWwindow *Window::getWindow() const { - return m_window; - } - int Window::getWidth() const { int width; glfwGetWindowSize(m_window, &width, nullptr); @@ -63,4 +60,8 @@ namespace vkcv { glfwGetWindowSize(m_window, nullptr, &height); return height; } -} \ No newline at end of file + + GLFWwindow *Window::getWindow() const { + return m_window; + } +}