Browse Source

Use wrapper

Kajetan Johannes Hammerle 2 months ago
parent
commit
ecb73dee86
4 changed files with 602 additions and 568 deletions
  1. 87 0
      include/core/VulkanWrapper.hpp
  2. 13 109
      src/VulkanUtils.cpp
  3. 13 28
      src/VulkanUtils.hpp
  4. 489 431
      src/VulkanWrapper.cpp

+ 87 - 0
include/core/VulkanWrapper.hpp

@@ -1,10 +1,97 @@
 #ifndef GAMINGCORE_VULKAN_WRAPPER_HPP
 #ifndef GAMINGCORE_VULKAN_WRAPPER_HPP
 #define GAMINGCORE_VULKAN_WRAPPER_HPP
 #define GAMINGCORE_VULKAN_WRAPPER_HPP
 
 
+#include <core/Meta.hpp>
+
+#include <vulkan/vulkan.h>
+
 namespace Core::Vulkan {
 namespace Core::Vulkan {
     bool init();
     bool init();
     void render();
     void render();
     void destroy();
     void destroy();
+
+    template<typename T>
+    struct BaseWrapper {
+        VkDevice device = VK_NULL_HANDLE;
+        T handle = VK_NULL_HANDLE;
+
+        BaseWrapper() = default;
+        BaseWrapper(const BaseWrapper&) = delete;
+
+        BaseWrapper(BaseWrapper&& o) : BaseWrapper() {
+            swap(o);
+        }
+
+        BaseWrapper& operator=(const BaseWrapper&) = delete;
+
+        BaseWrapper& operator=(BaseWrapper&& o) {
+            swap(o);
+            o = BaseWrapper();
+        }
+
+        void swap(BaseWrapper& o) {
+            Core::swap(device, o.device);
+            Core::swap(handle, o.handle);
+        }
+
+        operator T() {
+            return handle;
+        }
+
+        operator T*() {
+            return &handle;
+        }
+
+        operator const T*() const {
+            return &handle;
+        }
+    };
+
+    struct ImageView : public BaseWrapper<VkImageView> {
+        ImageView() = default;
+        ImageView(ImageView&&) = default;
+        ~ImageView();
+    };
+
+    struct Framebuffer : public BaseWrapper<VkFramebuffer> {
+        Framebuffer() = default;
+        Framebuffer(Framebuffer&&) = default;
+        ~Framebuffer();
+    };
+
+    struct Semaphore : public BaseWrapper<VkSemaphore> {
+        Semaphore() = default;
+        Semaphore(Semaphore&&) = default;
+        ~Semaphore();
+        bool init(VkDevice d);
+    };
+
+    struct Fence : public BaseWrapper<VkFence> {
+        Fence() = default;
+        Fence(Fence&&) = default;
+        ~Fence();
+        bool init(VkDevice d);
+    };
+
+    struct Swapchain : public BaseWrapper<VkSwapchainKHR> {
+        Swapchain() = default;
+        Swapchain(Swapchain&&) = default;
+        ~Swapchain();
+    };
+
+    struct ShaderModule : public BaseWrapper<VkShaderModule> {
+        ShaderModule() = default;
+        ShaderModule(ShaderModule&&) = default;
+        ~ShaderModule();
+        bool init(VkDevice d, const char* path);
+    };
+
+    struct PipelineLayout : public BaseWrapper<VkPipelineLayout> {
+        PipelineLayout() = default;
+        PipelineLayout(PipelineLayout&&) = default;
+        ~PipelineLayout();
+        bool init(VkDevice d);
+    };
 }
 }
 
 
 #endif
 #endif

+ 13 - 109
src/VulkanUtils.cpp

@@ -1,7 +1,6 @@
 #include "VulkanUtils.hpp"
 #include "VulkanUtils.hpp"
 
 
 #include <core/Array.hpp>
 #include <core/Array.hpp>
-#include <core/File.hpp>
 #include <core/Logger.hpp>
 #include <core/Logger.hpp>
 
 
 #include "core/VulkanUtility.hpp"
 #include "core/VulkanUtility.hpp"
@@ -16,7 +15,7 @@ static u32 getSwapImageCount(const VkSurfaceCapabilitiesKHR* caps) {
     return c;
     return c;
 }
 }
 
 
-bool initVulkanSwapchain(VkSwapchainKHR* sc, VulkanSwapchainData* d) {
+bool initVulkanSwapchain(Swapchain& sc, VulkanSwapchainData* d) {
     VkSurfaceCapabilitiesKHR caps = {0};
     VkSurfaceCapabilitiesKHR caps = {0};
     if(d->base.getSurfaceCapabilities(caps)) {
     if(d->base.getSurfaceCapabilities(caps)) {
         return true;
         return true;
@@ -39,17 +38,12 @@ bool initVulkanSwapchain(VkSwapchainKHR* sc, VulkanSwapchainData* d) {
         .clipped = VK_TRUE,
         .clipped = VK_TRUE,
         .oldSwapchain = VK_NULL_HANDLE};
         .oldSwapchain = VK_NULL_HANDLE};
     VK_CHECK_TRUE(vkCreateSwapchainKHR(d->base, &ci, nullptr, sc));
     VK_CHECK_TRUE(vkCreateSwapchainKHR(d->base, &ci, nullptr, sc));
+    sc.device = d->base;
     return false;
     return false;
 }
 }
 
 
-void destroyVulkanSwapchain(VkSwapchainKHR s, VkDevice d) {
-    if(d != VK_NULL_HANDLE) {
-        vkDestroySwapchainKHR(d, s, nullptr);
-    }
-}
-
 static bool createImageView(
 static bool createImageView(
-    VkImageView& view, VkDevice d, VkImage image, VkFormat format) {
+    ImageView& view, VkDevice d, VkImage image, VkFormat format) {
     VkImageViewCreateInfo info = {
     VkImageViewCreateInfo info = {
         .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
         .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
         .image = image,
         .image = image,
@@ -61,7 +55,8 @@ static bool createImageView(
             .levelCount = 1,
             .levelCount = 1,
             .baseArrayLayer = 0,
             .baseArrayLayer = 0,
             .layerCount = 1}};
             .layerCount = 1}};
-    VK_CHECK_TRUE(vkCreateImageView(d, &info, nullptr, &view));
+    VK_CHECK_TRUE(vkCreateImageView(d, &info, nullptr, view));
+    view.device = d;
     return false;
     return false;
 }
 }
 
 
@@ -80,90 +75,26 @@ bool initVulkanSwapchainImages(
     return false;
     return false;
 }
 }
 
 
