0
\$\begingroup\$

Here is my voxel engine. I want some review for performance on the renderer. What can be improved? What must change?

Also I'm not sure it is efficient to use vkWaitQueueIdle for syncing. The end goal would be to reproduce a cube world like game.

Code :

Application.h

#pragma once

#include <memory>
#include <chrono>

#include "Window.h"
#include "Renderer.h"
#include "ChunkManager.h"
#include "Player.h"

class Application
{
public:
    Application();
    ~Application();


    void run();


private:

    void update();
    void input();

    std::unique_ptr<Config> m_config;
    std::unique_ptr<Window> m_window;
    std::shared_ptr<Renderer> m_pRenderer;

    std::unique_ptr<ChunkManager> m_pChunkManager;
    std::shared_ptr<Player> m_pPlayer;

    Pipeline base_pipeline;
    Camera m_camera;

    std::chrono::steady_clock::time_point m_last_time = std::chrono::high_resolution_clock::now();
};

Application.cpp

#include "Application.h"


//#include "Math.h"


#include <thread>

Application::Application()
{
    m_pPlayer = std::make_shared<Player>();
    m_config = std::make_unique<Config>();
    m_window = std::make_unique<Window>(m_config, m_pPlayer->getCamera());
    m_pRenderer = std::make_shared<Renderer>(m_window->getHandle(), m_config->width, m_config->height);
    m_pChunkManager = std::make_unique<ChunkManager>(m_pRenderer, m_pPlayer);


    m_pRenderer->createNewPipeline(base_pipeline);
    m_pRenderer->createDepthResources();
    m_pRenderer->createFramebuffer();

    m_pRenderer->createTextureImage();
    m_pRenderer->createTextureImageView();
    m_pRenderer->createTextureSampler();

    m_pRenderer->createDescriptorPool();

    m_pChunkManager->createChunks();
    m_pChunkManager->loadChunks();
    m_pRenderer->createUBO();

    m_pRenderer->allocateCommandBuffers();

    m_pRenderer->createDescriptorSet();
}


Application::~Application()
{
    m_pChunkManager.reset();

    m_pRenderer->cleanSwapchain(std::make_shared<Pipeline>(base_pipeline));
    m_pRenderer->destroyTextures();
    m_pRenderer->destroyDescriptors();
    m_pRenderer->destroyUniformBuffer();

    m_pRenderer.reset();
    m_window.reset();
    m_config.reset();
}

void Application::run()
{
    while (!glfwWindowShouldClose(&m_window->getHandle()))
    {
        glfwPollEvents();
        input();
        update();

        if (m_pRenderer->draw(base_pipeline) != 0)
        {
            m_pChunkManager->setRebuild(true);
        }
    }

    vkDeviceWaitIdle(m_pRenderer->getDevice());
}

void Application::update()
{
    m_pChunkManager->update();

    if (m_pChunkManager->needRebuild())
    {
        for (uint16_t i = 0; i < m_pRenderer->getGraphic().command_buffers.size(); i++)
        {
            m_pRenderer->beginRecordCommandBuffers(m_pRenderer->getGraphic().command_buffers[i], m_pRenderer->getGraphic().framebuffers[i], base_pipeline);
            m_pChunkManager->renderChunks(m_pRenderer->getGraphic().command_buffers[i], base_pipeline);
            m_pRenderer->endRecordCommandBuffers(m_pRenderer->getGraphic().command_buffers[i]);
        }

        m_pChunkManager->setRebuild(false);
    }

    m_pPlayer->updateUBO(static_cast<float>(m_config->width), static_cast<float>(m_config->height));

    void* data;
    vkMapMemory(m_pRenderer->getDevice(), m_pRenderer->getGraphic().uniform_memory, 0, sizeof(m_pPlayer->getUBO()), 0, &data);
    memcpy(data, &m_pPlayer->getUBO(), sizeof(m_pPlayer->getUBO()));
    vkUnmapMemory(m_pRenderer->getDevice(), m_pRenderer->getGraphic().uniform_memory);

    //std::this_thread::sleep_for(std::chrono::nanoseconds(500));//delete when not streaming
}

void Application::input()
{
    std::chrono::steady_clock::time_point currentTime = std::chrono::high_resolution_clock::now();
    float delta_time = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - m_last_time).count();

    m_pPlayer->update(delta_time);

    m_last_time = currentTime;
}

Renderer.h

#ifndef _RENDERER_H
#define _RENDERER_H

#ifndef _GLFW3_
#define _GLFW3_

#define GLFW_INCLUDE_VULKAN
#include <GLFW\glfw3.h>

#endif // !_GLFW3_


#include <iostream>
#include <memory>
#include <algorithm>

#include "Voxel.h"

#include "VulkanInstance.h"
#include "VulkanDevice.h"
#include "VulkanSwapchain.h"
#include "VulkanRenderPass.h"
#include "VulkanDescriptor.h"
#include "VulkanCommandPool.h"
#include "VulkanPipeline.h"
#include "VulkanBuffer.h"
#include "Graphics.h"

#include "Logger.h"

constexpr unsigned int MAX_FRAMES_IN_FLIGHT = 2;

class Renderer
{
public:
    Renderer(GLFWwindow& window, uint32_t width, uint32_t height);
    ~Renderer();

    int32_t draw(Pipeline& pipeline);

    void setupInstance(GLFWwindow& window);
    void setupDevice();
    void setupSwapchain();
    void setupRenderPass();
    void setupDescriptorSetLayout();
    void setupCommandPool();
    void createNewPipeline(Pipeline& pipeline);

    void recordCommandBuffers(Pipeline& pipeline, size_t indices, Buffer& buffer);

    VkDevice& getDevice();
    Graphics& getGraphic();//need to seperate object needed outside the class


    //rendering

    void createVerticesBuffer(std::shared_ptr<std::vector<Voxel::Block>> vertices, Buffer& buffer);
    void createIndicesBuffer(std::shared_ptr<std::vector<uint16_t>> indices, Buffer& buffer);
    void createUBO();

    void allocateCommandBuffers();
    void beginRecordCommandBuffers(VkCommandBuffer& commandBuffer, VkFramebuffer& frameBuffer, Pipeline& pipeline);
    void recordDrawCommands(VkCommandBuffer& commandBuffer, Pipeline& pipeline, Buffer& buffer, size_t indices);
    void endRecordCommandBuffers(VkCommandBuffer& commandBuffer);

    //!rendering
    void setupCallback();
    void createSurface(GLFWwindow& window);
    void createFramebuffer();
    void createCommandPool();
    void createSyncObject();
    void createDescriptorLayout();
    void createDescriptorPool();
    void createDescriptorSet();
    void createTextureImage();
    void createTextureImageView();
    void createTextureSampler();
    void createDepthResources();

    //cleaning
    void destroyTextures();
    void destroyDescriptors();
    void destroyUniformBuffer();
    void destroyBuffers(Buffer& buffers);
    void cleanSwapchain(std::shared_ptr<Pipeline> pPipeline);

private:
    //init fonctions
    void recreateSwapchain(Pipeline& pipeline);


    //helper fonctions
    bool checkDeviceExtensionSupport(VkPhysicalDevice device);
    bool checkDeviceSuitability(VkPhysicalDevice device);
    bool checkValidationLayerSupport();

    VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags);
    void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory);
    uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties);
    void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size);
    void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height);
    VkFormat findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features);
    VkFormat findDepthFormat();
    bool hasStencilComponent(VkFormat format);

    VkCommandBuffer beginCommands();
    void endCommands(VkCommandBuffer commandBuffer);
    void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout);

    VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
    VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR> availablePresentModes);
    VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities);
    SwapchainDetails querySwapChainSupport(VkPhysicalDevice device);
    QueueFamilyIndices findQueueFamily(VkPhysicalDevice device);
    std::vector<const char*> getRequiredExtensions();

    //static fonctions
    static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType,
        uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData);

    //proxy fonctions
    VkResult CreateDebugReportCallbackEXT(VkInstance& instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback);
    void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator);


    //attributes

    VkDebugReportCallbackEXT callback;
    Graphics m_graphic;
    std::unique_ptr<uint32_t> WIDTH;
    std::unique_ptr<uint32_t> HEIGHT;


    std::unique_ptr<VulkanInstance> m_instance;
    std::unique_ptr<VulkanDevice> m_device;
    std::unique_ptr<VulkanSwapchain> m_swapchain;
    std::unique_ptr<VulkanRenderPass> m_pRenderpass;
    std::unique_ptr<VulkanDescriptor> m_descriptor;
    std::unique_ptr<VulkanCommandPool> m_commandPool;
    std::unique_ptr<VulkanPipeline> m_pPipelineFactory;
    std::unique_ptr<VulkanBuffer> m_pBufferFactory;

    size_t m_frame_index = 0;
};

#endif _RENDERER_H

Renderer.cpp

#include "Renderer.h"


#include <set>

#ifndef STB_IMAGE_IMPLEMENTATION

#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>

#endif // !STB_IMAGE_IMPLEMENTATION

//review createBuffer func

Renderer::Renderer(GLFWwindow& window, uint32_t width, uint32_t height)
{
    WIDTH = std::make_unique<uint32_t>(width);
    HEIGHT = std::make_unique<uint32_t>(height);

    m_pPipelineFactory = std::make_unique<VulkanPipeline>(m_graphic);
    m_pBufferFactory = std::make_unique<VulkanBuffer>(m_graphic);

    setupInstance(window);
    setupDevice();
    setupSwapchain();
    setupRenderPass();
    setupDescriptorSetLayout();
    setupCommandPool();
}


Renderer::~Renderer()
{
    for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
    {
        vkDestroySemaphore(m_graphic.device, m_graphic.semaphores_image_available[i], nullptr);
        vkDestroySemaphore(m_graphic.device, m_graphic.semaphores_render_finished[i], nullptr);
        vkDestroyFence(m_graphic.device, m_graphic.fences_in_flight[i], nullptr);
    }

    vkDestroyCommandPool(m_graphic.device, m_graphic.command_pool, nullptr);

    vkDestroyDevice(m_graphic.device, nullptr);

    if (m_graphic.validation_layer_enable)
    {
        DestroyDebugReportCallbackEXT(m_graphic.instance, callback, nullptr);
    }

    vkDestroySurfaceKHR(m_graphic.instance, m_graphic.surface, nullptr);
    vkDestroyInstance(m_graphic.instance, nullptr);
}

int32_t Renderer::draw(Pipeline& pipeline)
{
    vkWaitForFences(m_graphic.device, 1, &m_graphic.fences_in_flight[m_frame_index], VK_TRUE, std::numeric_limits<uint64_t>::max());
    vkResetFences(m_graphic.device, 1, &m_graphic.fences_in_flight[m_frame_index]);

    VkResult result;
    if (m_graphic.validation_layer_enable)
    {
        vkQueueWaitIdle(m_graphic.present_queue);
    }

    uint32_t image_index = 0;
    result = vkAcquireNextImageKHR(m_graphic.device, m_graphic.swapchain, std::numeric_limits<uint64_t>::max(), m_graphic.semaphores_image_available[m_frame_index], VK_NULL_HANDLE, &image_index);

    if (result == VK_ERROR_OUT_OF_DATE_KHR)
    {
        recreateSwapchain(pipeline);
        return 1;
    }
    else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
    {
        throw std::runtime_error("failed to acquire swap chain image!");
    }


    VkSubmitInfo submit_info = {};
    submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;

    VkSemaphore waitSemaphores[] = { m_graphic.semaphores_image_available[m_frame_index] };

    VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
    submit_info.waitSemaphoreCount = 1;
    submit_info.pWaitSemaphores = waitSemaphores;
    submit_info.pWaitDstStageMask = waitStages;

    submit_info.commandBufferCount = 1;
    submit_info.pCommandBuffers = &m_graphic.command_buffers[image_index];

    VkSemaphore signalSemaphores[] = { m_graphic.semaphores_render_finished[m_frame_index] };

    submit_info.signalSemaphoreCount = 1;
    submit_info.pSignalSemaphores = signalSemaphores;

    result = vkQueueSubmit(m_graphic.graphics_queue, 1, &submit_info, m_graphic.fences_in_flight[m_frame_index]);

    if (result != VK_SUCCESS)
    {
        std::cerr << result;
        throw std::runtime_error("failed to submit draw command buffer!");
    }

    VkPresentInfoKHR present_info = {};
    present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;

    present_info.waitSemaphoreCount = 1;
    present_info.pWaitSemaphores = signalSemaphores;

    VkSwapchainKHR swapchains[] = { m_graphic.swapchain };
    present_info.swapchainCount = 1;
    present_info.pSwapchains = swapchains;
    present_info.pImageIndices = &image_index;
    present_info.pResults = nullptr;

    result = vkQueuePresentKHR(m_graphic.present_queue, &present_info);

    if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR)
    {
        recreateSwapchain(pipeline);
        return 1;
    }
    else if (result != VK_SUCCESS)
    {
        throw std::runtime_error("failed to present swap chain image!");
    }

    m_frame_index = (m_frame_index + 1) % MAX_FRAMES_IN_FLIGHT;
    return 0;
}

void Renderer::setupInstance(GLFWwindow& window)
{
    m_instance = std::make_unique<VulkanInstance>(m_graphic);
    setupCallback();
    createSurface(window);
}

void Renderer::setupDevice()
{
    m_device = std::make_unique<VulkanDevice>(m_graphic);
    createSyncObject();
}

void Renderer::setupSwapchain()
{
    m_swapchain = std::make_unique<VulkanSwapchain>(m_graphic, *WIDTH.get(), *HEIGHT.get());
}

