← index
libmx2/libmx/cross/vk.hpp
Source: libmx2/libmx/cross/vk.hpp
#ifndef _VK_MX_H__
#define _VK_MX_H__
#include "config.h"
#ifndef WITH_MOLTEN
#include "volk.h"
#include <SDL_vulkan.h>
#else
#include <SDL_vulkan.h>
#include <vulkan/vulkan.h>
#endif
#include "exception.hpp"
#include "util.hpp"

#include <SDL_ttf.h>
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdlib>
#include <format>
#include <fstream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <stdexcept>
#include <utility>
#include <vector>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

#ifndef VK_CHECK_RESULT
#define VK_CHECK_RESULT(f)                                                                                                              \
    {                                                                                                                                   \
        VkResult res = (f);                                                                                                             \
        if (res != VK_SUCCESS) {                                                                                                        \
            throw mx::Exception(std::format("Fatal : VkResult is \"{}\" in {} at line {}", static_cast<int>(res), __FILE__, __LINE__)); \
        }                                                                                                                               \
    }
#endif

namespace mx {

    struct Vertex {
        float pos[3];
        float texCoord[2];
        float normal[3];
    };

    struct QueueFamilyIndices {
        std::optional<uint32_t> graphicsFamily;
        std::optional<uint32_t> presentFamily;
        bool isComplete() {
            return graphicsFamily.has_value() && presentFamily.has_value();
        }
    };

    struct SwapChainSupportDetails {
        VkSurfaceCapabilitiesKHR capabilities;
        std::vector<VkSurfaceFormatKHR> formats;
        std::vector<VkPresentModeKHR> presentModes;
    };

    struct UniformBufferObject {
        alignas(16) glm::mat4 model;
        alignas(16) glm::mat4 view;
        alignas(16) glm::mat4 proj;
        alignas(16) glm::vec4 params;
        alignas(16) glm::vec4 color;
    };

    class VKText {
      public:
        VKText(VkDevice device, VkPhysicalDevice physicalDevice, VkQueue graphicsQueue,
               VkCommandPool commandPool, const std::string &fontPath, int fontSize = 24);
        ~VKText();
        void printTextG_Solid(const std::string &text, int x, int y, const SDL_Color &col);
        void renderText(VkCommandBuffer cmdBuffer, VkPipelineLayout pipelineLayout,
                        uint32_t screenWidth, uint32_t screenHeight);
        void clearQueue();
        void setDescriptorSetLayout(VkDescriptorSetLayout layout) { descriptorSetLayout = layout; }
        VkSampler fontSampler = VK_NULL_HANDLE;

      private:
        struct TextVertex {
            float pos[2];
            float texCoord[2];
        };

        struct TextQuad {
            std::string text;
            int x, y;
            int width, height;
            SDL_Color color;
            std::vector<TextVertex> vertices;
            std::vector<uint16_t> indices;
            VkBuffer vertexBuffer = VK_NULL_HANDLE;
            VkDeviceMemory vertexBufferMemory = VK_NULL_HANDLE;
            VkBuffer indexBuffer = VK_NULL_HANDLE;
            VkDeviceMemory indexBufferMemory = VK_NULL_HANDLE;
            VkImage textImage = VK_NULL_HANDLE;
            VkDeviceMemory textImageMemory = VK_NULL_HANDLE;
            VkImageView textImageView = VK_NULL_HANDLE;
            VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
            uint32_t indexCount = 0;
            VkDevice device = VK_NULL_HANDLE;

