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
 #define GAMINGCORE_VULKAN_WRAPPER_HPP
 
+#include <core/Meta.hpp>
+
+#include <vulkan/vulkan.h>
+
 namespace Core::Vulkan {
     bool init();
     void render();
     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

+ 13 - 109
src/VulkanUtils.cpp

@@ -1,7 +1,6 @@
 #include "VulkanUtils.hpp"
 
 #include <core/Array.hpp>
-#include <core/File.hpp>
 #include <core/Logger.hpp>
 
 #include "core/VulkanUtility.hpp"
@@ -16,7 +15,7 @@ static u32 getSwapImageCount(const VkSurfaceCapabilitiesKHR* caps) {
     return c;
 }
 
-bool initVulkanSwapchain(VkSwapchainKHR* sc, VulkanSwapchainData* d) {
+bool initVulkanSwapchain(Swapchain& sc, VulkanSwapchainData* d) {
     VkSurfaceCapabilitiesKHR caps = {0};
     if(d->base.getSurfaceCapabilities(caps)) {
         return true;
@@ -39,17 +38,12 @@ bool initVulkanSwapchain(VkSwapchainKHR* sc, VulkanSwapchainData* d) {
         .clipped = VK_TRUE,
         .oldSwapchain = VK_NULL_HANDLE};
     VK_CHECK_TRUE(vkCreateSwapchainKHR(d->base, &ci, nullptr, sc));
+    sc.device = d->base;
     return false;
 }
 
-void destroyVulkanSwapchain(VkSwapchainKHR s, VkDevice d) {
-    if(d != VK_NULL_HANDLE) {
-        vkDestroySwapchainKHR(d, s, nullptr);
-    }
-}
-
 static bool createImageView(
-    VkImageView& view, VkDevice d, VkImage image, VkFormat format) {
+    ImageView& view, VkDevice d, VkImage image, VkFormat format) {
     VkImageViewCreateInfo info = {
         .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
         .image = image,
@@ -61,7 +55,8 @@ static bool createImageView(
             .levelCount = 1,
             .baseArrayLayer = 0,
             .layerCount = 1}};
-    VK_CHECK_TRUE(vkCreateImageView(d, &info, nullptr, &view));
+    VK_CHECK_TRUE(vkCreateImageView(d, &info, nullptr, view));
+    view.device = d;
     return false;
 }
 
@@ -80,90 +75,26 @@ bool initVulkanSwapchainImages(
     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(
-    Core::List<VkFramebuffer>& f, VulkanSwapchainImages& si, VkDevice d,
-    VkRenderPass rp, u32 width, u32 height) {
-    f.resize(si.imageViews.getLength());
-    for(u32 i = 0; i < si.imageViews.getLength(); i++) {
+    List<Framebuffer>& f, const List<ImageView>& iv, VkRenderPass rp, u32 width,
+    u32 height) {
+    for(u32 i = 0; i < iv.getLength(); i++) {
+        Framebuffer fb;
+        fb.device = iv[i].device;
         VkFramebufferCreateInfo info = {
             .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
             .renderPass = rp,
             .attachmentCount = 1,
-            .pAttachments = &si.imageViews[i],
+            .pAttachments = iv[i],
             .width = width,
             .height = height,
             .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;
 }
 
-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(
     VkCommandBuffer* cb, VkDevice d, VkCommandPool cp) {
     VkCommandBufferAllocateInfo info = {
@@ -174,30 +105,3 @@ bool initCommandVulkanBuffer(
     VK_CHECK_TRUE(vkAllocateCommandBuffers(d, &info, cb));
     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
 #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/VulkanWrapper.hpp"
+
+using Core::List;
+using Core::Vulkan::Framebuffer;
+using Core::Vulkan::ImageView;
+using Core::Vulkan::ShaderModule;
+using Core::Vulkan::Swapchain;
 
 struct VulkanSwapchainData {
     Core::Vulkan::Base& base;
@@ -16,38 +16,23 @@ struct VulkanSwapchainData {
     VkSurfaceFormatKHR surfaceFormat{};
     VkPresentModeKHR presentMode{};
     VkSharingMode sharingMode{};
-    Core::List<u32> queueFamilies{};
+    List<u32> queueFamilies{};
 };
 
-bool initVulkanSwapchain(VkSwapchainKHR* sc, VulkanSwapchainData* d);
-void destroyVulkanSwapchain(VkSwapchainKHR s, VkDevice d);
+bool initVulkanSwapchain(Swapchain& sc, VulkanSwapchainData* d);
 
 struct VulkanSwapchainImages {
-    Core::List<VkImage> images{};
-    Core::List<VkImageView> imageViews{};
+    List<VkImage> images{};
+    List<ImageView> imageViews{};
 };
 
 bool initVulkanSwapchainImages(
     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(
-    Core::List<VkFramebuffer>& f, VulkanSwapchainImages& si, VkDevice d,
-    VkRenderPass rp, u32 width, u32 height);
-void destroyVulkanFramebuffers(Core::List<VkFramebuffer>& f, VkDevice d);
+    List<Framebuffer>& f, const List<ImageView>& si, VkRenderPass rp, u32 width,
+    u32 height);
 
 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

+ 489 - 431
src/VulkanWrapper.cpp

@@ -1,488 +1,546 @@
 #include "core/VulkanWrapper.hpp"
 
-#include <core/Array.hpp>
-#include <core/Logger.hpp>
-#include <core/Utility.hpp>
+#include <core/File.hpp>
+#include <core/UniquePointer.hpp>
 
 #include "GLFW.hpp"
 #include "VulkanUtils.hpp"
 #include "core/VulkanBase.hpp"
-#include "core/VulkanUtility.hpp"
 
 namespace Vulkan = Core::Vulkan;
 
-static Core::Vulkan::Base base;
-
-struct BaseData {
-    u32 graphicsFamily = 0;
-    u32 presentFamily = 0;
-    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;
+#define WRAPPER_DESTRUCT(Type, ...)                                \
+    using Core::Vulkan::Type;                                      \
+    Type::~Type() {                                                \
+        if(device != VK_NULL_HANDLE) {                             \
+            vkDestroy##Type##__VA_ARGS__(device, handle, nullptr); \
+        }                                                          \
     }
-    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) {
-    if(m == VK_PRESENT_MODE_MAILBOX_KHR) {
-        return 5;
-    } else if(m == VK_PRESENT_MODE_FIFO_KHR) {
-        return 2;
-    }
-    return 0;
+bool Fence::init(VkDevice d) {
+    VkFenceCreateInfo info = {
+        .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
+        .flags = VK_FENCE_CREATE_SIGNALED_BIT};
+    VK_CHECK_TRUE(vkCreateFence(d, &info, nullptr, &handle));
+    device = d;
+    return false;
 }
 
-static bool getSwapchainSize(VkExtent2D* size) {
-    VkSurfaceCapabilitiesKHR c = {0};
-    if(base.getSurfaceCapabilities(c)) {
+bool ShaderModule::init(VkDevice d, const char* path) {
+    List<char> f;
+    if(Core::readFile(f, path)) {
         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");
+    size_t l = f.getLength() - 1;
+    if((l % 4) != 0) {
+        VK_REPORT_ERROR("Shader size is not a multiple of 4");
         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);
+    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, &handle));
+    device = d;
     return false;
 }
 
-static bool initSwapchain() {
-    VulkanSwapchainData d = {
-        .base = base,
-        .surfaceFormat = baseData.surfaceFormat,
-        .presentMode = baseData.presentMode};
-    if(getSwapchainSize(&d.size)) {
-        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);
+bool PipelineLayout::init(VkDevice d) {
+    VkPipelineLayoutCreateInfo info = {
+        .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
+    VK_CHECK_TRUE(vkCreatePipelineLayout(d, &info, nullptr, &handle));
+    device = d;
+    return false;
 }
 
-static bool initDevice() {
-    bool same = baseData.graphicsFamily == baseData.presentFamily;
-    Core::List<Vulkan::DeviceQueueData> data;
-    data.add(baseData.graphicsFamily, 1.0f);
-    if(!same) {
-        data.add(baseData.presentFamily, 1.0f);
+struct VulkanDummy {
+    Core::Vulkan::Base base{};
+
+    struct BaseData {
+        u32 graphicsFamily = 0;
+        u32 presentFamily = 0;
+        VkSurfaceFormatKHR surfaceFormat{};
+        VkPresentModeKHR presentMode{};
     };
-    Core::List<const char*> extensions;
-    extensions.add(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
-    if(base.initDevice(data, extensions)) {
-        return true;
+
+    BaseData baseData{};
+    VkQueue graphicsQueue{};
+    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) {
-        LOG_ERROR("Cannot get device graphics queue");
-        return true;
+
+    static int getSurfacePresentModePoints(VkPresentModeKHR m) {
+        if(m == VK_PRESENT_MODE_MAILBOX_KHR) {
+            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;
     }
-    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) {
-    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;
+    bool initSwapchain() {
+        VulkanSwapchainData d = {
+            .base = base,
+            .surfaceFormat = baseData.surfaceFormat,
+            .presentMode = baseData.presentMode};
+        if(getSwapchainSize(&d.size)) {
+            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);
     }
-    d.presentFamily = b.findSurfaceQueueFamily();
-    if(d.presentFamily == Vulkan::INVALID_QUEUE_FAMILY) {
-        LOG_INFO("> ... has no present family");
-        points = -1;
+
+    bool initDevice() {
+        bool same = baseData.graphicsFamily == baseData.presentFamily;
+        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");
-        points = -1;
+
+    static int getDevicePoints(Vulkan::Base& b, BaseData& d) {
+        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");
-        points = -1;
-    } else {
-        points += getSurfaceFormatPoints(d.surfaceFormat);
+
+    bool initPhysicalDevice() {
+        LOG_INFO("Searching for physical devices ...");
+        if(base.findPhysicalDevice(baseData, getDevicePoints)) {
+            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");
-        points = -1;
-    } else {
-        points += getSurfacePresentModePoints(d.presentMode);
+
+    bool initSwapchainImages() {
+        if(initVulkanSwapchainImages(
+               images, base, swapchain, baseData.surfaceFormat.format)) {
+            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() {
-    LOG_INFO("Searching for physical devices ...");
-    if(base.findPhysicalDevice(baseData, getDevicePoints)) {
-        LOG_ERROR("No matching physical device was found");
-        return true;
+    bool initShaders() {
+        return vertexShaderModule.init(base, "shaders/vertex.spv") ||
+               fragmentShaderModule.init(base, "shaders/fragment.spv");
     }
-    VkPhysicalDeviceProperties p;
-    base.getPhysicalDeviceProperties(p);
-    LOG_INFO("Best Device: #", p.deviceName);
-    return false;
-}
 
-static bool initSwapchainImages() {
-    if(initVulkanSwapchainImages(
-           images, base, swapchain, baseData.surfaceFormat.format)) {
-        LOG_ERROR("Could not get swapchain images");
-        return true;
+    bool initPipeline() {
+        VkPipelineShaderStageCreateInfo stages[2] = {
+            {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+             .stage = VK_SHADER_STAGE_VERTEX_BIT,
+             .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() {
-    return initVulkanShaderModule(
-               &vertexShaderModule, base, "shaders/vertex.spv") ||
-           initVulkanShaderModule(
-               &fragmentShaderModule, base, "shaders/fragment.spv");
-}
+    bool initRenderPass() {
+        VkAttachmentDescription c = {
+            .format = baseData.surfaceFormat.format,
+            .samples = VK_SAMPLE_COUNT_1_BIT,
+            .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() {
-    VkPipelineShaderStageCreateInfo stages[2] = {
-        {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
-         .stage = VK_SHADER_STAGE_VERTEX_BIT,
-         .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;
-}
+    bool initCommandPool() {
+        VkCommandPoolCreateInfo info = {
+            .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+            .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
+            .queueFamilyIndex = baseData.graphicsFamily};
+        VK_CHECK_TRUE(vkCreateCommandPool(base, &info, nullptr, &commandPool));
+        return false;
+    }
 
-static bool initRenderPass() {
-    VkAttachmentDescription c = {
-        .format = baseData.surfaceFormat.format,
-        .samples = VK_SAMPLE_COUNT_1_BIT,
-        .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;
-}
+    bool fillCommandBuffer(VkCommandBuffer cb, u32 index) {
+        VkCommandBufferBeginInfo info = {
+            .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
+        VK_CHECK_TRUE(vkBeginCommandBuffer(cb, &info));
+        VkClearValue v = {.color = {.float32 = {0.0f, 0.5f, 0.0f, 1.0f}}};
+        VkRenderPassBeginInfo rInfo = {
+            .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+            .renderPass = renderPass,
+            .framebuffer = framebuffers[index],
+            .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 initCommandPool() {
-    VkCommandPoolCreateInfo info = {
-        .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
-        .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
-        .queueFamilyIndex = baseData.graphicsFamily};
-    VK_CHECK_TRUE(vkCreateCommandPool(base, &info, nullptr, &commandPool));
-    return false;
-}
+    bool initFrames() {
+        for(size_t i = 0; i < MAX_FRAMES; i++) {
+            if(initCommandVulkanBuffer(
+                   &frames[i].commandBuffer, base, commandPool) ||
+               frames[i].imageAvailableSemaphore.init(base) ||
+               frames[i].renderFinishedSemaphore.init(base) ||
+               frames[i].inFlightFence.init(base)) {
+                return true;
+            }
+        }
+        return false;
+    }
 
-static bool fillCommandBuffer(VkCommandBuffer cb, u32 index) {
-    VkCommandBufferBeginInfo info = {
-        .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
-    VK_CHECK_TRUE(vkBeginCommandBuffer(cb, &info));
-    VkClearValue v = {.color = {.float32 = {0.0f, 0.5f, 0.0f, 1.0f}}};
-    VkRenderPassBeginInfo rInfo = {
-        .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
-        .renderPass = renderPass,
-        .framebuffer = framebuffers[index],
-        .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;
-}
+    bool init() {
+        return base.init() || initPhysicalDevice() || initDevice() ||
+               initSwapchain() || initSwapchainImages() || initShaders() ||
+               pipelineLayout.init(base) || initRenderPass() ||
+               initPipeline() ||
+               initVulkanFramebuffers(
+                   framebuffers, images.imageViews, renderPass,
+                   swapchainSize.width, swapchainSize.height) ||
+               initCommandPool() || initFrames();
+    }
 
-static bool initFrames() {
-    for(size_t i = 0; i < MAX_FRAMES; i++) {
-        if(initCommandVulkanBuffer(
-               &frames[i].commandBuffer, base, commandPool) ||
-           initVulkanSemaphore(&frames[i].imageAvailableSemaphore, base) ||
-           initVulkanSemaphore(&frames[i].renderFinishedSemaphore, base) ||
-           initVulkanFence(&frames[i].inFlightFence, base)) {
-            return true;
+    bool render() {
+        if(shouldWait) {
+            return false;
         }
+        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() {
-    return base.init() || initPhysicalDevice() || initDevice() ||
-           initSwapchain() || initSwapchainImages() || initShaders() ||
-           initVulkanPipelineLayout(&pipelineLayout, base) ||
-           initRenderPass() || initPipeline() ||
-           initVulkanFramebuffers(
-               framebuffers, images, base, renderPass, swapchainSize.width,
-               swapchainSize.height) ||
-           initCommandPool() || initFrames();
-}
+    void destroy() {
+        if(static_cast<VkDevice>(base) != VK_NULL_HANDLE) {
+            vkDeviceWaitIdle(base);
+            vkDestroyCommandPool(base, commandPool, nullptr);
+            vkDestroyPipeline(base, pipeline, nullptr);
+            vkDestroyRenderPass(base, renderPass, nullptr);
+        }
+    }
+};
 
-static bool shouldWait = false;
+static Core::UniquePointer<VulkanDummy> dummy;
 
-static bool render() {
-    if(shouldWait) {
-        return false;
-    }
-    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;
+bool Vulkan::init() {
+    dummy = new VulkanDummy();
+    return dummy->init();
 }
 
 void Vulkan::render() {
-    if(::render()) {
-        shouldWait = true;
+    if(dummy->render()) {
+        dummy->shouldWait = true;
     }
 }
 
 void Vulkan::destroy(void) {
-    if(static_cast<VkDevice>(base) != VK_NULL_HANDLE) {
-        vkDeviceWaitIdle(base);
-        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>();
+    dummy->destroy();
+    dummy = nullptr;
 }