void Renderer::setupRenderPass()
{
    VkFormat format = findSupportedFormat( { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
    m_pRenderpass = std::make_unique<VulkanRenderPass>(m_graphic, format);
}

void Renderer::setupDescriptorSetLayout()
{
    m_descriptor = std::make_unique<VulkanDescriptor>(m_graphic);
    m_descriptor->createDescriptorSetLayout();
}

void Renderer::setupCommandPool()
{
    m_commandPool = std::make_unique<VulkanCommandPool>(m_graphic);
}

void Renderer::createNewPipeline(Pipeline& pipeline)
{
    m_pPipelineFactory->createPipeline(pipeline);
}

VkDevice& Renderer::getDevice()
{
    return m_graphic.device;
}

Graphics & Renderer::getGraphic()
{
    return m_graphic;
}

void Renderer::destroyBuffers(Buffer & buffer)
{
    vkQueueWaitIdle(m_graphic.graphics_queue);

    for (uint32_t i = 0; i < m_graphic.command_buffers.size(); i++)
    {
        vkResetCommandBuffer(m_graphic.command_buffers[i], VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
    }

    vkDestroyBuffer(m_graphic.device, buffer.index, nullptr);
    vkFreeMemory(m_graphic.device, buffer.index_memory, nullptr);

    vkDestroyBuffer(m_graphic.device, buffer.vertex, nullptr);
    vkFreeMemory(m_graphic.device, buffer.vertex_memory, nullptr);
}

void Renderer::createVerticesBuffer(std::shared_ptr<std::vector<Voxel::Block>> vertices, Buffer& buffer)
{
    VkDeviceSize buffer_size = sizeof(vertices->at(0)) * vertices->size();

    VkBuffer staging_buffer;
    VkDeviceMemory staging_buffer_memory;
    m_pBufferFactory->createBuffer(staging_buffer, staging_buffer_memory, buffer_size, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);

    void* data;
    vkMapMemory(m_graphic.device, staging_buffer_memory, 0, buffer_size, 0, &data);
    memcpy(data, vertices->data(), (size_t)buffer_size);
    vkUnmapMemory(m_graphic.device, staging_buffer_memory);

    m_pBufferFactory->createBuffer(buffer.vertex, buffer.vertex_memory, buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
    copyBuffer(staging_buffer, buffer.vertex, buffer_size);

    vkDestroyBuffer(m_graphic.device, staging_buffer, nullptr);
    vkFreeMemory(m_graphic.device, staging_buffer_memory, nullptr);
}

void Renderer::createIndicesBuffer(std::shared_ptr<std::vector<uint16_t>> indices, Buffer& buffer)
{
    VkDeviceSize buffer_size = sizeof(indices->at(0)) * indices->size();

    VkBuffer staging_buffer;
    VkDeviceMemory staging_buffer_mem;
    m_pBufferFactory->createBuffer(staging_buffer, staging_buffer_mem, buffer_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);

    void* data;
    vkMapMemory(m_graphic.device, staging_buffer_mem, 0, buffer_size, 0, &data);
    memcpy(data, indices->data(), (size_t)buffer_size);
    vkUnmapMemory(m_graphic.device, staging_buffer_mem);

    m_pBufferFactory->createBuffer(buffer.index, buffer.index_memory, buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
    copyBuffer(staging_buffer, buffer.index, buffer_size);

    vkDestroyBuffer(m_graphic.device, staging_buffer, nullptr);
    vkFreeMemory(m_graphic.device, staging_buffer_mem, nullptr);
}

void Renderer::createUBO()
{
    VkDeviceSize buffer_size = sizeof(UniformBufferObject);
    m_pBufferFactory->createBuffer(m_graphic.uniform_buffer, m_graphic.uniform_memory, buffer_size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
}

void Renderer::allocateCommandBuffers()
{
    m_graphic.command_buffers.resize(m_graphic.framebuffers.size());

    VkCommandBufferAllocateInfo alloc_buffers_info = {};
    alloc_buffers_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
    alloc_buffers_info.commandPool = m_graphic.command_pool;
    alloc_buffers_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
    alloc_buffers_info.commandBufferCount = static_cast<uint32_t>(m_graphic.command_buffers.size());

    if (vkAllocateCommandBuffers(m_graphic.device, &alloc_buffers_info, m_graphic.command_buffers.data()) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to allocate command buffers!");
    }
}

void Renderer::beginRecordCommandBuffers(VkCommandBuffer & commandBuffer, VkFramebuffer& frameBuffer, Pipeline & pipeline)
{
    std::array<VkClearValue, 2> clear_values = {};
    clear_values[0].color = { 0.0f, 0.0f, 0.0f, 1.0f };
    clear_values[1].depthStencil = { 1.0f, 0 };

    VkCommandBufferBeginInfo begin_buffer_info = {};
    begin_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
    begin_buffer_info.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
    begin_buffer_info.pInheritanceInfo = nullptr;

    if (vkBeginCommandBuffer(commandBuffer, &begin_buffer_info) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to begin recording command buffer!");
    }

    VkRenderPassBeginInfo renderPassInfo = {};
    renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
    renderPassInfo.renderPass = m_graphic.render_pass;
    renderPassInfo.framebuffer = frameBuffer;

    renderPassInfo.renderArea.offset = { 0, 0 };
    renderPassInfo.renderArea.extent = m_graphic.swapchain_details.extent;

    renderPassInfo.clearValueCount = static_cast<uint32_t>(clear_values.size());
    renderPassInfo.pClearValues = clear_values.data();

    m_pRenderpass->beginRenderPass(commandBuffer, renderPassInfo);
    m_pPipelineFactory->bindPipeline(commandBuffer, pipeline);
}

void Renderer::recordDrawCommands(VkCommandBuffer & commandBuffer, Pipeline& pipeline, Buffer & buffer, size_t indices)
{
    VkBuffer vertexBuffers[] = { buffer.vertex };
    VkDeviceSize offsets[] = { 0 };

    vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
    vkCmdBindIndexBuffer(commandBuffer, buffer.index, 0, VK_INDEX_TYPE_UINT16);

    m_descriptor->bindDescriptorSet(commandBuffer, pipeline.layout, m_graphic.descriptor_set);

    vkCmdDrawIndexed(commandBuffer, static_cast<uint32_t>(indices), 1, 0, 0, 0);
}

void Renderer::endRecordCommandBuffers(VkCommandBuffer & commandBuffer)
{
    m_pRenderpass->endRenderPass(commandBuffer);

    if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to record command buffer!");
    }
}

void Renderer::setupCallback()
{
    if (!m_graphic.validation_layer_enable)
    {
        return;
    }

    VkDebugReportCallbackCreateInfoEXT create_info = {};
    create_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
    create_info.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
    create_info.pfnCallback = debugCallback;

    if (CreateDebugReportCallbackEXT(m_graphic.instance, &create_info, nullptr, &callback) != VK_SUCCESS)
    {
        throw std::runtime_error("Failed to setup callback");
    }
}

void Renderer::createSurface(GLFWwindow& window)
{
    if (glfwCreateWindowSurface(m_graphic.instance, &window, nullptr, &m_graphic.surface) != VK_SUCCESS)
    {
        throw std::runtime_error("Failed to create window surface!");
    }
}

void Renderer::createFramebuffer()
{
    m_graphic.framebuffers.resize(m_graphic.images_view.size());

    for (size_t i = 0; i < m_graphic.images_view.size(); i++)
    {
        std::array<VkImageView, 2> attachments = { m_graphic.images_view[i], m_graphic.depth_view };

        VkFramebufferCreateInfo framebufferInfo = {};
        framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
        framebufferInfo.renderPass = m_graphic.render_pass;
        framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
        framebufferInfo.pAttachments = attachments.data();
        framebufferInfo.width = m_graphic.swapchain_details.extent.width;
        framebufferInfo.height = m_graphic.swapchain_details.extent.height;
        framebufferInfo.layers = 1;

        if (vkCreateFramebuffer(m_graphic.device, &framebufferInfo, nullptr, &m_graphic.framebuffers[i]) != VK_SUCCESS)
        {
            throw std::runtime_error("failed to create framebuffer!");
        }
    }
}

void Renderer::createCommandPool()
{
    VkCommandPoolCreateInfo pool_info = {};
    pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
    pool_info.pNext = nullptr;
    pool_info.queueFamilyIndex = m_graphic.queue_indices.graphic_family;
    pool_info.flags = 0;

    if (vkCreateCommandPool(m_graphic.device, &pool_info, nullptr, &m_graphic.command_pool) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to create command pool!");
    }
}

void Renderer::createSyncObject()
{
    m_graphic.semaphores_image_available.resize(MAX_FRAMES_IN_FLIGHT);
    m_graphic.semaphores_render_finished.resize(MAX_FRAMES_IN_FLIGHT);
    m_graphic.fences_in_flight.resize(MAX_FRAMES_IN_FLIGHT);

    VkSemaphoreCreateInfo semaphore_info = {};
    semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
    semaphore_info.pNext = nullptr;
    semaphore_info.flags = 0;

    VkFenceCreateInfo fence_info = {};
    fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
    fence_info.pNext = nullptr;
    fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;

    for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
    {
        if (vkCreateSemaphore(m_graphic.device, &semaphore_info, nullptr, &m_graphic.semaphores_image_available[i]) != VK_SUCCESS ||
            vkCreateSemaphore(m_graphic.device, &semaphore_info, nullptr, &m_graphic.semaphores_render_finished[i]) != VK_SUCCESS ||
            vkCreateFence(m_graphic.device, &fence_info, nullptr, &m_graphic.fences_in_flight[i]) != VK_SUCCESS)
        {
            Logger::registerError("failed to create semaphores for a frame!");
            throw std::runtime_error("failed to create semaphores for a frame!");
        }
    }
}

void Renderer::createDescriptorLayout()
{
    VkDescriptorSetLayoutBinding uboLayoutBinding = {};
    uboLayoutBinding.binding = 0;
    uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
    uboLayoutBinding.descriptorCount = 1;
    uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
    uboLayoutBinding.pImmutableSamplers = nullptr;

    VkDescriptorSetLayoutBinding samplerLayoutBinding = {};
    samplerLayoutBinding.binding = 1;
    samplerLayoutBinding.descriptorCount = 1;
    samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
    samplerLayoutBinding.pImmutableSamplers = nullptr;
    samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;


    std::array<VkDescriptorSetLayoutBinding, 2> bindings = { uboLayoutBinding, samplerLayoutBinding };
    VkDescriptorSetLayoutCreateInfo layoutInfo = {};
    layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
    layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());;
    layoutInfo.pBindings = bindings.data();

    if (vkCreateDescriptorSetLayout(m_graphic.device, &layoutInfo, nullptr, &m_graphic.descriptor_set_layout) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to create descriptor set layout!");
    }
}

void Renderer::createDescriptorPool()
{
    std::array<VkDescriptorPoolSize, 2> poolSizes = {};
    poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
    poolSizes[0].descriptorCount = 1;
    poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
    poolSizes[1].descriptorCount = 1;

    VkDescriptorPoolCreateInfo poolInfo = {};
    poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
    poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
    poolInfo.pPoolSizes = poolSizes.data();
    poolInfo.maxSets = 1;

    if (vkCreateDescriptorPool(m_graphic.device, &poolInfo, nullptr, &m_graphic.descriptor_pool) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to create descriptor pool!");
    }
}

void Renderer::createDescriptorSet()
{
    VkDescriptorSetLayout layouts[] = { m_graphic.descriptor_set_layout };
    VkDescriptorSetAllocateInfo allocInfo = {};
    allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
    allocInfo.descriptorPool = m_graphic.descriptor_pool;
    allocInfo.descriptorSetCount = 1;
    allocInfo.pSetLayouts = layouts;

    if (vkAllocateDescriptorSets(m_graphic.device, &allocInfo, &m_graphic.descriptor_set) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to allocate descriptor set!");
    }

    VkDescriptorBufferInfo bufferInfo = {};
    bufferInfo.buffer = m_graphic.uniform_buffer;
    bufferInfo.offset = 0;
    bufferInfo.range = sizeof(UniformBufferObject);

    VkDescriptorImageInfo imageInfo = {};
    imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
    imageInfo.imageView = m_graphic.texture_view;
    imageInfo.sampler = m_graphic.texture_sampler;


    std::array<VkWriteDescriptorSet, 2> descriptorWrites = {};
    descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
    descriptorWrites[0].dstSet = m_graphic.descriptor_set;
    descriptorWrites[0].dstBinding = 0;
    descriptorWrites[0].dstArrayElement = 0;
    descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
    descriptorWrites[0].descriptorCount = 1;
    descriptorWrites[0].pBufferInfo = &bufferInfo;

    descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
    descriptorWrites[1].dstSet = m_graphic.descriptor_set;
    descriptorWrites[1].dstBinding = 1;
    descriptorWrites[1].dstArrayElement = 0;
    descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
    descriptorWrites[1].descriptorCount = 1;
    descriptorWrites[1].pImageInfo = &imageInfo;

    vkUpdateDescriptorSets(m_graphic.device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
}

void Renderer::createTextureImage()
{
    int tex_width, tex_height, tex_channel;
    stbi_uc* pixels = stbi_load("textures/texture.jpg", &tex_width, &tex_height, &tex_channel, STBI_rgb_alpha);
    VkDeviceSize imageSize = tex_width * tex_height * 4;

    if (!pixels)
    {
        throw std::runtime_error("failed to load texture image!");
    }

    VkBuffer stagingBuffer;
    VkDeviceMemory stagingBufferMemory;

    m_pBufferFactory->createBuffer(stagingBuffer, stagingBufferMemory, imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);

    void* data;
    vkMapMemory(m_graphic.device, stagingBufferMemory, 0, imageSize, 0, &data);
    memcpy(data, pixels, static_cast<size_t>(imageSize));
    vkUnmapMemory(m_graphic.device, stagingBufferMemory);

    stbi_image_free(pixels);

    createImage(tex_width, tex_height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, m_graphic.texture_image, m_graphic.texture_memory);

    transitionImageLayout(m_graphic.texture_image, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
    copyBufferToImage(stagingBuffer, m_graphic.texture_image, static_cast<uint32_t>(tex_width), static_cast<uint32_t>(tex_height));

    transitionImageLayout(m_graphic.texture_image, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);

    vkDestroyBuffer(m_graphic.device, stagingBuffer, nullptr);
    vkFreeMemory(m_graphic.device, stagingBufferMemory, nullptr);
}

void Renderer::createTextureImageView()
{
    m_graphic.texture_view = createImageView(m_graphic.texture_image, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT);
}

void Renderer::createTextureSampler()
{
    VkSamplerCreateInfo samplerInfo = {};
    samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
    samplerInfo.magFilter = VK_FILTER_LINEAR;
    samplerInfo.minFilter = VK_FILTER_LINEAR;

    samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;

    samplerInfo.anisotropyEnable = VK_TRUE;
    samplerInfo.maxAnisotropy = 16;

    samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
    samplerInfo.unnormalizedCoordinates = VK_FALSE;
    samplerInfo.compareEnable = VK_FALSE;
    samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;

    samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
    samplerInfo.mipLodBias = 0.0f;
    samplerInfo.minLod = 0.0f;
    samplerInfo.maxLod = 0.0f;

    if (vkCreateSampler(m_graphic.device, &samplerInfo, nullptr, &m_graphic.texture_sampler) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to create texture sampler!");
    }
}

void Renderer::createDepthResources()
{
    VkFormat depthFormat = findDepthFormat();

    createImage(m_graphic.swapchain_details.extent.width, m_graphic.swapchain_details.extent.height, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, m_graphic.depth_image, m_graphic.depth_memory);
    m_graphic.depth_view = createImageView(m_graphic.depth_image, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT);

    transitionImageLayout(m_graphic.depth_image, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
}

void Renderer::destroyTextures()
{
    vkDestroySampler(m_graphic.device, m_graphic.texture_sampler, nullptr);
    vkDestroyImageView(m_graphic.device, m_graphic.texture_view, nullptr);

    vkDestroyImage(m_graphic.device, m_graphic.texture_image, nullptr);
    vkFreeMemory(m_graphic.device, m_graphic.texture_memory, nullptr);
}

void Renderer::destroyDescriptors()
{
    vkDestroyDescriptorPool(m_graphic.device, m_graphic.descriptor_pool, nullptr);
    vkDestroyDescriptorSetLayout(m_graphic.device, m_graphic.descriptor_set_layout, nullptr);
}

void Renderer::destroyUniformBuffer()
{
    vkDestroyBuffer(m_graphic.device, m_graphic.uniform_buffer, nullptr);
    vkFreeMemory(m_graphic.device, m_graphic.uniform_memory, nullptr);
}

VkImageView Renderer::createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags)
{
    VkImageView imageView;

    VkImageViewCreateInfo viewInfo = {};
    viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
    viewInfo.image = image;
    viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
    viewInfo.format = format;
    viewInfo.subresourceRange.aspectMask = aspectFlags;
    viewInfo.subresourceRange.baseMipLevel = 0;
    viewInfo.subresourceRange.levelCount = 1;
    viewInfo.subresourceRange.baseArrayLayer = 0;
    viewInfo.subresourceRange.layerCount = 1;

    if (vkCreateImageView(m_graphic.device, &viewInfo, nullptr, &imageView) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to create texture image view!");
    }

    return imageView;
}

void Renderer::createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage & image, VkDeviceMemory & imageMemory)
{
    VkImageCreateInfo imageInfo = {};
    imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
    imageInfo.imageType = VK_IMAGE_TYPE_2D;
    imageInfo.extent.width = static_cast<uint32_t>(width);
    imageInfo.extent.height = static_cast<uint32_t>(height);
    imageInfo.extent.depth = 1;
    imageInfo.mipLevels = 1;
    imageInfo.arrayLayers = 1;

    imageInfo.format = format;
    imageInfo.tiling = tiling;
    imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    imageInfo.usage = usage;
    imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
    imageInfo.flags = 0;

    if (vkCreateImage(m_graphic.device, &imageInfo, nullptr, &image) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to create image!");
    }


    VkMemoryRequirements memRequirements;
    vkGetImageMemoryRequirements(m_graphic.device, image, &memRequirements);

    VkMemoryAllocateInfo allocInfo = {};
    allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    allocInfo.allocationSize = memRequirements.size;
    allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);

    if (vkAllocateMemory(m_graphic.device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to allocate image memory!");
    }

    vkBindImageMemory(m_graphic.device, image, imageMemory, 0);
}

void Renderer::recordCommandBuffers(Pipeline& pipeline, size_t indices, Buffer& buffer)
{
    std::array<VkClearValue, 2> clear_values = {};
    clear_values[0].color = { 0.0f, 0.0f, 0.0f, 1.0f };
    clear_values[1].depthStencil = { 1.0f, 0 };

    m_graphic.command_buffers.resize(m_graphic.framebuffers.size());

    VkCommandBufferAllocateInfo alloc_buffers_info = {};
    alloc_buffers_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
    alloc_buffers_info.commandPool = m_graphic.command_pool;
    alloc_buffers_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
    alloc_buffers_info.commandBufferCount = static_cast<uint32_t>(m_graphic.command_buffers.size());

    if (vkAllocateCommandBuffers(m_graphic.device, &alloc_buffers_info, m_graphic.command_buffers.data()) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to allocate command buffers!");
    }

    for (size_t i = 0; i < m_graphic.command_buffers.size(); i++)
    {
        VkCommandBufferBeginInfo begin_buffer_info = {};
        begin_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
        begin_buffer_info.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
        begin_buffer_info.pInheritanceInfo = nullptr; // Optional

        if (vkBeginCommandBuffer(m_graphic.command_buffers[i], &begin_buffer_info) != VK_SUCCESS)
        {
            throw std::runtime_error("failed to begin recording command buffer!");
        }

        VkRenderPassBeginInfo renderPassInfo = {};
        renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
        renderPassInfo.renderPass = m_graphic.render_pass;
        renderPassInfo.framebuffer = m_graphic.framebuffers[i];

        renderPassInfo.renderArea.offset = { 0, 0 };
        renderPassInfo.renderArea.extent = m_graphic.swapchain_details.extent;

        renderPassInfo.clearValueCount = static_cast<uint32_t>(clear_values.size());
        renderPassInfo.pClearValues = clear_values.data();

        //vkCmdBeginRenderPass(m_graphic.command_buffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
        m_pRenderpass->beginRenderPass(m_graphic.command_buffers[i], renderPassInfo);

        //vkCmdBindPipeline(m_graphic.command_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.handle);//to change
        m_pPipelineFactory->bindPipeline(m_graphic.command_buffers[i], pipeline);


        VkBuffer vertexBuffers[] = { buffer.vertex };
        VkDeviceSize offsets[] = { 0 };
        vkCmdBindVertexBuffers(m_graphic.command_buffers[i], 0, 1, vertexBuffers, offsets);

        vkCmdBindIndexBuffer(m_graphic.command_buffers[i], buffer.index, 0, VK_INDEX_TYPE_UINT16);

        //vkCmdBindDescriptorSets(m_graphic.command_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS,pipeline.layout, 0, 1, &m_graphic.descriptor_set, 0, nullptr);
        m_descriptor->bindDescriptorSet(m_graphic.command_buffers[i], pipeline.layout, m_graphic.descriptor_set);

        vkCmdDrawIndexed(m_graphic.command_buffers[i], static_cast<uint32_t>(indices), 1, 0, 0, 0);

        //vkCmdEndRenderPass(m_graphic.command_buffers[i]);
        m_pRenderpass->endRenderPass(m_graphic.command_buffers[i]);

        if (vkEndCommandBuffer(m_graphic.command_buffers[i]) != VK_SUCCESS)
        {
            throw std::runtime_error("failed to record command buffer!");
        }
    }
}

void Renderer::recreateSwapchain(Pipeline& pipeline)
{
    vkDeviceWaitIdle(m_graphic.device);
    cleanSwapchain(std::make_shared<Pipeline>(pipeline));


    m_swapchain->createSwapchain();

    VkFormat format = findSupportedFormat({ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
    m_pRenderpass->createRenderPass(format);

    createNewPipeline(pipeline);
    createDepthResources();
    createFramebuffer();
    allocateCommandBuffers();
}

void Renderer::cleanSwapchain(std::shared_ptr<Pipeline> pPipeline)
{
    vkDestroyImageView(m_graphic.device, m_graphic.depth_view, nullptr);
    vkDestroyImage(m_graphic.device, m_graphic.depth_image, nullptr);
    vkFreeMemory(m_graphic.device, m_graphic.depth_memory, nullptr);

    for (size_t i = 0; i < m_graphic.framebuffers.size(); i++)
    {
        vkDestroyFramebuffer(m_graphic.device, m_graphic.framebuffers[i], nullptr);
    }


    vkFreeCommandBuffers(m_graphic.device, m_graphic.command_pool, static_cast<uint32_t>(m_graphic.command_buffers.size()), m_graphic.command_buffers.data());

    vkDestroyPipeline(m_graphic.device, pPipeline->handle, nullptr);
    vkDestroyPipelineLayout(m_graphic.device, pPipeline->layout, nullptr);

    vkDestroyRenderPass(m_graphic.device, m_graphic.render_pass, nullptr);

    for (auto imageView : m_graphic.images_view)
    {
        vkDestroyImageView(m_graphic.device, imageView, nullptr);
    }

    vkDestroySwapchainKHR(m_graphic.device, m_graphic.swapchain, nullptr);
}

bool Renderer::checkDeviceExtensionSupport(VkPhysicalDevice device)
{
    uint32_t extension_count;
    vkEnumerateDeviceExtensionProperties(device, nullptr, &extension_count, nullptr);

    std::vector<VkExtensionProperties> availableExtensions(extension_count);
    vkEnumerateDeviceExtensionProperties(device, nullptr, &extension_count, availableExtensions.data());

    std::set<std::string> requiredExtensions(m_graphic.extensions.device_extensions.begin(), m_graphic.extensions.device_extensions.end());

    for (const auto& extension : availableExtensions)
    {
        requiredExtensions.erase(extension.extensionName);
    }

    return requiredExtensions.empty();
}

bool Renderer::checkDeviceSuitability(VkPhysicalDevice device)
{
    bool swapChainAdequate = false;

    VkPhysicalDeviceProperties deviceProperties;
    VkPhysicalDeviceFeatures deviceFeatures;
    vkGetPhysicalDeviceProperties(device, &deviceProperties);
    vkGetPhysicalDeviceFeatures(device, &deviceFeatures);

    if (!(deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && deviceFeatures.geometryShader))
    {
        return false;
    }

    QueueFamilyIndices indices = findQueueFamily(device);

    bool extensionsSupported = checkDeviceExtensionSupport(device);

    if (extensionsSupported)
    {
        SwapchainDetails swapChainSupport = querySwapChainSupport(device);
        swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
    }

    VkPhysicalDeviceFeatures supportedFeatures;
    vkGetPhysicalDeviceFeatures(device, &supportedFeatures);

    return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
}

bool Renderer::checkValidationLayerSupport()
{
    uint32_t layer_count = 0;
    vkEnumerateInstanceLayerProperties(&layer_count, nullptr);

    std::vector<VkLayerProperties> available_layers(layer_count);
    vkEnumerateInstanceLayerProperties(&layer_count, available_layers.data());

    /*std::cout << "available layer:" << std::endl;
    for (const auto& extension : available_layers)
    {
        std::cout << "\t" << extension.layerName << std::endl;
    }*/

    for (const char* layerName : m_graphic.extensions.validationLayers)
    {
        bool layerFound = false;

        for (const auto& layerProperties : available_layers)
        {
            if (strcmp(layerName, layerProperties.layerName) == 0)
            {
                layerFound = true;
                break;
            }
        }

        if (!layerFound)
        {
            return false;
        }
    }

    return true;
}

uint32_t Renderer::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
{
    VkPhysicalDeviceMemoryProperties memProperties;
    vkGetPhysicalDeviceMemoryProperties(m_graphic.physical_device, &memProperties);

    for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
    {
        if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
        {
            return i;
        }
    }

    throw std::runtime_error("failed to find suitable memory type!");
}

void Renderer::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)
{
    VkCommandBuffer commandBuffer = beginCommands();

    VkBufferCopy copyRegion = {};
    copyRegion.size = size;
    vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);

    endCommands(commandBuffer);
}

void Renderer::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height)
{
    VkCommandBuffer commandBuffer = beginCommands();

    VkBufferImageCopy region = {};
    region.bufferOffset = 0;
    region.bufferRowLength = 0;
    region.bufferImageHeight = 0;

    region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    region.imageSubresource.mipLevel = 0;
    region.imageSubresource.baseArrayLayer = 0;
    region.imageSubresource.layerCount = 1;

    region.imageOffset = { 0, 0, 0 };
    region.imageExtent = { width, height, 1 };

    vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);

    endCommands(commandBuffer);
}

VkFormat Renderer::findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features)
{
    for (VkFormat format : candidates)
    {
        VkFormatProperties props;
        vkGetPhysicalDeviceFormatProperties(m_graphic.physical_device, format, &props);

        if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features)
        {
            return format;
        }
        else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features)
        {
            return format;
        }
    }

    throw std::runtime_error("failed to find supported format!");
}