-void destroyVulkanSwapchainImages(VulkanSwapchainImages& si, VkDevice d) {
-    if(d == VK_NULL_HANDLE) {
-        return;
-    }
-    for(VkImageView& v : si.imageViews) {
-        vkDestroyImageView(d, v, nullptr);
-    }
-    si.imageViews.clear();
-    si.images.clear();
-}
-
-static bool initShaderModule(
-    Core::List<char>& f, VkDevice d, VkShaderModule* sm) {
-    size_t l = f.getLength() - 1;
-    if((l % 4) != 0) {
-        VK_REPORT_ERROR("Shader size is not a multiple of 4");
-        return true;
-    }
-    VkShaderModuleCreateInfo info = {
-        .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
-        .codeSize = l,
-        .pCode = reinterpret_cast<u32*>(static_cast<void*>(&f[0]))};
-    VK_CHECK_TRUE(vkCreateShaderModule(d, &info, nullptr, sm));
-    return false;
-}
-
-bool initVulkanShaderModule(VkShaderModule* sm, VkDevice d, const char* path) {
-    Core::List<char> f;
-    if(Core::readFile(f, path)) {
-        return true;
-    }
-    return initShaderModule(f, d, sm);
-}
-
-void destroyVulkanShaderModule(VkShaderModule sm, VkDevice d) {
-    if(d != VK_NULL_HANDLE) {
-        vkDestroyShaderModule(d, sm, nullptr);
-    }
-}
-
-bool initVulkanPipelineLayout(VkPipelineLayout* pl, VkDevice d) {
-    VkPipelineLayoutCreateInfo info = {
-        .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
-        .setLayoutCount = 0,
-        .pSetLayouts = nullptr,
-        .pushConstantRangeCount = 0,
-        .pPushConstantRanges = nullptr};
-    VK_CHECK_TRUE(vkCreatePipelineLayout(d, &info, nullptr, pl));
-    return false;
-}
-
-void destroyVulkanPipelineLayout(VkPipelineLayout pl, VkDevice d) {
-    if(d != VK_NULL_HANDLE) {
-        vkDestroyPipelineLayout(d, pl, nullptr);
-    }
-}
-
 bool initVulkanFramebuffers(
 bool initVulkanFramebuffers(
-    Core::List<VkFramebuffer>& f, VulkanSwapchainImages& si, VkDevice d,
+    List<Framebuffer>& f, const List<ImageView>& iv, VkRenderPass rp, u32 width,
-    VkRenderPass rp, u32 width, u32 height) {
+    u32 height) {
-    f.resize(si.imageViews.getLength());
+    for(u32 i = 0; i < iv.getLength(); i++) {
-    for(u32 i = 0; i < si.imageViews.getLength(); i++) {
+        Framebuffer fb;
+        fb.device = iv[i].device;
         VkFramebufferCreateInfo info = {
         VkFramebufferCreateInfo info = {
             .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
             .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
             .renderPass = rp,
             .renderPass = rp,
             .attachmentCount = 1,
             .attachmentCount = 1,
-            .pAttachments = &si.imageViews[i],
+            .pAttachments = iv[i],
             .width = width,
             .width = width,
             .height = height,
             .height = height,
             .layers = 1};
             .layers = 1};
-        VK_CHECK_TRUE(vkCreateFramebuffer(d, &info, nullptr, &f[i]));
+        VK_CHECK_TRUE(vkCreateFramebuffer(fb.device, &info, nullptr, fb));
+        f.put(Core::move(fb));
     }
     }
     return false;
     return false;
 }
 }
 
 
