#pragma once #define GLFW_INCLUDE_VULKAN #include #include #include #include #include #include #include #include #define GLM_FORCE_RADIANS #define GLM_FORCE_DEPTH_ZERO_TO_ONE #include #include #include #include #include "../../main/util.h" #include "../../main/logging.h" #include "../../main/rendering/graphics_interface.h" namespace progressia::desktop { namespace CstrUtils { struct CstrHash { std::size_t operator()(const char *s) const noexcept { std::size_t acc = 0; while (*s != 0) { acc = acc * 31 + *s; s++; } return acc; } }; struct CstrEqual { bool operator()(const char *lhs, const char *rhs) const noexcept { return strcmp(lhs, rhs) == 0; } }; struct CstrCompare { bool operator()(const char *lhs, const char *rhs) const noexcept { return strcmp(lhs, rhs) < 0; } }; using CstrHashSet = std::unordered_set; } // namespace CstrUtils class VkObjectWrapper : private progressia::main::NonCopyable { // empty }; constexpr std::size_t MAX_FRAMES_IN_FLIGHT = 2; class VulkanErrorHandler; class PhysicalDevice; class Surface; class Queue; class Queues; class CommandPool; class RenderPass; class Pipeline; class SwapChain; class TextureDescriptors; class Adapter; class Frame; class Vulkan : public VkObjectWrapper { private: VkInstance instance = VK_NULL_HANDLE; VkDevice device = VK_NULL_HANDLE; std::unique_ptr errorHandler; std::unique_ptr physicalDevice; std::unique_ptr surface; std::unique_ptr queues; std::unique_ptr commandPool; std::unique_ptr renderPass; std::unique_ptr pipeline; std::unique_ptr swapChain; std::unique_ptr textureDescriptors; std::unique_ptr adapter; std::unique_ptr gint; std::vector> frames; std::size_t currentFrame; bool isRenderingFrame; uint64_t lastStartedFrame; public: Vulkan(std::vector instanceExtensions, std::vector deviceExtensions, std::vector validationLayers); ~Vulkan(); VkInstance getInstance() const; VkDevice getDevice() const; const PhysicalDevice &getPhysicalDevice() const; Surface &getSurface(); const Surface &getSurface() const; Queues &getQueues(); const Queues &getQueues() const; SwapChain &getSwapChain(); const SwapChain &getSwapChain() const; CommandPool &getCommandPool(); const CommandPool &getCommandPool() const; RenderPass &getRenderPass(); const RenderPass &getRenderPass() const; Pipeline &getPipeline(); const Pipeline &getPipeline() const; TextureDescriptors &getTextureDescriptors(); const TextureDescriptors &getTextureDescriptors() const; Adapter &getAdapter(); const Adapter &getAdapter() const; Frame *getCurrentFrame(); const Frame *getCurrentFrame() const; progressia::main::GraphicsInterface &getGint(); const progressia::main::GraphicsInterface &getGint() const; /* * Returns false when the frame should be skipped */ bool startRender(); void endRender(); uint64_t getLastStartedFrame() const; std::size_t getFrameInFlightIndex() const; void waitIdle(); VkFormat findSupportedFormat(const std::vector &, VkImageTiling, VkFormatFeatureFlags); uint32_t findMemoryType(uint32_t allowedByDevice, VkMemoryPropertyFlags desiredProperties); template VkResult call(const char *functionName, Args &&...args) { using FunctionSignature = VkResult(VkInstance, Args...); auto func = reinterpret_cast( vkGetInstanceProcAddr(instance, functionName)); if (func != nullptr) { return func(instance, std::forward(args)...); } else { progressia::main::logging::error() << "[Vulkan] [dynVkCall / VkResult]\tFunction not found for " "name \"" << functionName << "\""; // REPORT_ERROR return VK_ERROR_EXTENSION_NOT_PRESENT; } } template VkResult callVoid(const char *functionName, Args &&...args) { using FunctionSignature = void(VkInstance, Args...); auto func = reinterpret_cast( vkGetInstanceProcAddr(instance, functionName)); if (func != nullptr) { func(instance, std::forward(args)...); return VK_SUCCESS; } else { progressia::main::logging::error() << "[Vulkan] [dynVkCall / void]\tFunction not found for name \"" << functionName << "\""; // REPORT_ERROR return VK_ERROR_EXTENSION_NOT_PRESENT; } } void handleVkResult(const char *errorMessage, VkResult); }; class VulkanErrorHandler : public VkObjectWrapper { private: VkDebugUtilsMessengerEXT debugMessenger; Vulkan &vulkan; public: VulkanErrorHandler(Vulkan &vulkan); std::unique_ptr attachDebugProbe(VkInstanceCreateInfo &); void onInstanceReady(); // NOLINTNEXTLINE(performance-trivially-destructible): fixing this makes code less readable due to use of macros in implementation ~VulkanErrorHandler(); void handleVkResult(const char *errorMessage, VkResult result); }; class Surface : public VkObjectWrapper { private: VkSurfaceKHR vk; Vulkan &vulkan; public: Surface(Vulkan &vulkan); ~Surface(); VkSurfaceKHR getVk(); }; class Queue { private: using Test = std::function; Test test; std::optional familyIndex; VkQueue vk; friend class Queues; Queue(Test test); public: bool isSuitable(VkPhysicalDevice, uint32_t familyIndex, Vulkan &, const VkQueueFamilyProperties &) const; VkQueue getVk() const; uint32_t getFamilyIndex() const; void waitIdle() const; }; class Queues { private: Queue graphicsQueue; Queue presentQueue; public: Queues(VkPhysicalDevice physicalDevice, Vulkan &vulkan); ~Queues(); // cppcheck-suppress functionConst; this method modifies the Queue fields void storeHandles(VkDevice device); bool isComplete() const; struct CreationRequest { float priority; std::vector queueCreateInfos; }; std::unique_ptr requestCreation(VkDeviceCreateInfo &) const; const Queue &getGraphicsQueue() const; const Queue &getPresentQueue() const; }; class CommandPool : public VkObjectWrapper { private: VkCommandPool pool; const Queue &queue; Vulkan &vulkan; VkCommandBuffer allocateCommandBuffer(); void beginCommandBuffer(VkCommandBuffer commandBuffer, VkCommandBufferUsageFlags usage); public: CommandPool(Vulkan &vulkan, const Queue &queue); ~CommandPool(); VkCommandBuffer beginSingleUse(); void runSingleUse(VkCommandBuffer, bool waitIdle = false); VkCommandBuffer allocateMultiUse(); VkCommandBuffer beginMultiUse(); void submitMultiUse(VkCommandBuffer, bool waitIdle = false); void freeMultiUse(VkCommandBuffer); }; } // namespace progressia::desktop