VkFormat Renderer::findDepthFormat()
{
    return findSupportedFormat(
        { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
        VK_IMAGE_TILING_OPTIMAL,
        VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
}

bool Renderer::hasStencilComponent(VkFormat format)
{
    return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
}

VkCommandBuffer Renderer::beginCommands()
{
    VkCommandBufferAllocateInfo allocInfo = {};
    allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
    allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
    allocInfo.commandPool = m_graphic.command_pool;
    allocInfo.commandBufferCount = 1;

    VkCommandBuffer commandBuffer;
    vkAllocateCommandBuffers(m_graphic.device, &allocInfo, &commandBuffer);

    VkCommandBufferBeginInfo beginInfo = {};
    beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
    beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;

    vkBeginCommandBuffer(commandBuffer, &beginInfo);

    return commandBuffer;
}

void Renderer::endCommands(VkCommandBuffer commandBuffer)
{
    vkEndCommandBuffer(commandBuffer);

    VkSubmitInfo submitInfo = {};
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &commandBuffer;

    vkQueueSubmit(m_graphic.graphics_queue, 1, &submitInfo, VK_NULL_HANDLE);
    vkQueueWaitIdle(m_graphic.graphics_queue);

    vkFreeCommandBuffers(m_graphic.device, m_graphic.command_pool, 1, &commandBuffer);
}

void Renderer::transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout)
{
    VkCommandBuffer commandBuffer = beginCommands();

    VkImageMemoryBarrier barrier = {};
    barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    barrier.oldLayout = oldLayout;
    barrier.newLayout = newLayout;

    barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;

    barrier.image = image;

    if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
    {
        barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;

        if (hasStencilComponent(format))
        {
            barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
        }
    }
    else 
    {
        barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    }

    barrier.subresourceRange.baseMipLevel = 0;
    barrier.subresourceRange.levelCount = 1;
    barrier.subresourceRange.baseArrayLayer = 0;
    barrier.subresourceRange.layerCount = 1;


    VkPipelineStageFlags sourceStage;
    VkPipelineStageFlags destinationStage;

    if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) 
    {
        barrier.srcAccessMask = 0;
        barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;

        sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
        destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
    }
    else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) 
    {
        barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
        barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;

        sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
        destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
    }
    else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
    {
        barrier.srcAccessMask = 0;
        barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;

        sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
        destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
    }
    else
    {
        throw std::invalid_argument("unsupported layout transition!");
    }

    vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);

    endCommands(commandBuffer);
}

