diff --git a/include/application.hpp b/include/application.hpp index 0556b5b..7dfc341 100644 --- a/include/application.hpp +++ b/include/application.hpp @@ -18,11 +18,13 @@ public: /// /// @param device Vulkan device /// @param physicalDevice Vulkan physical device + /// @param graphicsQueue Vulkan queue for graphics operations + /// @param presentQueue Vulkan queue for presentation operations /// - /// @throws std::invalid_argument if the device or physicalDevice is null. /// @throws LSFG::vulkan_error if any Vulkan call fails. /// - Application(VkDevice device, VkPhysicalDevice physicalDevice); + Application(VkDevice device, VkPhysicalDevice physicalDevice, + VkQueue graphicsQueue, VkQueue presentQueue); /// /// Add a swapchain to the application. @@ -32,7 +34,6 @@ public: /// @param extent The extent of the images. /// @param images The swapchain images. /// - /// @throws std::invalid_argument if the handle is already added. /// @throws LSFG::vulkan_error if any Vulkan call fails. /// void addSwapchain(VkSwapchainKHR handle, VkFormat format, VkExtent2D extent, @@ -46,28 +47,27 @@ public: /// @param semaphores The semaphores to wait on before presenting. /// @param idx The index of the swapchain image to present. /// - /// @throws std::invalid_argument if the handle is not found. /// @throws LSFG::vulkan_error if any Vulkan call fails. /// void presentSwapchain(VkSwapchainKHR handle, VkQueue queue, const std::vector& semaphores, uint32_t idx); - /// /// Remove a swapchain from the application. /// /// @param handle The Vulkan handle of the swapchain state to remove. /// @return true if the swapchain was removed, false if it was already retired. /// - /// @throws std::invalid_argument if the handle is null. - /// bool removeSwapchain(VkSwapchainKHR handle); - /// Get the Vulkan device. [[nodiscard]] VkDevice getDevice() const { return this->device; } /// Get the Vulkan physical device. [[nodiscard]] VkPhysicalDevice getPhysicalDevice() const { return this->physicalDevice; } + /// Get the Vulkan graphics queue. + [[nodiscard]] VkQueue getGraphicsQueue() const { return this->graphicsQueue; } + /// Get the Vulkan present queue. + [[nodiscard]] VkQueue getPresentQueue() const { return this->presentQueue; } // Non-copyable and non-movable Application(const Application&) = delete; @@ -81,6 +81,8 @@ private: // (non-owned resources) VkDevice device; VkPhysicalDevice physicalDevice; + VkQueue graphicsQueue; + VkQueue presentQueue; // (owned resources) std::unordered_map swapchains; @@ -95,16 +97,29 @@ public: /// /// Create the swapchain context. /// + /// @param app The application context to use. /// @param swapchain The Vulkan swapchain handle. /// @param format The format of the swapchain images. /// @param extent The extent of the swapchain images. /// @param images The swapchain images. /// - /// @throws std::invalid_argument if any parameter is null /// @throws LSFG::vulkan_error if any Vulkan call fails. /// - SwapchainContext(VkSwapchainKHR swapchain, VkFormat format, VkExtent2D extent, - const std::vector& images); + SwapchainContext(const Application& app, VkSwapchainKHR swapchain, + VkFormat format, VkExtent2D extent, const std::vector& images); + + /// + /// Present the next frame + /// + /// @param app The application context to use + /// @param queue The Vulkan queue to present the frame on. + /// @param semaphores The semaphores to wait on before presenting. + /// @param idx The index of the swapchain image to present. + /// + /// @throws LSFG::vulkan_error if any Vulkan call fails. + /// + void present(const Application& app, VkQueue queue, + const std::vector& semaphores, uint32_t idx); /// Get the Vulkan swapchain handle. [[nodiscard]] VkSwapchainKHR handle() const { return this->swapchain; } diff --git a/src/application.cpp b/src/application.cpp index 3a69504..f6216eb 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -4,62 +4,49 @@ #include -Application::Application(VkDevice device, VkPhysicalDevice physicalDevice) - : device(device), physicalDevice(physicalDevice) { - if (device == VK_NULL_HANDLE || physicalDevice == VK_NULL_HANDLE) - throw std::invalid_argument("Invalid Vulkan device or physical device"); -} +Application::Application(VkDevice device, VkPhysicalDevice physicalDevice, + VkQueue graphicsQueue, VkQueue presentQueue) + : device(device), physicalDevice(physicalDevice), + graphicsQueue(graphicsQueue), presentQueue(presentQueue) {} void Application::addSwapchain(VkSwapchainKHR handle, VkFormat format, VkExtent2D extent, const std::vector& images) { - // add the swapchain handle auto it = this->swapchains.find(handle); if (it != this->swapchains.end()) - throw std::invalid_argument("Swapchain handle already exists"); + return; // already added - // initialize the swapchain context - this->swapchains.emplace(handle, SwapchainContext(handle, format, extent, images)); + this->swapchains.emplace(handle, SwapchainContext(*this, handle, format, extent, images)); } +SwapchainContext::SwapchainContext(const Application& app, VkSwapchainKHR swapchain, + VkFormat format, VkExtent2D extent, const std::vector& images) + : swapchain(swapchain), format(format), extent(extent), images(images) {} + void Application::presentSwapchain(VkSwapchainKHR handle, VkQueue queue, const std::vector& semaphores, uint32_t idx) { - if (handle == VK_NULL_HANDLE) - throw std::invalid_argument("Invalid swapchain handle"); - - // find the swapchain context auto it = this->swapchains.find(handle); if (it == this->swapchains.end()) throw std::logic_error("Swapchain not found"); - // TODO: present + it->second.present(*this, queue, semaphores, idx); +} + +void SwapchainContext::present(const Application& app, VkQueue queue, + const std::vector& semaphores, uint32_t idx) { const VkPresentInfoKHR presentInfo = { .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, - .pNext = nullptr, .waitSemaphoreCount = static_cast(semaphores.size()), .pWaitSemaphores = semaphores.data(), .swapchainCount = 1, - .pSwapchains = &handle, - .pImageIndices = &idx, - .pResults = nullptr // can be null if not needed + .pSwapchains = &this->swapchain, + .pImageIndices = &idx }; auto res = vkQueuePresentKHR(queue, &presentInfo); - if (res != VK_SUCCESS) // do NOT check VK_SUBOPTIMAL_KHR + if (res != VK_SUCCESS) // FIXME: somehow return VK_SUBOPTIMAL_KHR throw LSFG::vulkan_error(res, "Failed to present swapchain"); } - -SwapchainContext::SwapchainContext(VkSwapchainKHR swapchain, VkFormat format, VkExtent2D extent, - const std::vector& images) - : swapchain(swapchain), format(format), extent(extent), images(images) { - if (swapchain == VK_NULL_HANDLE || format == VK_FORMAT_UNDEFINED) - throw std::invalid_argument("Invalid swapchain or images"); -} - bool Application::removeSwapchain(VkSwapchainKHR handle) { - if (handle == VK_NULL_HANDLE) - throw std::invalid_argument("Invalid swapchain handle"); - - // remove the swapchain context auto it = this->swapchains.find(handle); if (it == this->swapchains.end()) return false; // already retired... hopefully diff --git a/src/hooks.cpp b/src/hooks.cpp index 40773a1..3147cd9 100644 --- a/src/hooks.cpp +++ b/src/hooks.cpp @@ -21,6 +21,39 @@ namespace { VkDevice* pDevice) { auto res = vkCreateDevice(physicalDevice, pCreateInfo, pAllocator, pDevice); + // extract graphics and present queues + std::vector queueCreateInfos(pCreateInfo->queueCreateInfoCount); + std::copy_n(pCreateInfo->pQueueCreateInfos, queueCreateInfos.size(), queueCreateInfos.data()); + + uint32_t familyCount{}; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount, nullptr); + std::vector families(familyCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount, families.data()); + + std::optional graphicsFamilyIdx; + std::optional presentFamilyIdx; + for (uint32_t i = 0; i < families.size(); ++i) { + auto it = std::ranges::find_if(queueCreateInfos, + [i](const VkDeviceQueueCreateInfo& info) { + return info.queueFamilyIndex == i; + }) ; + if (it == queueCreateInfos.end()) + continue; // skip if this family is not used by the device + if (families.at(i).queueFlags & VK_QUEUE_GRAPHICS_BIT) + graphicsFamilyIdx.emplace(i); + if (families.at(i).queueFlags & VK_QUEUE_COMPUTE_BIT) + presentFamilyIdx.emplace(i); + } + if (!graphicsFamilyIdx.has_value() || !presentFamilyIdx.has_value()) { + Log::error("No suitable queue family found for graphics or present"); + exit(EXIT_FAILURE); + } + + VkQueue graphicsQueue{}; + vkGetDeviceQueue(*pDevice, *graphicsFamilyIdx, 0, &graphicsQueue); + VkQueue presentQueue{}; + vkGetDeviceQueue(*pDevice, *presentFamilyIdx, 0, &presentQueue); + // create the main application if (application.has_value()) { Log::error("Application already initialized, are you trying to create a second device?"); @@ -28,7 +61,7 @@ namespace { } try { - application.emplace(*pDevice, physicalDevice); + application.emplace(*pDevice, physicalDevice, graphicsQueue, presentQueue); Log::info("lsfg-vk(hooks): Application created successfully"); } catch (const LSFG::vulkan_error& e) { Log::error("Encountered Vulkan error {:x} while creating application: {}",