-void destroyVulkanFramebuffers(Core::List<VkFramebuffer>& fs, VkDevice d) {
-    if(d != VK_NULL_HANDLE) {
-        for(VkFramebuffer f : fs) {
-            vkDestroyFramebuffer(d, f, nullptr);
-        }
-    }
-    fs.clear();
-}
-
 bool initCommandVulkanBuffer(
 bool initCommandVulkanBuffer(
     VkCommandBuffer* cb, VkDevice d, VkCommandPool cp) {
     VkCommandBuffer* cb, VkDevice d, VkCommandPool cp) {
     VkCommandBufferAllocateInfo info = {
     VkCommandBufferAllocateInfo info = {
@@ -174,30 +105,3 @@ bool initCommandVulkanBuffer(
     VK_CHECK_TRUE(vkAllocateCommandBuffers(d, &info, cb));
     VK_CHECK_TRUE(vkAllocateCommandBuffers(d, &info, cb));
     return false;
     return false;
 }
 }
-
-bool initVulkanSemaphore(VkSemaphore* s, VkDevice d) {
-    VkSemaphoreCreateInfo info = {
-        .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
-    VK_CHECK_TRUE(vkCreateSemaphore(d, &info, nullptr, s));
-    return false;
-}
-
-void destroyVulkanSemaphore(VkSemaphore s, VkDevice d) {
-    if(d != VK_NULL_HANDLE) {
-        vkDestroySemaphore(d, s, nullptr);
-    }
-}
-
-bool initVulkanFence(VkFence* f, VkDevice d) {
-    VkFenceCreateInfo info = {
-        .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
-        .flags = VK_FENCE_CREATE_SIGNALED_BIT};
-    VK_CHECK_TRUE(vkCreateFence(d, &info, nullptr, f));
-    return false;
-}
-
-void destroyVulkanFence(VkFence f, VkDevice d) {
-    if(d != VK_NULL_HANDLE) {
-        vkDestroyFence(d, f, nullptr);
-    }
-}

+ 13 - 28
src/VulkanUtils.hpp

@@ -1,14 +1,14 @@
 #ifndef GAMINGCORE_VULKAN_UTILS_HPP
 #ifndef GAMINGCORE_VULKAN_UTILS_HPP
 #define GAMINGCORE_VULKAN_UTILS_HPP
 #define GAMINGCORE_VULKAN_UTILS_HPP
 
 
-#define GLFW_INCLUDE_VULKAN
-#include <core/List.hpp>
-#include <core/Types.hpp>
-#include <core/Utility.hpp>
-
-#include <GLFW/glfw3.h>
-
 #include "core/VulkanBase.hpp"
 #include "core/VulkanBase.hpp"
+#include "core/VulkanWrapper.hpp"
+
+using Core::List;
+using Core::Vulkan::Framebuffer;
+using Core::Vulkan::ImageView;
+using Core::Vulkan::ShaderModule;
+using Core::Vulkan::Swapchain;
 
 
 struct VulkanSwapchainData {
 struct VulkanSwapchainData {
     Core::Vulkan::Base& base;
     Core::Vulkan::Base& base;
@@ -16,38 +16,23 @@ struct VulkanSwapchainData {
     VkSurfaceFormatKHR surfaceFormat{};
     VkSurfaceFormatKHR surfaceFormat{};
     VkPresentModeKHR presentMode{};
     VkPresentModeKHR presentMode{};
     VkSharingMode sharingMode{};
     VkSharingMode sharingMode{};
-    Core::List<u32> queueFamilies{};
+    List<u32> queueFamilies{};
 };
 };
 
 
-bool initVulkanSwapchain(VkSwapchainKHR* sc, VulkanSwapchainData* d);
+bool initVulkanSwapchain(Swapchain& sc, VulkanSwapchainData* d);
-void destroyVulkanSwapchain(VkSwapchainKHR s, VkDevice d);
 
 
 struct VulkanSwapchainImages {
 struct VulkanSwapchainImages {
-    Core::List<VkImage> images{};
+    List<VkImage> images{};
-    Core::List<VkImageView> imageViews{};
+    List<ImageView> imageViews{};
 };
 };
 
 
 bool initVulkanSwapchainImages(
 bool initVulkanSwapchainImages(
     VulkanSwapchainImages& si, VkDevice d, VkSwapchainKHR sc, VkFormat format);
     VulkanSwapchainImages& si, VkDevice d, VkSwapchainKHR sc, VkFormat format);
-void destroyVulkanSwapchainImages(VulkanSwapchainImages& si, VkDevice d);
-
-bool initVulkanShaderModule(VkShaderModule* sm, VkDevice d, const char* path);
-void destroyVulkanShaderModule(VkShaderModule sm, VkDevice d);
-
-bool initVulkanPipelineLayout(VkPipelineLayout* pl, VkDevice d);
-void destroyVulkanPipelineLayout(VkPipelineLayout pl, VkDevice d);
 
 
 bool initVulkanFramebuffers(
 bool initVulkanFramebuffers(
-    Core::List<VkFramebuffer>& f, VulkanSwapchainImages& si, VkDevice d,
+    List<Framebuffer>& f, const List<ImageView>& si, VkRenderPass rp, u32 width,
-    VkRenderPass rp, u32 width, u32 height);
+    u32 height);
-void destroyVulkanFramebuffers(Core::List<VkFramebuffer>& f, VkDevice d);
 
 
 bool initCommandVulkanBuffer(VkCommandBuffer* cb, VkDevice d, VkCommandPool cp);
 bool initCommandVulkanBuffer(VkCommandBuffer* cb, VkDevice d, VkCommandPool cp);
 
 
-bool initVulkanSemaphore(VkSemaphore* s, VkDevice d);
-void destroyVulkanSemaphore(VkSemaphore s, VkDevice d);
-
-bool initVulkanFence(VkFence* f, VkDevice d);
-void destroyVulkanFence(VkFence f, VkDevice d);
-
 #endif
 #endif

+ 489 - 431
src/VulkanWrapper.cpp

@@ -1,488 +1,546 @@
 #include "core/VulkanWrapper.hpp"
 #include "core/VulkanWrapper.hpp"
 
 
-#include <core/Array.hpp>
+#include <core/File.hpp>
-#include <core/Logger.hpp>
+#include <core/UniquePointer.hpp>
-#include <core/Utility.hpp>
 
 
 #include "GLFW.hpp"
 #include "GLFW.hpp"
 #include "VulkanUtils.hpp"
 #include "VulkanUtils.hpp"
 #include "core/VulkanBase.hpp"
 #include "core/VulkanBase.hpp"
-#include "core/VulkanUtility.hpp"
 
 
 namespace Vulkan = Core::Vulkan;
 namespace Vulkan = Core::Vulkan;
 
 
-static Core::Vulkan::Base base;
+#define WRAPPER_DESTRUCT(Type, ...)                                \
-
+    using Core::Vulkan::Type;                                      \
-struct BaseData {
+    Type::~Type() {                                                \
-    u32 graphicsFamily = 0;
+        if(device != VK_NULL_HANDLE) {                             \
-    u32 presentFamily = 0;
+            vkDestroy##Type##__VA_ARGS__(device, handle, nullptr); \
-    VkSurfaceFormatKHR surfaceFormat{};
+        }                                                          \
-    VkPresentModeKHR presentMode{};
-};
-
-static BaseData baseData;
-static VkQueue graphicsQueue;
-static VkQueue presentQueue;
-static VkExtent2D swapchainSize;
-static VkSwapchainKHR swapchain;
-static VulkanSwapchainImages images;
-static VkShaderModule vertexShaderModule;
-static VkShaderModule fragmentShaderModule;
-static VkPipelineLayout pipelineLayout;
-static VkRenderPass renderPass;
-static VkViewport viewport;
-static VkRect2D scissor;
-static VkPipeline pipeline;
-static Core::List<VkFramebuffer> framebuffers;
-static VkCommandPool commandPool;
-static constexpr size_t MAX_FRAMES = 2;
-static size_t currentFrame = 0;
-
-typedef struct {
-    VkCommandBuffer commandBuffer;
-    VkSemaphore imageAvailableSemaphore;
-    VkSemaphore renderFinishedSemaphore;
-    VkFence inFlightFence;
-} Frame;
-
-static Frame frames[MAX_FRAMES];
-
-static int getSurfaceFormatPoints(const VkSurfaceFormatKHR& sf) {
-    if(sf.format == VK_FORMAT_B8G8R8A8_UNORM &&
-       sf.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
-        return 10;
     }
     }
-    return 1;
+
+WRAPPER_DESTRUCT(ImageView)
+WRAPPER_DESTRUCT(Framebuffer)
+WRAPPER_DESTRUCT(Semaphore)
+WRAPPER_DESTRUCT(Fence)
+WRAPPER_DESTRUCT(Swapchain, KHR)
+WRAPPER_DESTRUCT(ShaderModule)
+WRAPPER_DESTRUCT(PipelineLayout)
+
+bool Semaphore::init(VkDevice d) {
+    VkSemaphoreCreateInfo info = {
+        .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
+    VK_CHECK_TRUE(vkCreateSemaphore(d, &info, nullptr, &handle));
+    device = d;
+    return false;
 }
 }
 
 
-static int getSurfacePresentModePoints(VkPresentModeKHR m) {
+bool Fence::init(VkDevice d) {
-    if(m == VK_PRESENT_MODE_MAILBOX_KHR) {
+    VkFenceCreateInfo info = {
-        return 5;
+        .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
-    } else if(m == VK_PRESENT_MODE_FIFO_KHR) {
+        .flags = VK_FENCE_CREATE_SIGNALED_BIT};
-        return 2;
+    VK_CHECK_TRUE(vkCreateFence(d, &info, nullptr, &handle));
-    }
+    device = d;
-    return 0;
+    return false;
 }
 }
 
 
-static bool getSwapchainSize(VkExtent2D* size) {
+bool ShaderModule::init(VkDevice d, const char* path) {
-    VkSurfaceCapabilitiesKHR c = {0};
+    List<char> f;
-    if(base.getSurfaceCapabilities(c)) {
+    if(Core::readFile(f, path)) {
         return true;
         return true;
     }
     }
-    if(c.currentExtent.width != 0xFFFF'FFFFu &&
+    size_t l = f.getLength() - 1;
-       c.currentExtent.height != 0xFFFF'FFFFu) {
+    if((l % 4) != 0) {
-        *size = c.currentExtent;
+        VK_REPORT_ERROR("Shader size is not a multiple of 4");
-        LOG_INFO("Swapchain size: #x#", size->width, size->height);
-        return false;
-    }
-    int w = 0;
-    int h = 0;
-    glfwGetFramebufferSize(Core::Window::get(), &w, &h);
-    if(w <= 0 || h <= 0) {
-        LOG_ERROR("Could not get framebuffer size");
         return true;
         return true;
     }
     }
-    LOG_INFO("Framebuffer size: #x#", w, h);
+    VkShaderModuleCreateInfo info = {
-    size->width = Core::clamp(
+        .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
-        static_cast<u32>(w), c.minImageExtent.width, c.maxImageExtent.width);
+        .codeSize = l,
-    size->height = Core::clamp(
+        .pCode = reinterpret_cast<u32*>(static_cast<void*>(&f[0]))};
-        static_cast<u32>(h), c.minImageExtent.height, c.maxImageExtent.height);
+    VK_CHECK_TRUE(vkCreateShaderModule(d, &info, nullptr, &handle));
-    LOG_INFO("Swapchain size: #x#", size->width, size->height);
+    device = d;
     return false;
     return false;
 }
 }
 
 
-static bool initSwapchain() {
+bool PipelineLayout::init(VkDevice d) {
-    VulkanSwapchainData d = {
+    VkPipelineLayoutCreateInfo info = {
-        .base = base,
+        .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
-        .surfaceFormat = baseData.surfaceFormat,
+    VK_CHECK_TRUE(vkCreatePipelineLayout(d, &info, nullptr, &handle));
-        .presentMode = baseData.presentMode};
+    device = d;
-    if(getSwapchainSize(&d.size)) {
+    return false;
-        LOG_ERROR("Could not retrieve any swapchain size");
-        return true;
-    }
-    swapchainSize = d.size;
-    d.queueFamilies.add(baseData.graphicsFamily);
-    if(baseData.graphicsFamily != baseData.presentFamily) {
-        d.queueFamilies.add(baseData.presentFamily);
-        d.sharingMode = VK_SHARING_MODE_CONCURRENT;
-    } else {
-        d.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
-    }
-    return initVulkanSwapchain(&swapchain, &d);
 }
 }
 
 
-static bool initDevice() {
+struct VulkanDummy {
-    bool same = baseData.graphicsFamily == baseData.presentFamily;
+    Core::Vulkan::Base base{};
-    Core::List<Vulkan::DeviceQueueData> data;
+
-    data.add(baseData.graphicsFamily, 1.0f);
+    struct BaseData {
-    if(!same) {
+        u32 graphicsFamily = 0;
-        data.add(baseData.presentFamily, 1.0f);
+        u32 presentFamily = 0;
+        VkSurfaceFormatKHR surfaceFormat{};
+        VkPresentModeKHR presentMode{};
     };
     };
-    Core::List<const char*> extensions;
+
-    extensions.add(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+    BaseData baseData{};
-    if(base.initDevice(data, extensions)) {
+    VkQueue graphicsQueue{};
-        return true;
+    VkQueue presentQueue{};
+    VkExtent2D swapchainSize{};
+    Swapchain swapchain{};
+    VulkanSwapchainImages images{};
+    ShaderModule vertexShaderModule{};
+    ShaderModule fragmentShaderModule{};
+    PipelineLayout pipelineLayout{};
+    VkRenderPass renderPass{};
+    VkViewport viewport{};
+    VkRect2D scissor{};
+    VkPipeline pipeline{};
+    Core::List<Vulkan::Framebuffer> framebuffers{};
+    VkCommandPool commandPool{};
+    static constexpr size_t MAX_FRAMES = 2;
+    size_t currentFrame = 0;
+    bool shouldWait = false;
+
+    struct Frame {
+        VkCommandBuffer commandBuffer{};
+        Semaphore imageAvailableSemaphore{};
+        Semaphore renderFinishedSemaphore{};
+        Fence inFlightFence{};
+    };
+
+    Frame frames[MAX_FRAMES];
+
+    static int getSurfaceFormatPoints(const VkSurfaceFormatKHR& sf) {
+        if(sf.format == VK_FORMAT_B8G8R8A8_UNORM &&
+           sf.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
+            return 10;
+        }
+        return 1;
     }
     }
-    vkGetDeviceQueue(base, baseData.graphicsFamily, 0, &graphicsQueue);
+
-    if(graphicsQueue == VK_NULL_HANDLE) {
+    static int getSurfacePresentModePoints(VkPresentModeKHR m) {
-        LOG_ERROR("Cannot get device graphics queue");
+        if(m == VK_PRESENT_MODE_MAILBOX_KHR) {
-        return true;
+            return 5;
+        } else if(m == VK_PRESENT_MODE_FIFO_KHR) {
+            return 2;
+        }
+        return 0;
     }
     }
-    if(same) {
+
-        presentQueue = graphicsQueue;
+    bool getSwapchainSize(VkExtent2D* size) {
+        VkSurfaceCapabilitiesKHR c = {0};
+        if(base.getSurfaceCapabilities(c)) {
+            return true;
+        }
+        if(c.currentExtent.width != 0xFFFF'FFFFu &&
+           c.currentExtent.height != 0xFFFF'FFFFu) {
+            *size = c.currentExtent;
+            LOG_INFO("Swapchain size: #x#", size->width, size->height);
+            return false;
+        }
+        int w = 0;
+        int h = 0;
+        glfwGetFramebufferSize(Core::Window::get(), &w, &h);
+        if(w <= 0 || h <= 0) {
+            LOG_ERROR("Could not get framebuffer size");
+            return true;
+        }
+        LOG_INFO("Framebuffer size: #x#", w, h);
+        size->width = Core::clamp(
+            static_cast<u32>(w), c.minImageExtent.width,
+            c.maxImageExtent.width);
+        size->height = Core::clamp(
+            static_cast<u32>(h), c.minImageExtent.height,
+            c.maxImageExtent.height);
+        LOG_INFO("Swapchain size: #x#", size->width, size->height);
         return false;
         return false;
     }
     }
-    vkGetDeviceQueue(base, baseData.presentFamily, 0, &presentQueue);
-    if(presentQueue == VK_NULL_HANDLE) {
-        LOG_ERROR("Cannot get device present queue");
-        return true;
-    }
-    return false;
-}
 
 
-static int getDevicePoints(Vulkan::Base& b, BaseData& d) {
+    bool initSwapchain() {
-    int points = 0;
+        VulkanSwapchainData d = {
-    VkPhysicalDeviceProperties p;
+            .base = base,
-    b.getPhysicalDeviceProperties(p);
+            .surfaceFormat = baseData.surfaceFormat,
-    LOG_INFO("Checking '#'", p.deviceName);
+            .presentMode = baseData.presentMode};
-    switch(p.deviceType) {
+        if(getSwapchainSize(&d.size)) {
-        case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: points += 100; break;
+            LOG_ERROR("Could not retrieve any swapchain size");
-        case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: points += 50; break;
+            return true;
-        case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: points += 20; break;
+        }
-        default: break;
+        swapchainSize = d.size;
-    }
+        d.queueFamilies.add(baseData.graphicsFamily);
-    d.graphicsFamily = b.findQueueFamily(VK_QUEUE_GRAPHICS_BIT);
+        if(baseData.graphicsFamily != baseData.presentFamily) {
-    if(d.graphicsFamily == Vulkan::INVALID_QUEUE_FAMILY) {
+            d.queueFamilies.add(baseData.presentFamily);
-        LOG_INFO("> ... has no graphics family");
+            d.sharingMode = VK_SHARING_MODE_CONCURRENT;
-        points = -1;
+        } else {
+            d.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+        }
+        return initVulkanSwapchain(swapchain, &d);
     }
     }
-    d.presentFamily = b.findSurfaceQueueFamily();
+
-    if(d.presentFamily == Vulkan::INVALID_QUEUE_FAMILY) {
+    bool initDevice() {
-        LOG_INFO("> ... has no present family");
+        bool same = baseData.graphicsFamily == baseData.presentFamily;
-        points = -1;
+        Core::List<Vulkan::DeviceQueueData> data;
+        data.add(baseData.graphicsFamily, 1.0f);
+        if(!same) {
+            data.add(baseData.presentFamily, 1.0f);
+        };
+        Core::List<const char*> extensions;
+        extensions.add(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+        if(base.initDevice(data, extensions)) {
+            return true;
+        }
+        vkGetDeviceQueue(base, baseData.graphicsFamily, 0, &graphicsQueue);
+        if(graphicsQueue == VK_NULL_HANDLE) {
+            LOG_ERROR("Cannot get device graphics queue");
+            return true;
+        }
+        if(same) {
+            presentQueue = graphicsQueue;
+            return false;
+        }
+        vkGetDeviceQueue(base, baseData.presentFamily, 0, &presentQueue);
+        if(presentQueue == VK_NULL_HANDLE) {
+            LOG_ERROR("Cannot get device present queue");
+            return true;
+        }
+        return false;
     }
     }
-    if(!b.hasExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
+
-        LOG_INFO("> ... has no swapchain support");
+    static int getDevicePoints(Vulkan::Base& b, BaseData& d) {
-        points = -1;
+        int points = 0;
+        VkPhysicalDeviceProperties p;
+        b.getPhysicalDeviceProperties(p);
+        LOG_INFO("Checking '#'", p.deviceName);
+        switch(p.deviceType) {
+            case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: points += 100; break;
+            case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: points += 50; break;
+            case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: points += 20; break;
+            default: break;
+        }
+        d.graphicsFamily = b.findQueueFamily(VK_QUEUE_GRAPHICS_BIT);
+        if(d.graphicsFamily == Vulkan::INVALID_QUEUE_FAMILY) {
+            LOG_INFO("> ... has no graphics family");
+            points = -1;
+        }
+        d.presentFamily = b.findSurfaceQueueFamily();
+        if(d.presentFamily == Vulkan::INVALID_QUEUE_FAMILY) {
+            LOG_INFO("> ... has no present family");
+            points = -1;
+        }
+        if(!b.hasExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
+            LOG_INFO("> ... has no swapchain support");
+            points = -1;
+        }
+        if(b.findSurfaceFormat(d.surfaceFormat, getSurfaceFormatPoints)) {
+            LOG_INFO("> ... has no matching surface format");
+            points = -1;
+        } else {
+            points += getSurfaceFormatPoints(d.surfaceFormat);
+        }
+        if(b.findSurfacePresentMode(
+               d.presentMode, getSurfacePresentModePoints)) {
+            LOG_INFO("> ... has no matching present mode");
+            points = -1;
+        } else {
+            points += getSurfacePresentModePoints(d.presentMode);
+        }
+        LOG_INFO("> Final points: #", points);
+        return points;
     }
     }
-    if(b.findSurfaceFormat(d.surfaceFormat, getSurfaceFormatPoints)) {
+
-        LOG_INFO("> ... has no matching surface format");
+    bool initPhysicalDevice() {
-        points = -1;
+        LOG_INFO("Searching for physical devices ...");
-    } else {
+        if(base.findPhysicalDevice(baseData, getDevicePoints)) {
-        points += getSurfaceFormatPoints(d.surfaceFormat);
+            LOG_ERROR("No matching physical device was found");
+            return true;
+        }
+        VkPhysicalDeviceProperties p;
+        base.getPhysicalDeviceProperties(p);
+        LOG_INFO("Best Device: #", p.deviceName);
+        return false;
     }
     }
-    if(b.findSurfacePresentMode(d.presentMode, getSurfacePresentModePoints)) {
+
-        LOG_INFO("> ... has no matching present mode");
+    bool initSwapchainImages() {
-        points = -1;
+        if(initVulkanSwapchainImages(
-    } else {
+               images, base, swapchain, baseData.surfaceFormat.format)) {
-        points += getSurfacePresentModePoints(d.presentMode);
+            LOG_ERROR("Could not get swapchain images");
+            return true;
+        }
+        LOG_INFO("Found # images", images.images.getLength());
+        return false;
     }
     }
-    LOG_INFO("> Final points: #", points);
-    return points;
-}
 
 
-static bool initPhysicalDevice() {
+    bool initShaders() {
-    LOG_INFO("Searching for physical devices ...");
+        return vertexShaderModule.init(base, "shaders/vertex.spv") ||
-    if(base.findPhysicalDevice(baseData, getDevicePoints)) {
+               fragmentShaderModule.init(base, "shaders/fragment.spv");
-        LOG_ERROR("No matching physical device was found");
-        return true;
     }
     }
-    VkPhysicalDeviceProperties p;
-    base.getPhysicalDeviceProperties(p);
-    LOG_INFO("Best Device: #", p.deviceName);
-    return false;
-}
 
 
-static bool initSwapchainImages() {
+    bool initPipeline() {
-    if(initVulkanSwapchainImages(
+        VkPipelineShaderStageCreateInfo stages[2] = {
-           images, base, swapchain, baseData.surfaceFormat.format)) {
+            {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
-        LOG_ERROR("Could not get swapchain images");
+             .stage = VK_SHADER_STAGE_VERTEX_BIT,
-        return true;
+             .module = vertexShaderModule,
+             .pName = "main"},
+            {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+             .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
+             .module = fragmentShaderModule,
+             .pName = "main"}};
+
+        VkPipelineVertexInputStateCreateInfo vertexInputState = {
+            .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO};
+
+        VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = {
+            .sType =
+                VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
+            .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
+            .primitiveRestartEnable = false};
+
+        viewport = {
+            .x = 0.0f,
+            .y = 0.0f,
+            .width = static_cast<float>(swapchainSize.width),
+            .height = static_cast<float>(swapchainSize.height),
+            .minDepth = 0.0f,
+            .maxDepth = 1.0f};
+        scissor = {.offset = {0, 0}, .extent = swapchainSize};
+        VkPipelineViewportStateCreateInfo viewportState = {
+            .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+            .viewportCount = 1,
+            .pViewports = &viewport,
+            .scissorCount = 1,
+            .pScissors = &scissor};
+
+        VkPipelineRasterizationStateCreateInfo rasterizationState = {
+            .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
+            .depthClampEnable = false,
+            .rasterizerDiscardEnable = false,
+            .polygonMode = VK_POLYGON_MODE_FILL,
+            .cullMode = VK_CULL_MODE_BACK_BIT,
+            .frontFace = VK_FRONT_FACE_CLOCKWISE,
+            .depthBiasEnable = false,
+            .depthBiasConstantFactor = 0.0f,
+            .depthBiasClamp = 0.0f,
+            .depthBiasSlopeFactor = 0.0f,
+            .lineWidth = 1.0f};
+
+        VkPipelineMultisampleStateCreateInfo multisampleState = {
+            .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
+            .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
+            .sampleShadingEnable = false,
+            .minSampleShading = 1.0f,
+            .pSampleMask = nullptr,
+            .alphaToCoverageEnable = false,
+            .alphaToOneEnable = false};
+
+        VkPipelineColorBlendAttachmentState colorBlendAttachmentState = {
+            .blendEnable = false,
+            .srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
+            .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
+            .colorBlendOp = VK_BLEND_OP_ADD,
+            .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
+            .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
+            .alphaBlendOp = VK_BLEND_OP_ADD,
+            .colorWriteMask =
+                VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
+                VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT};
+
+        VkPipelineColorBlendStateCreateInfo colorBlendState = {
+            .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
+            .logicOpEnable = false,
+            .logicOp = VK_LOGIC_OP_COPY,
+            .attachmentCount = 1,
+            .pAttachments = &colorBlendAttachmentState,
+            .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}};
+
+        Core::Array<VkDynamicState, 2> dynamicStates = {
+            {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}};
+
+        VkPipelineDynamicStateCreateInfo dynamicState = {
+            .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
+            .dynamicStateCount = dynamicStates.getLength(),
+            .pDynamicStates = dynamicStates.begin()};
+
+        VkGraphicsPipelineCreateInfo info = {
+            .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+            .stageCount = 2,
+            .pStages = stages,
+            .pVertexInputState = &vertexInputState,
+            .pInputAssemblyState = &inputAssemblyState,
+            .pTessellationState = nullptr,
+            .pViewportState = &viewportState,
+            .pRasterizationState = &rasterizationState,
+            .pMultisampleState = &multisampleState,
+            .pDepthStencilState = nullptr,
+            .pColorBlendState = &colorBlendState,
+            .pDynamicState = &dynamicState,
+            .layout = pipelineLayout,
+            .renderPass = renderPass,
+            .subpass = 0,
+            .basePipelineHandle = VK_NULL_HANDLE,
+            .basePipelineIndex = -1};
+
+        VK_CHECK_TRUE(vkCreateGraphicsPipelines(
+            base, VK_NULL_HANDLE, 1, &info, nullptr, &pipeline));
+        return false;
     }
     }
-    LOG_INFO("Found # images", images.images.getLength());
-    return false;
-}
 
 
-static bool initShaders() {
+    bool initRenderPass() {
-    return initVulkanShaderModule(
+        VkAttachmentDescription c = {
-               &vertexShaderModule, base, "shaders/vertex.spv") ||
+            .format = baseData.surfaceFormat.format,
-           initVulkanShaderModule(
+            .samples = VK_SAMPLE_COUNT_1_BIT,
-               &fragmentShaderModule, base, "shaders/fragment.spv");
+            .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
-}
+            .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+            .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+            .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+            .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+            .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR};
+        VkAttachmentReference ca = {
+            .attachment = 0,
+            .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
+        VkSubpassDescription subpass = {
+            .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
+            .colorAttachmentCount = 1,
+            .pColorAttachments = &ca};
+        VkSubpassDependency dependency = {
+            .srcSubpass = VK_SUBPASS_EXTERNAL,
+            .dstSubpass = 0,
+            .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+            .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+            .srcAccessMask = 0,
+            .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+        };
+        VkRenderPassCreateInfo info = {
+            .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
+            .attachmentCount = 1,
+            .pAttachments = &c,
+            .subpassCount = 1,
+            .pSubpasses = &subpass,
+            .dependencyCount = 1,
+            .pDependencies = &dependency};
+        VK_CHECK_TRUE(vkCreateRenderPass(base, &info, nullptr, &renderPass));
+        return false;
+    }
 
 
-static bool initPipeline() {
+    bool initCommandPool() {
-    VkPipelineShaderStageCreateInfo stages[2] = {
+        VkCommandPoolCreateInfo info = {
-        {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+            .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
-         .stage = VK_SHADER_STAGE_VERTEX_BIT,
+            .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
-         .module = vertexShaderModule,
+            .queueFamilyIndex = baseData.graphicsFamily};
-         .pName = "main"},
+        VK_CHECK_TRUE(vkCreateCommandPool(base, &info, nullptr, &commandPool));
-        {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+        return false;
-         .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
+    }
-         .module = fragmentShaderModule,
-         .pName = "main"}};
-
-    VkPipelineVertexInputStateCreateInfo vertexInputState = {
-        .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO};
-
-    VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = {
-        .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
-        .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
-        .primitiveRestartEnable = false};
-
-    viewport = {
-        .x = 0.0f,
-        .y = 0.0f,
-        .width = static_cast<float>(swapchainSize.width),
-        .height = static_cast<float>(swapchainSize.height),
-        .minDepth = 0.0f,
-        .maxDepth = 1.0f};
-    scissor = {.offset = {0, 0}, .extent = swapchainSize};
-    VkPipelineViewportStateCreateInfo viewportState = {
-        .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
-        .viewportCount = 1,
-        .pViewports = &viewport,
-        .scissorCount = 1,
-        .pScissors = &scissor};
-
-    VkPipelineRasterizationStateCreateInfo rasterizationState = {
-        .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
-        .depthClampEnable = false,
-        .rasterizerDiscardEnable = false,
-        .polygonMode = VK_POLYGON_MODE_FILL,
-        .cullMode = VK_CULL_MODE_BACK_BIT,
-        .frontFace = VK_FRONT_FACE_CLOCKWISE,
-        .depthBiasEnable = false,
-        .depthBiasConstantFactor = 0.0f,
-        .depthBiasClamp = 0.0f,
-        .depthBiasSlopeFactor = 0.0f,
-        .lineWidth = 1.0f};
-
-    VkPipelineMultisampleStateCreateInfo multisampleState = {
-        .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
-        .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
-        .sampleShadingEnable = false,
-        .minSampleShading = 1.0f,
-        .pSampleMask = nullptr,
-        .alphaToCoverageEnable = false,
-        .alphaToOneEnable = false};
-
-    VkPipelineColorBlendAttachmentState colorBlendAttachmentState = {
-        .blendEnable = false,
-        .srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
-        .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
-        .colorBlendOp = VK_BLEND_OP_ADD,
-        .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
-        .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
-        .alphaBlendOp = VK_BLEND_OP_ADD,
-        .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
-                          VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT};
-
-    VkPipelineColorBlendStateCreateInfo colorBlendState = {
-        .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
-        .logicOpEnable = false,
-        .logicOp = VK_LOGIC_OP_COPY,
-        .attachmentCount = 1,
-        .pAttachments = &colorBlendAttachmentState,
-        .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}};
-
-    Core::Array<VkDynamicState, 2> dynamicStates = {
-        {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}};
-
-    VkPipelineDynamicStateCreateInfo dynamicState = {
-        .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
-        .dynamicStateCount = dynamicStates.getLength(),
-        .pDynamicStates = dynamicStates.begin()};
-
-    VkGraphicsPipelineCreateInfo info = {
-        .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
-        .stageCount = 2,
-        .pStages = stages,
-        .pVertexInputState = &vertexInputState,
-        .pInputAssemblyState = &inputAssemblyState,
-        .pTessellationState = nullptr,
-        .pViewportState = &viewportState,
-        .pRasterizationState = &rasterizationState,
-        .pMultisampleState = &multisampleState,
-        .pDepthStencilState = nullptr,
-        .pColorBlendState = &colorBlendState,
-        .pDynamicState = &dynamicState,
-        .layout = pipelineLayout,
-        .renderPass = renderPass,
-        .subpass = 0,
-        .basePipelineHandle = VK_NULL_HANDLE,
-        .basePipelineIndex = -1};
-
-    VK_CHECK_TRUE(vkCreateGraphicsPipelines(
-        base, VK_NULL_HANDLE, 1, &info, nullptr, &pipeline));
-    return false;
-}
 
 
-static bool initRenderPass() {
+    bool fillCommandBuffer(VkCommandBuffer cb, u32 index) {
-    VkAttachmentDescription c = {
+        VkCommandBufferBeginInfo info = {
-        .format = baseData.surfaceFormat.format,
+            .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
-        .samples = VK_SAMPLE_COUNT_1_BIT,
+        VK_CHECK_TRUE(vkBeginCommandBuffer(cb, &info));
-        .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
+        VkClearValue v = {.color = {.float32 = {0.0f, 0.5f, 0.0f, 1.0f}}};
-        .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+        VkRenderPassBeginInfo rInfo = {
-        .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+            .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
-        .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+            .renderPass = renderPass,
-        .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+            .framebuffer = framebuffers[index],
-        .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR};
+            .renderArea = {.offset = {0, 0}, .extent = swapchainSize},
-    VkAttachmentReference ca = {
+            .clearValueCount = 1,
-        .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
+            .pClearValues = &v};
-    VkSubpassDescription subpass = {
+        vkCmdBeginRenderPass(cb, &rInfo, VK_SUBPASS_CONTENTS_INLINE);
-        .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
+        vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
-        .colorAttachmentCount = 1,
+        vkCmdSetViewport(cb, 0, 1, &viewport);
-        .pColorAttachments = &ca};
+        vkCmdSetScissor(cb, 0, 1, &scissor);
-    VkSubpassDependency dependency = {
+        vkCmdDraw(cb, 3, 1, 0, 0);
-        .srcSubpass = VK_SUBPASS_EXTERNAL,
+        vkCmdEndRenderPass(cb);
-        .dstSubpass = 0,
+        VK_CHECK_TRUE(vkEndCommandBuffer(cb));
-        .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+        return false;
-        .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+    }
-        .srcAccessMask = 0,
-        .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
-    };
-    VkRenderPassCreateInfo info = {
-        .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
-        .attachmentCount = 1,
-        .pAttachments = &c,
-        .subpassCount = 1,
-        .pSubpasses = &subpass,
-        .dependencyCount = 1,
-        .pDependencies = &dependency};
-    VK_CHECK_TRUE(vkCreateRenderPass(base, &info, nullptr, &renderPass));
-    return false;
-}
 
 
-static bool initCommandPool() {
+    bool initFrames() {
-    VkCommandPoolCreateInfo info = {
+        for(size_t i = 0; i < MAX_FRAMES; i++) {
-        .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+            if(initCommandVulkanBuffer(
-        .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
+                   &frames[i].commandBuffer, base, commandPool) ||
-        .queueFamilyIndex = baseData.graphicsFamily};
+               frames[i].imageAvailableSemaphore.init(base) ||
-    VK_CHECK_TRUE(vkCreateCommandPool(base, &info, nullptr, &commandPool));
+               frames[i].renderFinishedSemaphore.init(base) ||
-    return false;
+               frames[i].inFlightFence.init(base)) {
-}
+                return true;
+            }
+        }
+        return false;
+    }
 
 
-static bool fillCommandBuffer(VkCommandBuffer cb, u32 index) {
+    bool init() {
-    VkCommandBufferBeginInfo info = {
+        return base.init() || initPhysicalDevice() || initDevice() ||
-        .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
+               initSwapchain() || initSwapchainImages() || initShaders() ||
-    VK_CHECK_TRUE(vkBeginCommandBuffer(cb, &info));
+               pipelineLayout.init(base) || initRenderPass() ||
-    VkClearValue v = {.color = {.float32 = {0.0f, 0.5f, 0.0f, 1.0f}}};
+               initPipeline() ||
-    VkRenderPassBeginInfo rInfo = {
+               initVulkanFramebuffers(
-        .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+                   framebuffers, images.imageViews, renderPass,
-        .renderPass = renderPass,
+                   swapchainSize.width, swapchainSize.height) ||
-        .framebuffer = framebuffers[index],
+               initCommandPool() || initFrames();
-        .renderArea = {.offset = {0, 0}, .extent = swapchainSize},
+    }
-        .clearValueCount = 1,
-        .pClearValues = &v};
-    vkCmdBeginRenderPass(cb, &rInfo, VK_SUBPASS_CONTENTS_INLINE);
-    vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
-    vkCmdSetViewport(cb, 0, 1, &viewport);
-    vkCmdSetScissor(cb, 0, 1, &scissor);
-    vkCmdDraw(cb, 3, 1, 0, 0);
-    vkCmdEndRenderPass(cb);
-    VK_CHECK_TRUE(vkEndCommandBuffer(cb));
-    return false;
-}
 
 
-static bool initFrames() {
+    bool render() {
-    for(size_t i = 0; i < MAX_FRAMES; i++) {
+        if(shouldWait) {
-        if(initCommandVulkanBuffer(
+            return false;
-               &frames[i].commandBuffer, base, commandPool) ||
-           initVulkanSemaphore(&frames[i].imageAvailableSemaphore, base) ||
-           initVulkanSemaphore(&frames[i].renderFinishedSemaphore, base) ||
-           initVulkanFence(&frames[i].inFlightFence, base)) {
-            return true;
         }
         }
+        Frame* f = frames + currentFrame;
+        VK_CHECK_TRUE(
+            vkWaitForFences(base, 1, f->inFlightFence, true, UINT64_MAX));
+        VK_CHECK_TRUE(vkResetFences(base, 1, f->inFlightFence));
+
+        uint32_t imageIndex = 0;
+        VK_CHECK_TRUE(vkAcquireNextImageKHR(
+            base, swapchain, UINT64_MAX, f->imageAvailableSemaphore,
+            VK_NULL_HANDLE, &imageIndex));
+
+        vkResetCommandBuffer(f->commandBuffer, 0);
+        fillCommandBuffer(f->commandBuffer, imageIndex);
+
+        VkSemaphore waitSemaphores[] = {f->imageAvailableSemaphore};
+        VkPipelineStageFlags waitStages[] = {
+            VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
+        VkSemaphore signalSemaphores[] = {f->renderFinishedSemaphore};
+        VkSubmitInfo info = {
+            .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+            .waitSemaphoreCount = 1,
+            .pWaitSemaphores = waitSemaphores,
+            .pWaitDstStageMask = waitStages,
+            .commandBufferCount = 1,
+            .pCommandBuffers = &f->commandBuffer,
+            .signalSemaphoreCount = 1,
+            .pSignalSemaphores = signalSemaphores};
+        VK_CHECK_TRUE(vkQueueSubmit(graphicsQueue, 1, &info, f->inFlightFence));
+
+        VkPresentInfoKHR presentInfo = {
+            .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
+            .waitSemaphoreCount = 1,
+            .pWaitSemaphores = signalSemaphores,
+            .swapchainCount = 1,
+            .pSwapchains = swapchain,
+            .pImageIndices = &imageIndex};
+
+        VK_CHECK_TRUE(vkQueuePresentKHR(presentQueue, &presentInfo));
+        currentFrame = (currentFrame + 1) % MAX_FRAMES;
+        return false;
     }
     }
-    return false;
-}
 
 
-bool Vulkan::init() {
+    void destroy() {
-    return base.init() || initPhysicalDevice() || initDevice() ||
+        if(static_cast<VkDevice>(base) != VK_NULL_HANDLE) {
-           initSwapchain() || initSwapchainImages() || initShaders() ||
+            vkDeviceWaitIdle(base);
-           initVulkanPipelineLayout(&pipelineLayout, base) ||
+            vkDestroyCommandPool(base, commandPool, nullptr);
-           initRenderPass() || initPipeline() ||
+            vkDestroyPipeline(base, pipeline, nullptr);
-           initVulkanFramebuffers(
+            vkDestroyRenderPass(base, renderPass, nullptr);
-               framebuffers, images, base, renderPass, swapchainSize.width,
+        }
-               swapchainSize.height) ||
+    }
-           initCommandPool() || initFrames();
+};
-}
 
 
-static bool shouldWait = false;
+static Core::UniquePointer<VulkanDummy> dummy;
 
 
-static bool render() {
+bool Vulkan::init() {
-    if(shouldWait) {
+    dummy = new VulkanDummy();
-        return false;
+    return dummy->init();
-    }
-    Frame* f = frames + currentFrame;
-    VK_CHECK_TRUE(
-        vkWaitForFences(base, 1, &f->inFlightFence, true, UINT64_MAX));
-    VK_CHECK_TRUE(vkResetFences(base, 1, &f->inFlightFence));
-
-    uint32_t imageIndex = 0;
-    VK_CHECK_TRUE(vkAcquireNextImageKHR(
-        base, swapchain, UINT64_MAX, f->imageAvailableSemaphore, VK_NULL_HANDLE,
-        &imageIndex));
-
-    vkResetCommandBuffer(f->commandBuffer, 0);
-    fillCommandBuffer(f->commandBuffer, imageIndex);
-
-    VkSemaphore waitSemaphores[] = {f->imageAvailableSemaphore};
-    VkPipelineStageFlags waitStages[] = {
-        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
-    VkSemaphore signalSemaphores[] = {f->renderFinishedSemaphore};
-    VkSubmitInfo info = {
-        .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
-        .waitSemaphoreCount = 1,
-        .pWaitSemaphores = waitSemaphores,
-        .pWaitDstStageMask = waitStages,
-        .commandBufferCount = 1,
-        .pCommandBuffers = &f->commandBuffer,
-        .signalSemaphoreCount = 1,
-        .pSignalSemaphores = signalSemaphores};
-    VK_CHECK_TRUE(vkQueueSubmit(graphicsQueue, 1, &info, f->inFlightFence));
-
-    VkPresentInfoKHR presentInfo = {
-        .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
-        .waitSemaphoreCount = 1,
-        .pWaitSemaphores = signalSemaphores,
-        .swapchainCount = 1,
-        .pSwapchains = &swapchain,
-        .pImageIndices = &imageIndex};
-
-    VK_CHECK_TRUE(vkQueuePresentKHR(presentQueue, &presentInfo));
-    currentFrame = (currentFrame + 1) % MAX_FRAMES;
-    return false;
 }
 }
 
 
 void Vulkan::render() {
 void Vulkan::render() {
-    if(::render()) {
+    if(dummy->render()) {
-        shouldWait = true;
+        dummy->shouldWait = true;
     }
     }
 }
 }
 
 
 void Vulkan::destroy(void) {
 void Vulkan::destroy(void) {
-    if(static_cast<VkDevice>(base) != VK_NULL_HANDLE) {
+    dummy->destroy();
-        vkDeviceWaitIdle(base);
+    dummy = nullptr;
-        for(size_t i = 0; i < MAX_FRAMES; i++) {
-            Frame* f = frames + i;
-            destroyVulkanFence(f->inFlightFence, base);
-            destroyVulkanSemaphore(f->imageAvailableSemaphore, base);
-            destroyVulkanSemaphore(f->renderFinishedSemaphore, base);
-        }
-        vkDestroyCommandPool(base, commandPool, nullptr);
-        destroyVulkanFramebuffers(framebuffers, base);
-        vkDestroyPipeline(base, pipeline, nullptr);
-        vkDestroyRenderPass(base, renderPass, nullptr);
-    }
-    destroyVulkanPipelineLayout(pipelineLayout, base);
-    destroyVulkanShaderModule(vertexShaderModule, base);
-    destroyVulkanShaderModule(fragmentShaderModule, base);
-    destroyVulkanSwapchainImages(images, base);
-    destroyVulkanSwapchain(swapchain, base);
-    base.destroy();
-    // frees the memory before the static objects are gone
-    images.images = List<VkImage>();
-    images.imageViews = List<VkImageView>();
-    framebuffers = List<VkFramebuffer>();
 }
 }