VkSurfaceFormatKHR Renderer::chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats)
{
    if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED)
    {
        return { VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
    }

    for (const auto& availableFormat : availableFormats)
    {
        if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
        {
            return availableFormat;
        }
    }

    return availableFormats[0];
}

VkPresentModeKHR Renderer::chooseSwapPresentMode(const std::vector<VkPresentModeKHR> availablePresentModes)
{
    VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR;

    for (const auto& availablePresentMode : availablePresentModes)
    {
        if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR)
        {
            return availablePresentMode;
        }
        else if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR)
        {
            bestMode = availablePresentMode;
        }
    }

    return bestMode;
}

VkExtent2D Renderer::chooseSwapExtent(const VkSurfaceCapabilitiesKHR & capabilities)
{
    if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max())
    {
        return capabilities.currentExtent;
    }
    else
    {
        VkExtent2D actualExtent = { *WIDTH.get(), *HEIGHT.get() };

        actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width));
        actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height));

        return actualExtent;
    }
}

SwapchainDetails Renderer::querySwapChainSupport(VkPhysicalDevice device)
{
    SwapchainDetails details;

    vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, m_graphic.surface, &details.capabilities);


    uint32_t format_count = 0;
    vkGetPhysicalDeviceSurfaceFormatsKHR(device, m_graphic.surface, &format_count, nullptr);

    if (format_count != 0)
    {
        details.formats.resize(format_count);
        vkGetPhysicalDeviceSurfaceFormatsKHR(device, m_graphic.surface, &format_count, details.formats.data());
    }


    uint32_t present_mode_count;
    vkGetPhysicalDeviceSurfacePresentModesKHR(device, m_graphic.surface, &present_mode_count, nullptr);

    if (present_mode_count != 0)
    {
        details.presentModes.resize(present_mode_count);
        vkGetPhysicalDeviceSurfacePresentModesKHR(device, m_graphic.surface, &present_mode_count, details.presentModes.data());
    }

    return details;
}