            TextQuad() = default;
            TextQuad(const TextQuad &) = delete;
            TextQuad &operator=(const TextQuad &) = delete;
            TextQuad(TextQuad &&other) noexcept {
                *this = std::move(other);
            }
            TextQuad &operator=(TextQuad &&other) noexcept {
                if (this != &other) {
                    text = std::move(other.text);
                    x = other.x;
                    y = other.y;
                    width = other.width;
                    height = other.height;
                    color = other.color;
                    vertices = std::move(other.vertices);
                    indices = std::move(other.indices);
                    vertexBuffer = other.vertexBuffer;
                    vertexBufferMemory = other.vertexBufferMemory;
                    indexBuffer = other.indexBuffer;
                    indexBufferMemory = other.indexBufferMemory;
                    textImage = other.textImage;
                    textImageMemory = other.textImageMemory;
                    textImageView = other.textImageView;
                    descriptorSet = other.descriptorSet;
                    indexCount = other.indexCount;
                    device = other.device;

                    other.vertexBuffer = VK_NULL_HANDLE;
                    other.vertexBufferMemory = VK_NULL_HANDLE;
                    other.indexBuffer = VK_NULL_HANDLE;
                    other.indexBufferMemory = VK_NULL_HANDLE;
                    other.textImage = VK_NULL_HANDLE;
                    other.textImageMemory = VK_NULL_HANDLE;
                    other.textImageView = VK_NULL_HANDLE;
                    other.descriptorSet = VK_NULL_HANDLE;
                    other.indexCount = 0;
                    other.device = VK_NULL_HANDLE;
                }
                return *this;
            }

            ~TextQuad() {
                if (device != VK_NULL_HANDLE) {
                    if (textImageView != VK_NULL_HANDLE) {
                        vkDestroyImageView(device, textImageView, nullptr);
                    }
                    if (textImage != VK_NULL_HANDLE) {
                        vkDestroyImage(device, textImage, nullptr);
                        vkFreeMemory(device, textImageMemory, nullptr);
                    }
                    if (vertexBuffer != VK_NULL_HANDLE) {
                        vkDestroyBuffer(device, vertexBuffer, nullptr);
                        vkFreeMemory(device, vertexBufferMemory, nullptr);
                    }
                    if (indexBuffer != VK_NULL_HANDLE) {
                        vkDestroyBuffer(device, indexBuffer, nullptr);
                        vkFreeMemory(device, indexBufferMemory, nullptr);
                    }
                }
            }
        };

        VkDevice device = VK_NULL_HANDLE;
        VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
        VkQueue graphicsQueue = VK_NULL_HANDLE;
        VkCommandPool commandPool = VK_NULL_HANDLE;

        TTF_Font *font = nullptr;
        std::vector<TextQuad> textQuads;
        VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
        VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;