QueueFamilyIndices Renderer::findQueueFamily(VkPhysicalDevice device)
{
    QueueFamilyIndices indices;

    uint32_t queue_family_count = 0;
    vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, nullptr);

    std::vector<VkQueueFamilyProperties> queue_families(queue_family_count);
    vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families.data());

    int i = 0;
    for (const auto& queueFamily : queue_families)
    {
        if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
        {
            indices.graphic_family = i;
        }

        VkBool32 present_support = false;
        vkGetPhysicalDeviceSurfaceSupportKHR(device, i, m_graphic.surface, &present_support);

        if (queueFamily.queueCount > 0 && present_support)
        {
            indices.present_family = i;
        }

        if (indices.isComplete())
        {
            break;
        }

        i++;
    }

    return indices;
}

std::vector<const char*> Renderer::getRequiredExtensions()
{
    uint32_t glfwExtensionCount = 0;
    const char** glfwExtensions;
    glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

    std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);

    if (m_graphic.validation_layer_enable)
    {
        extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
    }

    return extensions;
}

VKAPI_ATTR VkBool32 VKAPI_CALL Renderer::debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj,
    size_t location, int32_t code, const char* layerPrefix, const char* msg,void* userData)
{

    std::cerr << "\nvalidation layer: " << msg << std::endl;
    Logger::registerError("\nvalidation layer: " + std::string(msg));

    return VK_FALSE;
}

VkResult Renderer::CreateDebugReportCallbackEXT(VkInstance& instance, const VkDebugReportCallbackCreateInfoEXT * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDebugReportCallbackEXT * pCallback)
{
    auto func = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT");
    if (func != nullptr)
    {
        return func(instance, pCreateInfo, pAllocator, pCallback);
    }
    else
    {
        return VK_ERROR_EXTENSION_NOT_PRESENT;
    }
}

void Renderer::DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks * pAllocator)
{
    auto func = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT");
    if (func != nullptr)
    {
        func(instance, callback, pAllocator);
    }
}

Graphics.h

#ifndef _GRAPHICS_H
#define _GRAPHICS_H

#include "vulkan\vulkan.h"
#include <vector>


struct Extensions
{
    const std::vector<const char*> validationLayers = { "VK_LAYER_LUNARG_standard_validation", "VK_LAYER_LUNARG_monitor" };
    const std::vector<const char*> device_extensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
};

struct QueueFamilyIndices
{
    int32_t graphic_family = -1;
    int32_t present_family = -1;

    bool isComplete()
    {
        return graphic_family >= 0 && present_family >= 0;
    }
};

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

struct SwapchainImage
{
    VkFormat format;
    VkExtent2D extent;
};

struct Graphics
{
#ifdef NDEBUG
    const bool validation_layer_enable = false;
#else
    const bool validation_layer_enable = true;
#endif

    VkInstance instance = VK_NULL_HANDLE;
    VkPhysicalDevice physical_device = VK_NULL_HANDLE;
    VkDevice device = VK_NULL_HANDLE;
    VkSurfaceKHR surface = VK_NULL_HANDLE;

    Extensions extensions;
    QueueFamilyIndices queue_indices;

    VkQueue graphics_queue = VK_NULL_HANDLE;
    VkQueue present_queue = VK_NULL_HANDLE;

    SwapchainImage swapchain_details;
    VkSwapchainKHR swapchain = VK_NULL_HANDLE;
    std::vector<VkImage> swapchain_images;
    std::vector<VkImageView> images_view;

    std::vector<VkFramebuffer> framebuffers;

    VkCommandPool command_pool;
    std::vector<VkCommandBuffer> command_buffers;//replace VkCommandBuffer with CommandBuffer

    std::vector<VkSemaphore> semaphores_render_finished;
    std::vector<VkSemaphore> semaphores_image_available;
    std::vector<VkFence> fences_in_flight;

    VkRenderPass render_pass;

    VkBuffer uniform_buffer;
    VkDeviceMemory uniform_memory;

    VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE;
    VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
    VkDescriptorSet descriptor_set = VK_NULL_HANDLE;

    VkImage texture_image;
    VkImageView texture_view;
    VkSampler texture_sampler;
    VkDeviceMemory texture_memory;

    VkImage depth_image;
    VkDeviceMemory depth_memory;
    VkImageView depth_view;
};

struct Pipeline
{
    VkPipeline handle = VK_NULL_HANDLE;
    VkPipelineLayout layout = VK_NULL_HANDLE;
};

struct Buffer
{
    VkBuffer vertex = VK_NULL_HANDLE;
    VkDeviceMemory vertex_memory = VK_NULL_HANDLE;

    VkBuffer index = VK_NULL_HANDLE;
    VkDeviceMemory index_memory = VK_NULL_HANDLE;
};

struct CommandBuffers
{
    bool needUpdate = true;//base state
    VkCommandBuffer handle = VK_NULL_HANDLE;
};

#endif // !_GRAPHICS_H

Camera.h

#ifndef _CAMERA_H
#define _CAMERA_H


#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include <memory>
#include <bitset>
#include <iostream>

class Camera
{
public:
    Camera();
    ~Camera();

    void setInput(int32_t key, int32_t scancode, int32_t mods, int32_t action);
    void mouse(double xpos, double ypos);

    void updatePos();

    glm::vec3 getPosition();
    glm::vec3 getRotation();
    float& getPitch();
    float& getYaw();

    void setDeltaTime(float dt);

private:

    std::bitset<348> m_keyboard_press;

    glm::vec3 m_position;
    glm::vec3 m_rotation;

    float m_yaw;
    float m_pitch;

    glm::vec2 last_mouse_pos;

    float delta_time;
};

#endif // !_CAMERA_H

Camera.cpp

#include "Camera.h"



Camera::Camera()
{
    m_yaw = 0;
    m_pitch = 0;
    last_mouse_pos = glm::vec2(0, 0);
    delta_time = 0;

    m_keyboard_press = { false };

    m_position = { 0.0f, 0.0f, 0.0f };
    m_rotation = { 1.0f, 1.0f, 0.0f };
}


Camera::~Camera()
{
}

void Camera::setInput(int32_t key, int32_t scancode, int32_t mods, int32_t action)
{
    if (action == GLFW_PRESS)
    {
        m_keyboard_press.set(key, true);
    }
    else if(action == GLFW_RELEASE)
    {
        m_keyboard_press.set(key, false);
    }
}

void Camera::mouse(double xpos, double ypos)
{
    float sensibility = 0.005f;

    glm::vec2 mouse_delta = glm::vec2(xpos, ypos);

    m_yaw = mouse_delta.x * sensibility;
    m_pitch = mouse_delta.y * sensibility;
}

void Camera::updatePos()
{
    glm::vec3 change = { 0.0f, 0.0f, 0.0f };
    float speed = 10.0f;

    if (m_keyboard_press[GLFW_KEY_W] == true)
    {
        change.x -= -glm::cos(glm::radians(m_yaw)) * speed;//rotation still not working
        change.z -= -glm::sin(glm::radians(m_yaw)) * speed;
    }

    if (m_keyboard_press[GLFW_KEY_S] == true)
    {
        change.x += -glm::cos(glm::radians(m_rotation.y)) * speed;
        change.z += -glm::sin(glm::radians(m_rotation.y)) * speed;
    }

    if (m_keyboard_press[GLFW_KEY_D] == true)
    {
        change.x += -glm::cos(glm::radians(m_rotation.y + 90)) * speed;
        change.z += -glm::sin(glm::radians(m_rotation.y + 90)) * speed;
    }

    if (m_keyboard_press[GLFW_KEY_A] == true)
    {
        change.x -= -glm::cos(glm::radians(m_rotation.y + 90)) * speed;
        change.z -= -glm::sin(glm::radians(m_rotation.y + 90)) * speed;
    }

    if (m_keyboard_press[GLFW_KEY_LEFT_SHIFT] == true)
    {
        change.y += 1.0f * speed;
    }

    if (m_keyboard_press[GLFW_KEY_LEFT_CONTROL] == true)
    {
        change.y -= 1.0f * speed;
    }

    m_position += change * delta_time;
}