        void initFont(const std::string &fontPath, int fontSize);
        void createDescriptorPool();
        VkDescriptorSet createDescriptorSet(VkImageView imageView);
        void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
                          VkMemoryPropertyFlags properties, VkBuffer &buffer,
                          VkDeviceMemory &bufferMemory);
        uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties);
        void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height);
        VkCommandBuffer beginSingleTimeCommands();
        void endSingleTimeCommands(VkCommandBuffer commandBuffer);
        void transitionImageLayout(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout);
        void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling,
                         VkImageUsageFlags usage, VkMemoryPropertyFlags properties,
                         VkImage &image, VkDeviceMemory &imageMemory);
        VkImageView createImageView(VkImage image, VkFormat format);
        SDL_Surface *convertToRGBA(SDL_Surface *surface);
    };

    class VKWindow {
      public:
        VKWindow() = default;
        VKWindow(const std::string &title, int width, int height, bool full = false);
        virtual ~VKWindow() {}
        void initWindow(const std::string &title, int width, int height, bool full = false);
        virtual void initVulkan();
        void createVertexBuffer();
        void setPath(const std::string &path);
        void loop();
        virtual void proc();
        virtual void cleanup();
        virtual void event(SDL_Event &e) = 0;
        virtual void draw();
        void createGraphicsPipeline();
        VkShaderModule createShaderModule(const std::vector<char> &code);
        int w = 0, h = 0;
        mxUtil util;
        void quit();
        void setFullScreen(const bool full);
        void updateTexture(SDL_Surface *newSurface);
        void updateTexture(void *pixels, VkDeviceSize imageSize);
        void printText(const std::string &text, int x, int y, const SDL_Color &col);
        void clearTextQueue();

      protected:
        bool active = true;
        VkInstance instance = VK_NULL_HANDLE;
        VkSurfaceKHR surface = VK_NULL_HANDLE;
        VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
        VkDevice device = VK_NULL_HANDLE;
        VkQueue graphicsQueue = VK_NULL_HANDLE;
        VkQueue presentQueue = VK_NULL_HANDLE;
        VkDescriptorSetLayout descriptorSetLayout;

        VkSwapchainKHR swapChain = VK_NULL_HANDLE;
        std::vector<VkImage> swapChainImages;
        VkFormat swapChainImageFormat;
        VkExtent2D swapChainExtent;
        std::vector<VkImageView> swapChainImageViews;
        std::vector<VkBuffer> uniformBuffers;
        std::vector<VkDeviceMemory> uniformBuffersMemory;
        std::vector<void *> uniformBuffersMapped;

        VkRenderPass renderPass = VK_NULL_HANDLE;
        VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
        VkPipeline graphicsPipeline = VK_NULL_HANDLE;
        VkPipeline graphicsPipelineWireframe = VK_NULL_HANDLE;
        VkPolygonMode currentPolygonMode = VK_POLYGON_MODE_FILL;
        std::vector<VkFramebuffer> swapChainFramebuffers;

        VkCommandPool commandPool = VK_NULL_HANDLE;
        std::vector<VkCommandBuffer> commandBuffers;

        VkSemaphore imageAvailableSemaphore = VK_NULL_HANDLE;
        VkSemaphore renderFinishedSemaphore = VK_NULL_HANDLE;
        VkFence inFlightFence = VK_NULL_HANDLE;

        VkBuffer vertexBuffer = VK_NULL_HANDLE;
        VkDeviceMemory vertexBufferMemory = VK_NULL_HANDLE;
        VkBuffer indexBuffer = VK_NULL_HANDLE;
        VkDeviceMemory indexBufferMemory = VK_NULL_HANDLE;
        uint32_t indexCount = 0;

        VkImage textureImage = VK_NULL_HANDLE;
        VkDeviceMemory textureImageMemory = VK_NULL_HANDLE;
        VkImageView textureImageView = VK_NULL_HANDLE;
        VkSampler textureSampler = VK_NULL_HANDLE;
        VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
        std::vector<VkDescriptorSet> descriptorSets;

        VkImage depthImage = VK_NULL_HANDLE;
        VkDeviceMemory depthImageMemory = VK_NULL_HANDLE;
        VkImageView depthImageView = VK_NULL_HANDLE;
        VkFormat depthFormat = VK_FORMAT_D32_SFLOAT;

        float cameraDistance = 3.0f;

        std::unique_ptr<VKText> textRenderer;
        VkPipeline textPipeline = VK_NULL_HANDLE;
        VkPipelineLayout textPipelineLayout = VK_NULL_HANDLE;
        VkDescriptorPool textDescriptorPool = VK_NULL_HANDLE;
        VkDescriptorSetLayout textDescriptorSetLayout = VK_NULL_HANDLE;

        uint32_t width, height;
        void createDescriptorSetLayout();
        void createDepthResources();
        VkFormat findSupportedFormat(const std::vector<VkFormat> &candidates, VkImageTiling tiling, VkFormatFeatureFlags features);
        VkFormat findDepthFormat();
        SDL_Window *window = nullptr;
        SDL_Surface *surface_img = nullptr;
        void createInstance();
        void createSurface();
        void pickPhysicalDevice();
        void createLogicalDevice();
        void createUniformBuffers();
        virtual void createSwapChain();
        void createImageViews();
        void createRenderPass();
        void createFramebuffers();
        void createCommandPool();
        void createCommandBuffers();
        void createSyncObjects();
        virtual void cleanupSwapChain();
        QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device);
        VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR> &availableFormats);
        VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR> &availablePresentModes);
        VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities);
        bool isDeviceSuitable(VkPhysicalDevice device);
        SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device);
        void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer &buffer, VkDeviceMemory &bufferMemory);
        uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties);
        void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size);
        void createTextureImage(SDL_Surface *surfacex);
        void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage &image, VkDeviceMemory &imageMemory);
        void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout);
        void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height);
        VkCommandBuffer beginSingleTimeCommands();
        void endSingleTimeCommands(VkCommandBuffer commandBuffer);
        void createTextureImageView();
        VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags);
        void createTextureSampler();
        void createDescriptorPool();
        void createDescriptorSets();
        void updateDescriptorSets();
        void recreateSwapChain();
        void setupTextureImage(uint32_t w, uint32_t h);
        void createTextDescriptorSetLayout();
        void createTextPipeline();
        void createTextDescriptorPool();
    };

} // namespace mx

#endif