glm::vec3 Camera::getPosition()
{
    return m_position;
}

glm::vec3 Camera::getRotation()
{
    return m_rotation;
}

float & Camera::getPitch()
{
    return m_pitch;
}

float & Camera::getYaw()
{
    return m_yaw;
}

void Camera::setDeltaTime(float dt)
{
    delta_time = dt;
}

VulkanBuffer.h

#ifndef _VULKANBUFFER_H
#define _VULKANBUFFER_H

#include <memory>

#include "Graphics.h"
#include "Voxel.h"

class VulkanBuffer
{
public:
    VulkanBuffer(Graphics & graphic);
    ~VulkanBuffer();

    void createBuffer(VkBuffer & buffer, VkDeviceMemory & bufferMemory, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties);

private:

    uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties);

    Graphics & m_graphic;
};

#endif // !_VULKANBUFFER_H

VulkanBuffer.cpp

#include "VulkanBuffer.h"



VulkanBuffer::VulkanBuffer(Graphics& graphic) : m_graphic(graphic)
{
}


VulkanBuffer::~VulkanBuffer()
{
}

void VulkanBuffer::createBuffer(VkBuffer & buffer, VkDeviceMemory & bufferMemory, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties)
{
    VkResult result;

    VkBufferCreateInfo bufferInfo = {};
    bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
    bufferInfo.size = size;
    bufferInfo.usage = usage;
    bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

    result = vkCreateBuffer(m_graphic.device, &bufferInfo, nullptr, &buffer);
    switch (result)
    {
    case VK_SUCCESS:
        break;
    case VK_ERROR_OUT_OF_HOST_MEMORY:
        throw std::runtime_error("Failed to create buffer!");
        break;
    case VK_ERROR_OUT_OF_DEVICE_MEMORY:
        throw std::runtime_error("Failed to create buffer!");
        break;
    default:
        throw std::runtime_error("Failed to create buffer!");
        break;
    }

    VkMemoryRequirements memRequirements;
    vkGetBufferMemoryRequirements(m_graphic.device, buffer, &memRequirements);

    VkMemoryAllocateInfo allocInfo = {};
    allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    allocInfo.allocationSize = memRequirements.size;
    allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);

    result = vkAllocateMemory(m_graphic.device, &allocInfo, nullptr, &bufferMemory);
    switch (result)
    {
    case VK_SUCCESS:
        break;
    case VK_ERROR_OUT_OF_HOST_MEMORY:
        throw std::runtime_error("Failed to allocate buffer memory!");
        break;
    case VK_ERROR_OUT_OF_DEVICE_MEMORY:
        throw std::runtime_error("Failed to allocate buffer memory!");
        break;
    case VK_ERROR_TOO_MANY_OBJECTS:
        throw std::runtime_error("Failed to allocate buffer memory!");
        break;
    case VK_ERROR_INVALID_EXTERNAL_HANDLE:
        throw std::runtime_error("Failed to allocate buffer memory!");
        break;

    default:
        throw std::runtime_error("Failed to allocate buffer memory!");
        break;
    }


    result = vkBindBufferMemory(m_graphic.device, buffer, bufferMemory, 0);
    switch (result)
    {
    case VK_SUCCESS:
        break;
    case VK_ERROR_OUT_OF_HOST_MEMORY:
        throw std::runtime_error("Failed to bind buffer!");
        break;
    case VK_ERROR_OUT_OF_DEVICE_MEMORY:
        throw std::runtime_error("Failed to bind buffer!");
        break;
    default:
        throw std::runtime_error("Failed to bind buffer!");
        break;
    }
}

uint32_t VulkanBuffer::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
{
    VkPhysicalDeviceMemoryProperties memProperties;
    vkGetPhysicalDeviceMemoryProperties(m_graphic.physical_device, &memProperties);

    for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
    {
        if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
        {
            return i;
        }
    }

    throw std::runtime_error("failed to find suitable memory type!");
}

Window.h

#pragma once

#ifndef _GLFW3_
#define _GLFW3_

#define GLFW_INCLUDE_VULKAN
#include <GLFW\glfw3.h>

#endif // !_GLFW3_

#include <memory>

#include "Camera.h"
#include "Config.h"

class Window
{
public:
    Window(std::unique_ptr<Config>& config, Camera& camera);
    ~Window();

    static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
    static void mouse_callback(GLFWwindow* window, double xpos, double ypos);

    GLFWwindow& getHandle();
    //Camera& getCamera();

private:

    GLFWwindow * m_handle;
};

Window.cpp

#include "Window.h"



Window::Window(std::unique_ptr<Config>& config, Camera& camera)
{
    glfwInit();

    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
    glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);

    m_handle = glfwCreateWindow(config->width, config->height, "Vulkan", nullptr, nullptr);

    glfwSetInputMode(m_handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    glfwSetWindowUserPointer(m_handle, &camera);
    glfwSetKeyCallback(m_handle, key_callback);
    glfwSetCursorPosCallback(m_handle, mouse_callback);
}


Window::~Window()
{
    glfwDestroyWindow(m_handle);
    glfwTerminate();
}

void Window::key_callback(GLFWwindow * window, int key, int scancode, int action, int mods)
{
    Camera* pCamera = static_cast<Camera*>(glfwGetWindowUserPointer(window));

    pCamera->setInput(static_cast<int32_t>(key), static_cast<int32_t>(scancode), static_cast<int32_t>(mods), static_cast<int32_t>(action));

    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
    {
        glfwSetWindowShouldClose(window, GLFW_TRUE);
    }
}

void Window::mouse_callback(GLFWwindow * window, double xpos, double ypos)
{
    Camera* pCamera = static_cast<Camera*>(glfwGetWindowUserPointer(window));

    pCamera->mouse(xpos, ypos);
}

GLFWwindow & Window::getHandle()
{
    return *m_handle;
}

If you need anything else, please ask.

\$\endgroup\$
  • \$\begingroup\$ Code to be reviewed must be in the body of the question. \$\endgroup\$ – Toby Speight Feb 7 at 16:09
  • \$\begingroup\$ @TobySpeight So I post the code full engine in the body? \$\endgroup\$ – MrScriptX Feb 7 at 16:12
  • 3
    \$\begingroup\$ @MrScriptX at least the complete, working part you want to get reviewed. After all, your code in your GitHub repository may change at any time. Due to StackExchange Q&A style, any answer should be valid throughout the question's life, and the question+answer should be self contained. A link to a repository unfortunately defeats both purposes: your code may change (and invalidate reviews), and visitors to this site cannot benefit from the review, as the code is missing. Keep in mind that this will put your code under CC-BY-SA, though. \$\endgroup\$ – Zeta Feb 7 at 16:21
  • \$\begingroup\$ @Incomputable Hm. The headers are missing. \$\endgroup\$ – Zeta Feb 7 at 17:55
  • \$\begingroup\$ @Zeta, would be great to implement some sort of tool that would automatically generate the post given a repository. I guess it is possible with boost.asio and std.filesystem? \$\endgroup\$ – Incomputable Feb 7 at 17:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.