init
This commit is contained in:
		
						commit
						dad6ce7a21
					
				| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
{
 | 
			
		||||
    "configurations": [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Linux",
 | 
			
		||||
            "includePath": [
 | 
			
		||||
                "${workspaceFolder}/**"
 | 
			
		||||
            ],
 | 
			
		||||
            "defines": [],
 | 
			
		||||
            "compilerPath": "/usr/bin/gcc",
 | 
			
		||||
            "cStandard": "c17",
 | 
			
		||||
            "cppStandard": "gnu++17",
 | 
			
		||||
            "intelliSenseMode": "linux-gcc-x64",
 | 
			
		||||
            "configurationProvider": "ms-vscode.makefile-tools"
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "version": 4
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,53 @@
 | 
			
		|||
{
 | 
			
		||||
    "files.associations": {
 | 
			
		||||
        "array": "cpp",
 | 
			
		||||
        "atomic": "cpp",
 | 
			
		||||
        "bit": "cpp",
 | 
			
		||||
        "*.tcc": "cpp",
 | 
			
		||||
        "cctype": "cpp",
 | 
			
		||||
        "clocale": "cpp",
 | 
			
		||||
        "cmath": "cpp",
 | 
			
		||||
        "compare": "cpp",
 | 
			
		||||
        "concepts": "cpp",
 | 
			
		||||
        "cstdarg": "cpp",
 | 
			
		||||
        "cstddef": "cpp",
 | 
			
		||||
        "cstdint": "cpp",
 | 
			
		||||
        "cstdio": "cpp",
 | 
			
		||||
        "cstdlib": "cpp",
 | 
			
		||||
        "cstring": "cpp",
 | 
			
		||||
        "cwchar": "cpp",
 | 
			
		||||
        "cwctype": "cpp",
 | 
			
		||||
        "deque": "cpp",
 | 
			
		||||
        "string": "cpp",
 | 
			
		||||
        "unordered_map": "cpp",
 | 
			
		||||
        "vector": "cpp",
 | 
			
		||||
        "exception": "cpp",
 | 
			
		||||
        "algorithm": "cpp",
 | 
			
		||||
        "functional": "cpp",
 | 
			
		||||
        "iterator": "cpp",
 | 
			
		||||
        "memory": "cpp",
 | 
			
		||||
        "memory_resource": "cpp",
 | 
			
		||||
        "numeric": "cpp",
 | 
			
		||||
        "random": "cpp",
 | 
			
		||||
        "string_view": "cpp",
 | 
			
		||||
        "system_error": "cpp",
 | 
			
		||||
        "tuple": "cpp",
 | 
			
		||||
        "type_traits": "cpp",
 | 
			
		||||
        "utility": "cpp",
 | 
			
		||||
        "initializer_list": "cpp",
 | 
			
		||||
        "iosfwd": "cpp",
 | 
			
		||||
        "iostream": "cpp",
 | 
			
		||||
        "istream": "cpp",
 | 
			
		||||
        "limits": "cpp",
 | 
			
		||||
        "new": "cpp",
 | 
			
		||||
        "numbers": "cpp",
 | 
			
		||||
        "ostream": "cpp",
 | 
			
		||||
        "stdexcept": "cpp",
 | 
			
		||||
        "streambuf": "cpp",
 | 
			
		||||
        "cinttypes": "cpp",
 | 
			
		||||
        "typeinfo": "cpp",
 | 
			
		||||
        "fstream": "cpp",
 | 
			
		||||
        "set": "cpp",
 | 
			
		||||
        "unordered_set": "cpp"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
CFLAGS = -std=c++17 -o2 -I. -I$(VULKAN_SDK_PATH)/include
 | 
			
		||||
LDFLAGS = -L$(VULKAN_SDK_PATH)/lib `pkg-config --static --libs glfw3` -lvulkan -ldl -lpthread -lX11 -lXrandr -lXi
 | 
			
		||||
 | 
			
		||||
# create list of all spv files and set as dependency
 | 
			
		||||
vertSources = $(shell find ./shaders -type f -name "*.vert")
 | 
			
		||||
vertSpvs = $(patsubst %.vert, %.vert.spv, $(vertSources))
 | 
			
		||||
fragSources = $(shell find ./shaders -type f -name "*.frag")
 | 
			
		||||
fragSpvs = $(patsubst %.frag, %.frag.spv, $(fragSources))
 | 
			
		||||
 | 
			
		||||
TARGET = VulkanTest
 | 
			
		||||
$(TARGET): $(vertSpvs) $(fragSpvs)
 | 
			
		||||
$(TARGET): *.cpp *.hpp
 | 
			
		||||
	g++ $(CFLAGS) -o $(TARGET) *.cpp $(LDFLAGS)
 | 
			
		||||
 | 
			
		||||
# make shader targets
 | 
			
		||||
%.spv: %
 | 
			
		||||
	${GLSLC} $< -o $@
 | 
			
		||||
 | 
			
		||||
.PHONY: test clean
 | 
			
		||||
 | 
			
		||||
test: VulkanTest
 | 
			
		||||
	./VulkanTest
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -f VulkanTest
 | 
			
		||||
	rm -f *.spv
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
/usr/local/bin/glslc shaders/simple_shader.vert -o shaders/simple_shader.vert.spv
 | 
			
		||||
/usr/local/bin/glslc shaders/simple_shader.frag -o shaders/simple_shader.frag.spv
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,140 @@
 | 
			
		|||
#include "first_app.hpp"
 | 
			
		||||
 | 
			
		||||
// std
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <array>
 | 
			
		||||
 | 
			
		||||
namespace hk
 | 
			
		||||
{
 | 
			
		||||
    FirstApp::FirstApp()
 | 
			
		||||
    {
 | 
			
		||||
        loadModels();
 | 
			
		||||
        createPipelineLayout();
 | 
			
		||||
        createPipeline();
 | 
			
		||||
        createCommandBuffers();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FirstApp::~FirstApp()
 | 
			
		||||
    {
 | 
			
		||||
        vkDestroyPipelineLayout(device.device(), pipelineLayout, nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void FirstApp::run()
 | 
			
		||||
    {
 | 
			
		||||
        while (!window.shouldClose())
 | 
			
		||||
        {
 | 
			
		||||
            glfwPollEvents();
 | 
			
		||||
            drawFrame();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        vkDeviceWaitIdle(device.device());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void FirstApp::loadModels()
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<Model::Vertex> vertices{
 | 
			
		||||
            {{0.0f, -0.5f}, {1.0f, 0.0f, 0.0f}},
 | 
			
		||||
            {{0.5f, 0.5f}, {0.0f, 1.0f, 0.0f}},
 | 
			
		||||
            {{-0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}}
 | 
			
		||||
        };
 | 
			
		||||
        model = std::make_unique<Model>(device, vertices);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void FirstApp::createPipelineLayout()
 | 
			
		||||
    {
 | 
			
		||||
        VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
 | 
			
		||||
        pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
 | 
			
		||||
        pipelineLayoutInfo.setLayoutCount = 0;
 | 
			
		||||
        pipelineLayoutInfo.pSetLayouts = nullptr;
 | 
			
		||||
        pipelineLayoutInfo.pushConstantRangeCount = 0;
 | 
			
		||||
        pipelineLayoutInfo.pPushConstantRanges = nullptr;
 | 
			
		||||
 | 
			
		||||
        if (vkCreatePipelineLayout(device.device(), &pipelineLayoutInfo, nullptr, &pipelineLayout) !=
 | 
			
		||||
            VK_SUCCESS)
 | 
			
		||||
        {
 | 
			
		||||
            throw std::runtime_error("failed to create pipeline layout!");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void FirstApp::createPipeline()
 | 
			
		||||
    {
 | 
			
		||||
        auto pipelineConfig =
 | 
			
		||||
            Pipeline::defaultPipelineConfigInfo(swapChain.width(), swapChain.height());
 | 
			
		||||
 | 
			
		||||
        pipelineConfig.renderPass = swapChain.getRenderPass();
 | 
			
		||||
        pipelineConfig.pipelineLayout = pipelineLayout;
 | 
			
		||||
        pipeline = std::make_unique<Pipeline>(
 | 
			
		||||
            device,
 | 
			
		||||
            "shaders/simple_shader.vert.spv",
 | 
			
		||||
            "shaders/simple_shader.frag.spv",
 | 
			
		||||
            pipelineConfig);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void FirstApp::createCommandBuffers()
 | 
			
		||||
    {
 | 
			
		||||
        commandBuffers.resize(swapChain.imageCount());
 | 
			
		||||
 | 
			
		||||
        VkCommandBufferAllocateInfo allocInfo{};
 | 
			
		||||
        allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
 | 
			
		||||
        allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
 | 
			
		||||
        allocInfo.commandPool = device.getCommandPool();
 | 
			
		||||
        allocInfo.commandBufferCount = static_cast<uint32_t>(commandBuffers.size());
 | 
			
		||||
 | 
			
		||||
        if (vkAllocateCommandBuffers(device.device(), &allocInfo, commandBuffers.data()) !=
 | 
			
		||||
            VK_SUCCESS)
 | 
			
		||||
        {
 | 
			
		||||
            throw std::runtime_error("failed to allocate command buffers!");
 | 
			
		||||
        }
 | 
			
		||||
        for (int i = 0; i < commandBuffers.size(); i++)
 | 
			
		||||
        {
 | 
			
		||||
            VkCommandBufferBeginInfo beginInfo{};
 | 
			
		||||
            beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
 | 
			
		||||
            if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != 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 = swapChain.getRenderPass();
 | 
			
		||||
            renderPassInfo.framebuffer = swapChain.getFrameBuffer(i);
 | 
			
		||||
 | 
			
		||||
            renderPassInfo.renderArea.offset = {0, 0};
 | 
			
		||||
            renderPassInfo.renderArea.extent = swapChain.getSwapChainExtent();
 | 
			
		||||
 | 
			
		||||
            std::array<VkClearValue, 2> clearValues{};
 | 
			
		||||
            clearValues[0].color = {0.1f, 0.1f, 0.1f, 1.0f};
 | 
			
		||||
            clearValues[1].depthStencil = {1.0f, 0};
 | 
			
		||||
            renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
 | 
			
		||||
            renderPassInfo.pClearValues = clearValues.data();
 | 
			
		||||
 | 
			
		||||
            vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
 | 
			
		||||
 | 
			
		||||
            pipeline->bind(commandBuffers[i]);
 | 
			
		||||
            model->bind(commandBuffers[i]);
 | 
			
		||||
            model->draw(commandBuffers[i]);
 | 
			
		||||
 | 
			
		||||
            vkCmdEndRenderPass(commandBuffers[i]);
 | 
			
		||||
            if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS)
 | 
			
		||||
            {
 | 
			
		||||
                throw std::runtime_error("failed to record command buffer!");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void FirstApp::drawFrame()
 | 
			
		||||
    {
 | 
			
		||||
        uint32_t imageIndex;
 | 
			
		||||
        auto result = swapChain.acquireNextImage(&imageIndex);
 | 
			
		||||
        if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
 | 
			
		||||
        {
 | 
			
		||||
            throw std::runtime_error("failed to acquire swap chain image!");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        result = swapChain.submitCommandBuffers(&commandBuffers[imageIndex], &imageIndex);
 | 
			
		||||
        if (result != VK_SUCCESS)
 | 
			
		||||
        {
 | 
			
		||||
            throw std::runtime_error("failed to present swap chain image!");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "hk_device.hpp"
 | 
			
		||||
#include "hk_pipeline.hpp"
 | 
			
		||||
#include "hk_swap_chain.hpp"
 | 
			
		||||
#include "hk_window.hpp"
 | 
			
		||||
#include "hk_model.hpp"
 | 
			
		||||
 | 
			
		||||
// std
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace hk
 | 
			
		||||
{
 | 
			
		||||
    class FirstApp
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        static constexpr int WIDTH = 800;
 | 
			
		||||
        static constexpr int HEIGHT = 600;
 | 
			
		||||
 | 
			
		||||
        FirstApp();
 | 
			
		||||
        ~FirstApp();
 | 
			
		||||
 | 
			
		||||
        FirstApp(const FirstApp &) = delete;
 | 
			
		||||
        FirstApp &operator=(const FirstApp &) = delete;
 | 
			
		||||
 | 
			
		||||
        void run();
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        void loadModels();
 | 
			
		||||
        void createPipelineLayout();
 | 
			
		||||
        void createPipeline();
 | 
			
		||||
        void createCommandBuffers();
 | 
			
		||||
        void drawFrame();
 | 
			
		||||
 | 
			
		||||
        Window window{WIDTH, HEIGHT, "Hello Vulkan!"};
 | 
			
		||||
        Device device{window};
 | 
			
		||||
        SwapChain swapChain{device, window.getExtend()};
 | 
			
		||||
        std::unique_ptr<Pipeline> pipeline;
 | 
			
		||||
        VkPipelineLayout pipelineLayout;
 | 
			
		||||
        std::vector<VkCommandBuffer> commandBuffers;
 | 
			
		||||
        std::unique_ptr<Model> model;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,610 @@
 | 
			
		|||
#include "hk_device.hpp"
 | 
			
		||||
 | 
			
		||||
// std headers
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <unordered_set>
 | 
			
		||||
 | 
			
		||||
namespace hk
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  // local callback functions
 | 
			
		||||
  static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
 | 
			
		||||
      VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
 | 
			
		||||
      VkDebugUtilsMessageTypeFlagsEXT messageType,
 | 
			
		||||
      const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
 | 
			
		||||
      void *pUserData)
 | 
			
		||||
  {
 | 
			
		||||
    std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl;
 | 
			
		||||
 | 
			
		||||
    return VK_FALSE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  VkResult CreateDebugUtilsMessengerEXT(
 | 
			
		||||
      VkInstance instance,
 | 
			
		||||
      const VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo,
 | 
			
		||||
      const VkAllocationCallbacks *pAllocator,
 | 
			
		||||
      VkDebugUtilsMessengerEXT *pDebugMessenger)
 | 
			
		||||
  {
 | 
			
		||||
    auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(
 | 
			
		||||
        instance,
 | 
			
		||||
        "vkCreateDebugUtilsMessengerEXT");
 | 
			
		||||
    if (func != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
      return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
      return VK_ERROR_EXTENSION_NOT_PRESENT;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void DestroyDebugUtilsMessengerEXT(
 | 
			
		||||
      VkInstance instance,
 | 
			
		||||
      VkDebugUtilsMessengerEXT debugMessenger,
 | 
			
		||||
      const VkAllocationCallbacks *pAllocator)
 | 
			
		||||
  {
 | 
			
		||||
    auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(
 | 
			
		||||
        instance,
 | 
			
		||||
        "vkDestroyDebugUtilsMessengerEXT");
 | 
			
		||||
    if (func != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
      func(instance, debugMessenger, pAllocator);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // class member functions
 | 
			
		||||
  Device::Device(Window &window) : window{window}
 | 
			
		||||
  {
 | 
			
		||||
    createInstance();
 | 
			
		||||
    setupDebugMessenger();
 | 
			
		||||
    createSurface();
 | 
			
		||||
    pickPhysicalDevice();
 | 
			
		||||
    createLogicalDevice();
 | 
			
		||||
    createCommandPool();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Device::~Device()
 | 
			
		||||
  {
 | 
			
		||||
    vkDestroyCommandPool(device_, commandPool, nullptr);
 | 
			
		||||
    vkDestroyDevice(device_, nullptr);
 | 
			
		||||
 | 
			
		||||
    if (enableValidationLayers)
 | 
			
		||||
    {
 | 
			
		||||
      DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vkDestroySurfaceKHR(instance, surface_, nullptr);
 | 
			
		||||
    vkDestroyInstance(instance, nullptr);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void Device::createInstance()
 | 
			
		||||
  {
 | 
			
		||||
    if (enableValidationLayers && !checkValidationLayerSupport())
 | 
			
		||||
    {
 | 
			
		||||
      throw std::runtime_error("validation layers requested, but not available!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VkApplicationInfo appInfo = {};
 | 
			
		||||
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
 | 
			
		||||
    appInfo.pApplicationName = "LittleVulkanEngine App";
 | 
			
		||||
    appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
 | 
			
		||||
    appInfo.pEngineName = "No Engine";
 | 
			
		||||
    appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
 | 
			
		||||
    appInfo.apiVersion = VK_API_VERSION_1_0;
 | 
			
		||||
 | 
			
		||||
    VkInstanceCreateInfo createInfo = {};
 | 
			
		||||
    createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
 | 
			
		||||
    createInfo.pApplicationInfo = &appInfo;
 | 
			
		||||
 | 
			
		||||
    auto extensions = getRequiredExtensions();
 | 
			
		||||
    createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
 | 
			
		||||
    createInfo.ppEnabledExtensionNames = extensions.data();
 | 
			
		||||
 | 
			
		||||
    VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
 | 
			
		||||
    if (enableValidationLayers)
 | 
			
		||||
    {
 | 
			
		||||
      createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
 | 
			
		||||
      createInfo.ppEnabledLayerNames = validationLayers.data();
 | 
			
		||||
 | 
			
		||||
      populateDebugMessengerCreateInfo(debugCreateInfo);
 | 
			
		||||
      createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT *)&debugCreateInfo;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
      createInfo.enabledLayerCount = 0;
 | 
			
		||||
      createInfo.pNext = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS)
 | 
			
		||||
    {
 | 
			
		||||
      throw std::runtime_error("failed to create instance!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hasGflwRequiredInstanceExtensions();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void Device::pickPhysicalDevice()
 | 
			
		||||
  {
 | 
			
		||||
    uint32_t deviceCount = 0;
 | 
			
		||||
    vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
 | 
			
		||||
    if (deviceCount == 0)
 | 
			
		||||
    {
 | 
			
		||||
      throw std::runtime_error("failed to find GPUs with Vulkan support!");
 | 
			
		||||
    }
 | 
			
		||||
    std::cout << "Device count: " << deviceCount << std::endl;
 | 
			
		||||
    std::vector<VkPhysicalDevice> devices(deviceCount);
 | 
			
		||||
    vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
 | 
			
		||||
 | 
			
		||||
    for (const auto &device : devices)
 | 
			
		||||
    {
 | 
			
		||||
      if (isDeviceSuitable(device))
 | 
			
		||||
      {
 | 
			
		||||
        physicalDevice = device;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (physicalDevice == VK_NULL_HANDLE)
 | 
			
		||||
    {
 | 
			
		||||
      throw std::runtime_error("failed to find a suitable GPU!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vkGetPhysicalDeviceProperties(physicalDevice, &properties);
 | 
			
		||||
    std::cout << "physical device: " << properties.deviceName << std::endl;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void Device::createLogicalDevice()
 | 
			
		||||
  {
 | 
			
		||||
    QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
 | 
			
		||||
 | 
			
		||||
    std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
 | 
			
		||||
    std::set<uint32_t> uniqueQueueFamilies = {indices.graphicsFamily, indices.presentFamily};
 | 
			
		||||
 | 
			
		||||
    float queuePriority = 1.0f;
 | 
			
		||||
    for (uint32_t queueFamily : uniqueQueueFamilies)
 | 
			
		||||
    {
 | 
			
		||||
      VkDeviceQueueCreateInfo queueCreateInfo = {};
 | 
			
		||||
      queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
 | 
			
		||||
      queueCreateInfo.queueFamilyIndex = queueFamily;
 | 
			
		||||
      queueCreateInfo.queueCount = 1;
 | 
			
		||||
      queueCreateInfo.pQueuePriorities = &queuePriority;
 | 
			
		||||
      queueCreateInfos.push_back(queueCreateInfo);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VkPhysicalDeviceFeatures deviceFeatures = {};
 | 
			
		||||
    deviceFeatures.samplerAnisotropy = VK_TRUE;
 | 
			
		||||
 | 
			
		||||
    VkDeviceCreateInfo createInfo = {};
 | 
			
		||||
    createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
 | 
			
		||||
 | 
			
		||||
    createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
 | 
			
		||||
    createInfo.pQueueCreateInfos = queueCreateInfos.data();
 | 
			
		||||
 | 
			
		||||
    createInfo.pEnabledFeatures = &deviceFeatures;
 | 
			
		||||
    createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
 | 
			
		||||
    createInfo.ppEnabledExtensionNames = deviceExtensions.data();
 | 
			
		||||
 | 
			
		||||
    // might not really be necessary anymore because device specific validation layers
 | 
			
		||||
    // have been deprecated
 | 
			
		||||
    if (enableValidationLayers)
 | 
			
		||||
    {
 | 
			
		||||
      createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
 | 
			
		||||
      createInfo.ppEnabledLayerNames = validationLayers.data();
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
      createInfo.enabledLayerCount = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device_) != VK_SUCCESS)
 | 
			
		||||
    {
 | 
			
		||||
      throw std::runtime_error("failed to create logical device!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vkGetDeviceQueue(device_, indices.graphicsFamily, 0, &graphicsQueue_);
 | 
			
		||||
    vkGetDeviceQueue(device_, indices.presentFamily, 0, &presentQueue_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void Device::createCommandPool()
 | 
			
		||||
  {
 | 
			
		||||
    QueueFamilyIndices queueFamilyIndices = findPhysicalQueueFamilies();
 | 
			
		||||
 | 
			
		||||
    VkCommandPoolCreateInfo poolInfo = {};
 | 
			
		||||
    poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
 | 
			
		||||
    poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily;
 | 
			
		||||
    poolInfo.flags =
 | 
			
		||||
        VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
 | 
			
		||||
 | 
			
		||||
    if (vkCreateCommandPool(device_, &poolInfo, nullptr, &commandPool) != VK_SUCCESS)
 | 
			
		||||
    {
 | 
			
		||||
      throw std::runtime_error("failed to create command pool!");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void Device::createSurface() { window.createWindowSurface(instance, &surface_); }
 | 
			
		||||
 | 
			
		||||
  bool Device::isDeviceSuitable(VkPhysicalDevice device)
 | 
			
		||||
  {
 | 
			
		||||
    QueueFamilyIndices indices = findQueueFamilies(device);
 | 
			
		||||
 | 
			
		||||
    bool extensionsSupported = checkDeviceExtensionSupport(device);
 | 
			
		||||
 | 
			
		||||
    bool swapChainAdequate = false;
 | 
			
		||||
    if (extensionsSupported)
 | 
			
		||||
    {
 | 
			
		||||
      SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device);
 | 
			
		||||
      swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VkPhysicalDeviceFeatures supportedFeatures;
 | 
			
		||||
    vkGetPhysicalDeviceFeatures(device, &supportedFeatures);
 | 
			
		||||
 | 
			
		||||
    return indices.isComplete() && extensionsSupported && swapChainAdequate &&
 | 
			
		||||
           supportedFeatures.samplerAnisotropy;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void Device::populateDebugMessengerCreateInfo(
 | 
			
		||||
      VkDebugUtilsMessengerCreateInfoEXT &createInfo)
 | 
			
		||||
  {
 | 
			
		||||
    createInfo = {};
 | 
			
		||||
    createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
 | 
			
		||||
    createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
 | 
			
		||||
                                 VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
 | 
			
		||||
    createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
 | 
			
		||||
                             VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
 | 
			
		||||
                             VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
 | 
			
		||||
    createInfo.pfnUserCallback = debugCallback;
 | 
			
		||||
    createInfo.pUserData = nullptr; // Optional
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void Device::setupDebugMessenger()
 | 
			
		||||
  {
 | 
			
		||||
    if (!enableValidationLayers)
 | 
			
		||||
      return;
 | 
			
		||||
    VkDebugUtilsMessengerCreateInfoEXT createInfo;
 | 
			
		||||
    populateDebugMessengerCreateInfo(createInfo);
 | 
			
		||||
    if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS)
 | 
			
		||||
    {
 | 
			
		||||
      throw std::runtime_error("failed to set up debug messenger!");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool Device::checkValidationLayerSupport()
 | 
			
		||||
  {
 | 
			
		||||
    uint32_t layerCount;
 | 
			
		||||
    vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
 | 
			
		||||
 | 
			
		||||
    std::vector<VkLayerProperties> availableLayers(layerCount);
 | 
			
		||||
    vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
 | 
			
		||||
 | 
			
		||||
    for (const char *layerName : validationLayers)
 | 
			
		||||
    {
 | 
			
		||||
      bool layerFound = false;
 | 
			
		||||
 | 
			
		||||
      for (const auto &layerProperties : availableLayers)
 | 
			
		||||
      {
 | 
			
		||||
        if (strcmp(layerName, layerProperties.layerName) == 0)
 | 
			
		||||
        {
 | 
			
		||||
          layerFound = true;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!layerFound)
 | 
			
		||||
      {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::vector<const char *> Device::getRequiredExtensions()
 | 
			
		||||
  {
 | 
			
		||||
    uint32_t glfwExtensionCount = 0;
 | 
			
		||||
    const char **glfwExtensions;
 | 
			
		||||
    glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
 | 
			
		||||
 | 
			
		||||
    std::vector<const char *> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
 | 
			
		||||
 | 
			
		||||
    if (enableValidationLayers)
 | 
			
		||||
    {
 | 
			
		||||
      extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return extensions;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void Device::hasGflwRequiredInstanceExtensions()
 | 
			
		||||
  {
 | 
			
		||||
    uint32_t extensionCount = 0;
 | 
			
		||||
    vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
 | 
			
		||||
    std::vector<VkExtensionProperties> extensions(extensionCount);
 | 
			
		||||
    vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
 | 
			
		||||
 | 
			
		||||
    std::cout << "available extensions:" << std::endl;
 | 
			
		||||
    std::unordered_set<std::string> available;
 | 
			
		||||
    for (const auto &extension : extensions)
 | 
			
		||||
    {
 | 
			
		||||
      std::cout << "\t" << extension.extensionName << std::endl;
 | 
			
		||||
      available.insert(extension.extensionName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::cout << "required extensions:" << std::endl;
 | 
			
		||||
    auto requiredExtensions = getRequiredExtensions();
 | 
			
		||||
    for (const auto &required : requiredExtensions)
 | 
			
		||||
    {
 | 
			
		||||
      std::cout << "\t" << required << std::endl;
 | 
			
		||||
      if (available.find(required) == available.end())
 | 
			
		||||
      {
 | 
			
		||||
        throw std::runtime_error("Missing required glfw extension");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool Device::checkDeviceExtensionSupport(VkPhysicalDevice device)
 | 
			
		||||
  {
 | 
			
		||||
    uint32_t extensionCount;
 | 
			
		||||
    vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
 | 
			
		||||
 | 
			
		||||
    std::vector<VkExtensionProperties> availableExtensions(extensionCount);
 | 
			
		||||
    vkEnumerateDeviceExtensionProperties(
 | 
			
		||||
        device,
 | 
			
		||||
        nullptr,
 | 
			
		||||
        &extensionCount,
 | 
			
		||||
        availableExtensions.data());
 | 
			
		||||
 | 
			
		||||
    std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
 | 
			
		||||
 | 
			
		||||
    for (const auto &extension : availableExtensions)
 | 
			
		||||
    {
 | 
			
		||||
      requiredExtensions.erase(extension.extensionName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return requiredExtensions.empty();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  QueueFamilyIndices Device::findQueueFamilies(VkPhysicalDevice device)
 | 
			
		||||
  {
 | 
			
		||||
    QueueFamilyIndices indices;
 | 
			
		||||
 | 
			
		||||
    uint32_t queueFamilyCount = 0;
 | 
			
		||||
    vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
 | 
			
		||||
 | 
			
		||||
    std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
 | 
			
		||||
    vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
 | 
			
		||||
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    for (const auto &queueFamily : queueFamilies)
 | 
			
		||||
    {
 | 
			
		||||
      if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
 | 
			
		||||
      {
 | 
			
		||||
        indices.graphicsFamily = i;
 | 
			
		||||
        indices.graphicsFamilyHasValue = true;
 | 
			
		||||
      }
 | 
			
		||||
      VkBool32 presentSupport = false;
 | 
			
		||||
      vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface_, &presentSupport);
 | 
			
		||||
      if (queueFamily.queueCount > 0 && presentSupport)
 | 
			
		||||
      {
 | 
			
		||||
        indices.presentFamily = i;
 | 
			
		||||
        indices.presentFamilyHasValue = true;
 | 
			
		||||
      }
 | 
			
		||||
      if (indices.isComplete())
 | 
			
		||||
      {
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      i++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return indices;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  SwapChainSupportDetails Device::querySwapChainSupport(VkPhysicalDevice device)
 | 
			
		||||
  {
 | 
			
		||||
    SwapChainSupportDetails details;
 | 
			
		||||
    vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface_, &details.capabilities);
 | 
			
		||||
 | 
			
		||||
    uint32_t formatCount;
 | 
			
		||||
    vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface_, &formatCount, nullptr);
 | 
			
		||||
 | 
			
		||||
    if (formatCount != 0)
 | 
			
		||||
    {
 | 
			
		||||
      details.formats.resize(formatCount);
 | 
			
		||||
      vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface_, &formatCount, details.formats.data());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint32_t presentModeCount;
 | 
			
		||||
    vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface_, &presentModeCount, nullptr);
 | 
			
		||||
 | 
			
		||||
    if (presentModeCount != 0)
 | 
			
		||||
    {
 | 
			
		||||
      details.presentModes.resize(presentModeCount);
 | 
			
		||||
      vkGetPhysicalDeviceSurfacePresentModesKHR(
 | 
			
		||||
          device,
 | 
			
		||||
          surface_,
 | 
			
		||||
          &presentModeCount,
 | 
			
		||||
          details.presentModes.data());
 | 
			
		||||
    }
 | 
			
		||||
    return details;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  VkFormat Device::findSupportedFormat(
 | 
			
		||||
      const std::vector<VkFormat> &candidates, VkImageTiling tiling, VkFormatFeatureFlags features)
 | 
			
		||||
  {
 | 
			
		||||
    for (VkFormat format : candidates)
 | 
			
		||||
    {
 | 
			
		||||
      VkFormatProperties props;
 | 
			
		||||
      vkGetPhysicalDeviceFormatProperties(physicalDevice, 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!");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint32_t Device::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
 | 
			
		||||
  {
 | 
			
		||||
    VkPhysicalDeviceMemoryProperties memProperties;
 | 
			
		||||
    vkGetPhysicalDeviceMemoryProperties(physicalDevice, &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 Device::createBuffer(
 | 
			
		||||
      VkDeviceSize size,
 | 
			
		||||
      VkBufferUsageFlags usage,
 | 
			
		||||
      VkMemoryPropertyFlags properties,
 | 
			
		||||
      VkBuffer &buffer,
 | 
			
		||||
      VkDeviceMemory &bufferMemory)
 | 
			
		||||
  {
 | 
			
		||||
    VkBufferCreateInfo bufferInfo{};
 | 
			
		||||
    bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
 | 
			
		||||
    bufferInfo.size = size;
 | 
			
		||||
    bufferInfo.usage = usage;
 | 
			
		||||
    bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
 | 
			
		||||
 | 
			
		||||
    if (vkCreateBuffer(device_, &bufferInfo, nullptr, &buffer) != VK_SUCCESS)
 | 
			
		||||
    {
 | 
			
		||||
      throw std::runtime_error("failed to create vertex buffer!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VkMemoryRequirements memRequirements;
 | 
			
		||||
    vkGetBufferMemoryRequirements(device_, buffer, &memRequirements);
 | 
			
		||||
 | 
			
		||||
    VkMemoryAllocateInfo allocInfo{};
 | 
			
		||||
    allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
 | 
			
		||||
    allocInfo.allocationSize = memRequirements.size;
 | 
			
		||||
    allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
 | 
			
		||||
 | 
			
		||||
    if (vkAllocateMemory(device_, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS)
 | 
			
		||||
    {
 | 
			
		||||
      throw std::runtime_error("failed to allocate vertex buffer memory!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vkBindBufferMemory(device_, buffer, bufferMemory, 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  VkCommandBuffer Device::beginSingleTimeCommands()
 | 
			
		||||
  {
 | 
			
		||||
    VkCommandBufferAllocateInfo allocInfo{};
 | 
			
		||||
    allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
 | 
			
		||||
    allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
 | 
			
		||||
    allocInfo.commandPool = commandPool;
 | 
			
		||||
    allocInfo.commandBufferCount = 1;
 | 
			
		||||
 | 
			
		||||
    VkCommandBuffer commandBuffer;
 | 
			
		||||
    vkAllocateCommandBuffers(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 Device::endSingleTimeCommands(VkCommandBuffer commandBuffer)
 | 
			
		||||
  {
 | 
			
		||||
    vkEndCommandBuffer(commandBuffer);
 | 
			
		||||
 | 
			
		||||
    VkSubmitInfo submitInfo{};
 | 
			
		||||
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
 | 
			
		||||
    submitInfo.commandBufferCount = 1;
 | 
			
		||||
    submitInfo.pCommandBuffers = &commandBuffer;
 | 
			
		||||
 | 
			
		||||
    vkQueueSubmit(graphicsQueue_, 1, &submitInfo, VK_NULL_HANDLE);
 | 
			
		||||
    vkQueueWaitIdle(graphicsQueue_);
 | 
			
		||||
 | 
			
		||||
    vkFreeCommandBuffers(device_, commandPool, 1, &commandBuffer);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void Device::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)
 | 
			
		||||
  {
 | 
			
		||||
    VkCommandBuffer commandBuffer = beginSingleTimeCommands();
 | 
			
		||||
 | 
			
		||||
    VkBufferCopy copyRegion{};
 | 
			
		||||
    copyRegion.srcOffset = 0; // Optional
 | 
			
		||||
    copyRegion.dstOffset = 0; // Optional
 | 
			
		||||
    copyRegion.size = size;
 | 
			
		||||
    vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region);
 | 
			
		||||
 | 
			
		||||
    endSingleTimeCommands(commandBuffer);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void Device::copyBufferToImage(
 | 
			
		||||
      VkBuffer buffer, VkImage image, uint32_t width, uint32_t height, uint32_t layerCount)
 | 
			
		||||
  {
 | 
			
		||||
    VkCommandBuffer commandBuffer = beginSingleTimeCommands();
 | 
			
		||||
 | 
			
		||||
    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 = layerCount;
 | 
			
		||||
 | 
			
		||||
    region.imageOffset = {0, 0, 0};
 | 
			
		||||
    region.imageExtent = {width, height, 1};
 | 
			
		||||
 | 
			
		||||
    vkCmdCopyBufferToImage(
 | 
			
		||||
        commandBuffer,
 | 
			
		||||
        buffer,
 | 
			
		||||
        image,
 | 
			
		||||
        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
 | 
			
		||||
        1,
 | 
			
		||||
        ®ion);
 | 
			
		||||
    endSingleTimeCommands(commandBuffer);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void Device::createImageWithInfo(
 | 
			
		||||
      const VkImageCreateInfo &imageInfo,
 | 
			
		||||
      VkMemoryPropertyFlags properties,
 | 
			
		||||
      VkImage &image,
 | 
			
		||||
      VkDeviceMemory &imageMemory)
 | 
			
		||||
  {
 | 
			
		||||
    if (vkCreateImage(device_, &imageInfo, nullptr, &image) != VK_SUCCESS)
 | 
			
		||||
    {
 | 
			
		||||
      throw std::runtime_error("failed to create image!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VkMemoryRequirements memRequirements;
 | 
			
		||||
    vkGetImageMemoryRequirements(device_, image, &memRequirements);
 | 
			
		||||
 | 
			
		||||
    VkMemoryAllocateInfo allocInfo{};
 | 
			
		||||
    allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
 | 
			
		||||
    allocInfo.allocationSize = memRequirements.size;
 | 
			
		||||
    allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
 | 
			
		||||
 | 
			
		||||
    if (vkAllocateMemory(device_, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS)
 | 
			
		||||
    {
 | 
			
		||||
      throw std::runtime_error("failed to allocate image memory!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (vkBindImageMemory(device_, image, imageMemory, 0) != VK_SUCCESS)
 | 
			
		||||
    {
 | 
			
		||||
      throw std::runtime_error("failed to bind image memory!");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
} // namespace hk
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,112 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "hk_window.hpp"
 | 
			
		||||
 | 
			
		||||
// std lib headers
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace hk
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  struct SwapChainSupportDetails
 | 
			
		||||
  {
 | 
			
		||||
    VkSurfaceCapabilitiesKHR capabilities;
 | 
			
		||||
    std::vector<VkSurfaceFormatKHR> formats;
 | 
			
		||||
    std::vector<VkPresentModeKHR> presentModes;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  struct QueueFamilyIndices
 | 
			
		||||
  {
 | 
			
		||||
    uint32_t graphicsFamily;
 | 
			
		||||
    uint32_t presentFamily;
 | 
			
		||||
    bool graphicsFamilyHasValue = false;
 | 
			
		||||
    bool presentFamilyHasValue = false;
 | 
			
		||||
    bool isComplete() { return graphicsFamilyHasValue && presentFamilyHasValue; }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  class Device
 | 
			
		||||
  {
 | 
			
		||||
  public:
 | 
			
		||||
#ifdef NDEBUG
 | 
			
		||||
    const bool enableValidationLayers = false;
 | 
			
		||||
#else
 | 
			
		||||
    const bool enableValidationLayers = true;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    Device(Window &window);
 | 
			
		||||
    ~Device();
 | 
			
		||||
 | 
			
		||||
    // Not copyable or movable
 | 
			
		||||
    Device(const Device &) = delete;
 | 
			
		||||
    void operator=(const Device &) = delete;
 | 
			
		||||
    Device(Device &&) = delete;
 | 
			
		||||
    Device &operator=(Device &&) = delete;
 | 
			
		||||
 | 
			
		||||
    VkCommandPool getCommandPool() { return commandPool; }
 | 
			
		||||
    VkDevice device() { return device_; }
 | 
			
		||||
    VkSurfaceKHR surface() { return surface_; }
 | 
			
		||||
    VkQueue graphicsQueue() { return graphicsQueue_; }
 | 
			
		||||
    VkQueue presentQueue() { return presentQueue_; }
 | 
			
		||||
 | 
			
		||||
    SwapChainSupportDetails getSwapChainSupport() { return querySwapChainSupport(physicalDevice); }
 | 
			
		||||
    uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties);
 | 
			
		||||
    QueueFamilyIndices findPhysicalQueueFamilies() { return findQueueFamilies(physicalDevice); }
 | 
			
		||||
    VkFormat findSupportedFormat(
 | 
			
		||||
        const std::vector<VkFormat> &candidates, VkImageTiling tiling, VkFormatFeatureFlags features);
 | 
			
		||||
 | 
			
		||||
    // Buffer Helper Functions
 | 
			
		||||
    void createBuffer(
 | 
			
		||||
        VkDeviceSize size,
 | 
			
		||||
        VkBufferUsageFlags usage,
 | 
			
		||||
        VkMemoryPropertyFlags properties,
 | 
			
		||||
        VkBuffer &buffer,
 | 
			
		||||
        VkDeviceMemory &bufferMemory);
 | 
			
		||||
    VkCommandBuffer beginSingleTimeCommands();
 | 
			
		||||
    void endSingleTimeCommands(VkCommandBuffer commandBuffer);
 | 
			
		||||
    void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size);
 | 
			
		||||
    void copyBufferToImage(
 | 
			
		||||
        VkBuffer buffer, VkImage image, uint32_t width, uint32_t height, uint32_t layerCount);
 | 
			
		||||
 | 
			
		||||
    void createImageWithInfo(
 | 
			
		||||
        const VkImageCreateInfo &imageInfo,
 | 
			
		||||
        VkMemoryPropertyFlags properties,
 | 
			
		||||
        VkImage &image,
 | 
			
		||||
        VkDeviceMemory &imageMemory);
 | 
			
		||||
 | 
			
		||||
    VkPhysicalDeviceProperties properties;
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    void createInstance();
 | 
			
		||||
    void setupDebugMessenger();
 | 
			
		||||
    void createSurface();
 | 
			
		||||
    void pickPhysicalDevice();
 | 
			
		||||
    void createLogicalDevice();
 | 
			
		||||
    void createCommandPool();
 | 
			
		||||
 | 
			
		||||
    // helper functions
 | 
			
		||||
    bool isDeviceSuitable(VkPhysicalDevice device);
 | 
			
		||||
    std::vector<const char *> getRequiredExtensions();
 | 
			
		||||
    bool checkValidationLayerSupport();
 | 
			
		||||
    QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device);
 | 
			
		||||
    void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT &createInfo);
 | 
			
		||||
    void hasGflwRequiredInstanceExtensions();
 | 
			
		||||
    bool checkDeviceExtensionSupport(VkPhysicalDevice device);
 | 
			
		||||
    SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device);
 | 
			
		||||
 | 
			
		||||
    VkInstance instance;
 | 
			
		||||
    VkDebugUtilsMessengerEXT debugMessenger;
 | 
			
		||||
    VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
 | 
			
		||||
    Window &window;
 | 
			
		||||
    VkCommandPool commandPool;
 | 
			
		||||
 | 
			
		||||
    VkDevice device_;
 | 
			
		||||
    VkSurfaceKHR surface_;
 | 
			
		||||
    VkQueue graphicsQueue_;
 | 
			
		||||
    VkQueue presentQueue_;
 | 
			
		||||
 | 
			
		||||
    const std::vector<const char *> validationLayers = {"VK_LAYER_KHRONOS_validation"};
 | 
			
		||||
    const std::vector<const char *> deviceExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
} // namespace hk
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,71 @@
 | 
			
		|||
#include "hk_model.hpp"
 | 
			
		||||
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
 | 
			
		||||
namespace hk{
 | 
			
		||||
    Model::Model(Device &device, const std::vector<Vertex> &vertices)
 | 
			
		||||
        : device{device}
 | 
			
		||||
    {
 | 
			
		||||
        createVertexBuffers(vertices);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Model::~Model()
 | 
			
		||||
    {
 | 
			
		||||
        vkDestroyBuffer(device.device(), vertexBuffer, nullptr);
 | 
			
		||||
        vkFreeMemory(device.device(), vertexBufferMemory, nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Model::createVertexBuffers(const std::vector<Vertex> &vertices)
 | 
			
		||||
    {
 | 
			
		||||
        vertexCount = static_cast<uint32_t>(vertices.size());
 | 
			
		||||
        assert(vertexCount >= 3 && "Vertex count must be at least 3");
 | 
			
		||||
        VkDeviceSize bufferSize = sizeof(vertices[0]) * vertexCount;
 | 
			
		||||
        device.createBuffer(
 | 
			
		||||
            bufferSize,
 | 
			
		||||
            VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
 | 
			
		||||
            VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
 | 
			
		||||
            vertexBuffer,
 | 
			
		||||
            vertexBufferMemory
 | 
			
		||||
        );
 | 
			
		||||
        void *data;
 | 
			
		||||
        vkMapMemory(device.device(), vertexBufferMemory, 0, bufferSize, 0, &data);
 | 
			
		||||
        memcpy(data, vertices.data(), static_cast<size_t>(bufferSize));
 | 
			
		||||
        vkUnmapMemory(device.device(), vertexBufferMemory);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Model::bind(VkCommandBuffer commandBuffer)
 | 
			
		||||
    {
 | 
			
		||||
        VkBuffer buffers[] = {vertexBuffer};
 | 
			
		||||
        VkDeviceSize offsets[] = {0};
 | 
			
		||||
        vkCmdBindVertexBuffers(commandBuffer, 0, 1, buffers, offsets);
 | 
			
		||||
    }
 | 
			
		||||
    void Model::draw(VkCommandBuffer commandBuffer)
 | 
			
		||||
    {
 | 
			
		||||
        vkCmdDraw(commandBuffer, vertexCount, 1, 0, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<VkVertexInputBindingDescription> Model::Vertex::getBindingDescriptions()
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<VkVertexInputBindingDescription> bindingDescriptions(1);
 | 
			
		||||
        bindingDescriptions[0].binding = 0;
 | 
			
		||||
        bindingDescriptions[0].stride = sizeof(Vertex);
 | 
			
		||||
        bindingDescriptions[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
 | 
			
		||||
        return bindingDescriptions;
 | 
			
		||||
    }
 | 
			
		||||
    std::vector<VkVertexInputAttributeDescription> Model::Vertex::getAttributeDescriptions()
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<VkVertexInputAttributeDescription> attributeDescriptions(2);
 | 
			
		||||
        attributeDescriptions[0].binding = 0;
 | 
			
		||||
        attributeDescriptions[0].location = 0;
 | 
			
		||||
        attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
 | 
			
		||||
        attributeDescriptions[0].offset = offsetof(Vertex, posision);
 | 
			
		||||
 | 
			
		||||
        attributeDescriptions[1].binding = 0;
 | 
			
		||||
        attributeDescriptions[1].location = 1;
 | 
			
		||||
        attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
 | 
			
		||||
        attributeDescriptions[1].offset = offsetof(Vertex, color);
 | 
			
		||||
 | 
			
		||||
        return attributeDescriptions;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
#include "hk_device.hpp"
 | 
			
		||||
 | 
			
		||||
// libs
 | 
			
		||||
#define GLM_FORCE_RADIANS
 | 
			
		||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
 | 
			
		||||
#include <glm/glm.hpp>
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace hk{
 | 
			
		||||
    class Model
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        struct Vertex {
 | 
			
		||||
            glm::vec2 posision;
 | 
			
		||||
            glm::vec3 color;
 | 
			
		||||
            
 | 
			
		||||
            static std::vector<VkVertexInputBindingDescription> getBindingDescriptions();
 | 
			
		||||
            static std::vector<VkVertexInputAttributeDescription> getAttributeDescriptions();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Model(Device &device, const std::vector<Vertex> &vertices);
 | 
			
		||||
        ~Model();
 | 
			
		||||
 | 
			
		||||
        Model(const Model &) = delete;
 | 
			
		||||
        Model &operator=(const Model &) = delete;
 | 
			
		||||
 | 
			
		||||
        void bind(VkCommandBuffer commandBuffer);
 | 
			
		||||
        void draw(VkCommandBuffer commandBuffer);
 | 
			
		||||
    private:
 | 
			
		||||
        void createVertexBuffers(const std::vector<Vertex> &vertices);
 | 
			
		||||
 | 
			
		||||
        Device &device;
 | 
			
		||||
        VkBuffer vertexBuffer;
 | 
			
		||||
        VkDeviceMemory vertexBufferMemory;
 | 
			
		||||
        uint32_t vertexCount;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,214 @@
 | 
			
		|||
#include "hk_pipeline.hpp"
 | 
			
		||||
 | 
			
		||||
#include "hk_model.hpp"
 | 
			
		||||
 | 
			
		||||
// std
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <cassert>
 | 
			
		||||
 | 
			
		||||
namespace hk
 | 
			
		||||
{
 | 
			
		||||
    Pipeline::Pipeline(
 | 
			
		||||
            Device& device,
 | 
			
		||||
            const std::string &vertFilePath,
 | 
			
		||||
            const std::string &fragFilePath,
 | 
			
		||||
            const PipelineConfigInfo& configInfo) : device(device)
 | 
			
		||||
    {
 | 
			
		||||
        createGraphicPipeline(vertFilePath, fragFilePath, configInfo);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Pipeline::~Pipeline()
 | 
			
		||||
    {
 | 
			
		||||
        vkDestroyShaderModule(device.device(), vertShaderModule, nullptr);
 | 
			
		||||
        vkDestroyShaderModule(device.device(), fragShaderModule, nullptr);
 | 
			
		||||
        vkDestroyPipeline(device.device(), graphicPipeline, nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Pipeline::bind(VkCommandBuffer commandBuffer)
 | 
			
		||||
    {
 | 
			
		||||
        vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicPipeline);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    PipelineConfigInfo Pipeline::defaultPipelineConfigInfo(uint32_t width, uint32_t height)
 | 
			
		||||
    {
 | 
			
		||||
        PipelineConfigInfo configInfo{};
 | 
			
		||||
        configInfo.inputAssemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
 | 
			
		||||
        configInfo.inputAssemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
 | 
			
		||||
        configInfo.inputAssemblyInfo.primitiveRestartEnable = VK_FALSE;
 | 
			
		||||
 | 
			
		||||
        configInfo.viewport.x = 0.0f;
 | 
			
		||||
        configInfo.viewport.y = 0.0f;
 | 
			
		||||
        configInfo.viewport.width = static_cast<float>(width);
 | 
			
		||||
        configInfo.viewport.height = static_cast<float>(height);
 | 
			
		||||
        configInfo.viewport.minDepth = 0.0f;
 | 
			
		||||
        configInfo.viewport.maxDepth = 1.0f;
 | 
			
		||||
 | 
			
		||||
        configInfo.scissor.offset = {0, 0};
 | 
			
		||||
        configInfo.scissor.extent = {width, height};
 | 
			
		||||
 | 
			
		||||
        configInfo.rasterizationInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
 | 
			
		||||
        configInfo.rasterizationInfo.depthClampEnable = VK_FALSE;
 | 
			
		||||
        configInfo.rasterizationInfo.rasterizerDiscardEnable = VK_FALSE;
 | 
			
		||||
        configInfo.rasterizationInfo.polygonMode = VK_POLYGON_MODE_FILL;
 | 
			
		||||
        configInfo.rasterizationInfo.lineWidth = 1.0f;
 | 
			
		||||
        configInfo.rasterizationInfo.cullMode = VK_CULL_MODE_NONE;
 | 
			
		||||
        configInfo.rasterizationInfo.frontFace = VK_FRONT_FACE_CLOCKWISE;
 | 
			
		||||
        configInfo.rasterizationInfo.depthBiasEnable = VK_FALSE;
 | 
			
		||||
        configInfo.rasterizationInfo.depthBiasConstantFactor = 0.0f; // Optional
 | 
			
		||||
        configInfo.rasterizationInfo.depthBiasClamp = 0.0f;          // Optional
 | 
			
		||||
        configInfo.rasterizationInfo.depthBiasSlopeFactor = 0.0f;    // Optional
 | 
			
		||||
 | 
			
		||||
        configInfo.multisampleInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
 | 
			
		||||
        configInfo.multisampleInfo.sampleShadingEnable = VK_FALSE;
 | 
			
		||||
        configInfo.multisampleInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
 | 
			
		||||
        configInfo.multisampleInfo.minSampleShading = 1.0f;          // Optional
 | 
			
		||||
        configInfo.multisampleInfo.pSampleMask = nullptr;            // Optional
 | 
			
		||||
        configInfo.multisampleInfo.alphaToCoverageEnable = VK_FALSE; // Optional
 | 
			
		||||
        configInfo.multisampleInfo.alphaToOneEnable = VK_FALSE;      // Optional
 | 
			
		||||
 | 
			
		||||
        configInfo.colorBlendAttachment.colorWriteMask =
 | 
			
		||||
            VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT |
 | 
			
		||||
            VK_COLOR_COMPONENT_A_BIT;
 | 
			
		||||
        configInfo.colorBlendAttachment.blendEnable = VK_FALSE;
 | 
			
		||||
        configInfo.colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;  // Optional
 | 
			
		||||
        configInfo.colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional
 | 
			
		||||
        configInfo.colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;             // Optional
 | 
			
		||||
        configInfo.colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;  // Optional
 | 
			
		||||
        configInfo.colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional
 | 
			
		||||
        configInfo.colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;             // Optional
 | 
			
		||||
 | 
			
		||||
        configInfo.colorBlendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
 | 
			
		||||
        configInfo.colorBlendInfo.logicOpEnable = VK_FALSE;
 | 
			
		||||
        configInfo.colorBlendInfo.logicOp = VK_LOGIC_OP_COPY; // Optional
 | 
			
		||||
        configInfo.colorBlendInfo.attachmentCount = 1;
 | 
			
		||||
        configInfo.colorBlendInfo.pAttachments = &configInfo.colorBlendAttachment;
 | 
			
		||||
        configInfo.colorBlendInfo.blendConstants[0] = 0.0f; // Optional
 | 
			
		||||
        configInfo.colorBlendInfo.blendConstants[1] = 0.0f; // Optional
 | 
			
		||||
        configInfo.colorBlendInfo.blendConstants[2] = 0.0f; // Optional
 | 
			
		||||
        configInfo.colorBlendInfo.blendConstants[3] = 0.0f; // Optional
 | 
			
		||||
 | 
			
		||||
        configInfo.depthStencilInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
 | 
			
		||||
        configInfo.depthStencilInfo.depthTestEnable = VK_TRUE;
 | 
			
		||||
        configInfo.depthStencilInfo.depthWriteEnable = VK_TRUE;
 | 
			
		||||
        configInfo.depthStencilInfo.depthCompareOp = VK_COMPARE_OP_LESS;
 | 
			
		||||
        configInfo.depthStencilInfo.depthBoundsTestEnable = VK_FALSE;
 | 
			
		||||
        configInfo.depthStencilInfo.minDepthBounds = 0.0f; // Optional
 | 
			
		||||
        configInfo.depthStencilInfo.maxDepthBounds = 1.0f; // Optional
 | 
			
		||||
        configInfo.depthStencilInfo.stencilTestEnable = VK_FALSE;
 | 
			
		||||
        configInfo.depthStencilInfo.front = {}; // Optional
 | 
			
		||||
        configInfo.depthStencilInfo.back = {};  // Optional
 | 
			
		||||
 | 
			
		||||
        return configInfo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<char> Pipeline::readFile(const std::string &filePath)
 | 
			
		||||
    {
 | 
			
		||||
        std::ifstream file(filePath, std::ios::ate | std::ios::binary);
 | 
			
		||||
 | 
			
		||||
        if (!file.is_open())
 | 
			
		||||
        {
 | 
			
		||||
            throw std::runtime_error("failed to open file: " + filePath);
 | 
			
		||||
        }
 | 
			
		||||
        size_t fileSize = static_cast<size_t>(file.tellg());
 | 
			
		||||
        std::vector<char> buffer(fileSize);
 | 
			
		||||
 | 
			
		||||
        file.seekg(0);
 | 
			
		||||
        file.read(buffer.data(), fileSize);
 | 
			
		||||
 | 
			
		||||
        file.close();
 | 
			
		||||
 | 
			
		||||
        return buffer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Pipeline::createGraphicPipeline(
 | 
			
		||||
        const std::string& vertFilePath,
 | 
			
		||||
        const std::string& fragFilePath,
 | 
			
		||||
        const PipelineConfigInfo& configInfo)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        assert(configInfo.pipelineLayout != VK_NULL_HANDLE && "Cannot create graphics pipeline: no pipelineLayout provided in configInfo");
 | 
			
		||||
        assert(configInfo.renderPass != VK_NULL_HANDLE && "Cannot create graphics pipeline: no renderPass provided in configInfo");
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        std::vector<char> vertCode = readFile(vertFilePath);
 | 
			
		||||
        std::vector<char> fragCode = readFile(fragFilePath);
 | 
			
		||||
 | 
			
		||||
        createShaderModule(vertCode, &vertShaderModule);
 | 
			
		||||
        createShaderModule(fragCode, &fragShaderModule);
 | 
			
		||||
 | 
			
		||||
        VkPipelineShaderStageCreateInfo shaderStages[2];
 | 
			
		||||
        shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
 | 
			
		||||
        shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
 | 
			
		||||
        shaderStages[0].module = vertShaderModule;
 | 
			
		||||
        shaderStages[0].pName = "main";
 | 
			
		||||
        shaderStages[0].flags = 0;
 | 
			
		||||
        shaderStages[0].pNext = nullptr;
 | 
			
		||||
        shaderStages[0].pSpecializationInfo = nullptr;
 | 
			
		||||
 | 
			
		||||
        shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
 | 
			
		||||
        shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
 | 
			
		||||
        shaderStages[1].module = fragShaderModule;
 | 
			
		||||
        shaderStages[1].pName = "main";
 | 
			
		||||
        shaderStages[1].flags = 0;
 | 
			
		||||
        shaderStages[1].pNext = nullptr;
 | 
			
		||||
        shaderStages[1].pSpecializationInfo = nullptr;   
 | 
			
		||||
 | 
			
		||||
        auto bindingDescriptions = Model::Vertex::getBindingDescriptions();
 | 
			
		||||
        auto attributeDescriptions = Model::Vertex::getAttributeDescriptions();
 | 
			
		||||
 | 
			
		||||
        VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
 | 
			
		||||
        vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
 | 
			
		||||
        vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
 | 
			
		||||
        vertexInputInfo.vertexBindingDescriptionCount = static_cast<uint32_t>(bindingDescriptions.size());
 | 
			
		||||
        vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
 | 
			
		||||
        vertexInputInfo.pVertexBindingDescriptions = bindingDescriptions.data();
 | 
			
		||||
 | 
			
		||||
        VkPipelineViewportStateCreateInfo viewportInfo{};
 | 
			
		||||
        viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
 | 
			
		||||
        viewportInfo.viewportCount = 1;
 | 
			
		||||
        viewportInfo.pViewports = &configInfo.viewport;
 | 
			
		||||
        viewportInfo.scissorCount = 1;
 | 
			
		||||
        viewportInfo.pScissors = &configInfo.scissor;
 | 
			
		||||
 | 
			
		||||
        VkGraphicsPipelineCreateInfo pipelineInfo{};
 | 
			
		||||
        pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
 | 
			
		||||
        pipelineInfo.stageCount = 2;
 | 
			
		||||
        pipelineInfo.pStages = shaderStages;
 | 
			
		||||
        pipelineInfo.pVertexInputState = &vertexInputInfo;
 | 
			
		||||
        pipelineInfo.pInputAssemblyState = &configInfo.inputAssemblyInfo;
 | 
			
		||||
        pipelineInfo.pViewportState = &viewportInfo;
 | 
			
		||||
        pipelineInfo.pRasterizationState = &configInfo.rasterizationInfo;
 | 
			
		||||
        pipelineInfo.pMultisampleState = &configInfo.multisampleInfo;
 | 
			
		||||
        pipelineInfo.pColorBlendState = &configInfo.colorBlendInfo;
 | 
			
		||||
        pipelineInfo.pDepthStencilState = &configInfo.depthStencilInfo;
 | 
			
		||||
        pipelineInfo.pDynamicState = nullptr;
 | 
			
		||||
 | 
			
		||||
        pipelineInfo.layout = configInfo.pipelineLayout;
 | 
			
		||||
        pipelineInfo.renderPass = configInfo.renderPass;
 | 
			
		||||
        pipelineInfo.subpass = configInfo.subpass;
 | 
			
		||||
 | 
			
		||||
        pipelineInfo.basePipelineIndex = -1;
 | 
			
		||||
        pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
 | 
			
		||||
 | 
			
		||||
        if (vkCreateGraphicsPipelines(device.device(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicPipeline) != VK_SUCCESS)
 | 
			
		||||
        {
 | 
			
		||||
            throw std::runtime_error("failed to create graphics pipeline!");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    void Pipeline::createShaderModule(const std::vector<char> &code, VkShaderModule *shaderModule)
 | 
			
		||||
    {
 | 
			
		||||
        VkShaderModuleCreateInfo createInfo{};
 | 
			
		||||
        createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
 | 
			
		||||
        createInfo.codeSize = code.size();
 | 
			
		||||
        createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
 | 
			
		||||
 | 
			
		||||
        if (vkCreateShaderModule(device.device(), &createInfo, nullptr, shaderModule) != VK_SUCCESS)
 | 
			
		||||
        {
 | 
			
		||||
            throw std::runtime_error("failed to create shader module!");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "hk_device.hpp"
 | 
			
		||||
 | 
			
		||||
// std
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace hk
 | 
			
		||||
{
 | 
			
		||||
    struct PipelineConfigInfo
 | 
			
		||||
    {
 | 
			
		||||
        VkViewport viewport;
 | 
			
		||||
        VkRect2D scissor;
 | 
			
		||||
        VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo;
 | 
			
		||||
        VkPipelineRasterizationStateCreateInfo rasterizationInfo;
 | 
			
		||||
        VkPipelineMultisampleStateCreateInfo multisampleInfo;
 | 
			
		||||
        VkPipelineColorBlendAttachmentState colorBlendAttachment;
 | 
			
		||||
        VkPipelineColorBlendStateCreateInfo colorBlendInfo;
 | 
			
		||||
        VkPipelineDepthStencilStateCreateInfo depthStencilInfo;
 | 
			
		||||
        VkPipelineLayout pipelineLayout = nullptr;
 | 
			
		||||
        VkRenderPass renderPass = nullptr;
 | 
			
		||||
        uint32_t subpass = 0;
 | 
			
		||||
    };
 | 
			
		||||
    class Pipeline
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        Pipeline(
 | 
			
		||||
            Device& device,
 | 
			
		||||
            const std::string &vertFilePath,
 | 
			
		||||
            const std::string &fragFilePath,
 | 
			
		||||
            const PipelineConfigInfo& configInfo);
 | 
			
		||||
        
 | 
			
		||||
        ~Pipeline();
 | 
			
		||||
        
 | 
			
		||||
        Pipeline(const Pipeline&) = delete;
 | 
			
		||||
        Pipeline &operator=(const Pipeline&) = delete;
 | 
			
		||||
 | 
			
		||||
        void bind(VkCommandBuffer commandBuffer);
 | 
			
		||||
 | 
			
		||||
        static PipelineConfigInfo defaultPipelineConfigInfo(uint32_t width, uint32_t height);
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        static std::vector<char> readFile(const std::string &filePath);
 | 
			
		||||
        void createGraphicPipeline(
 | 
			
		||||
            const std::string &vertFilePath,
 | 
			
		||||
            const std::string &fragFilePath,
 | 
			
		||||
            const PipelineConfigInfo& configInfo);
 | 
			
		||||
 | 
			
		||||
        void createShaderModule(const std::vector<char>& code, VkShaderModule* shaderModule);
 | 
			
		||||
    
 | 
			
		||||
        Device& device;
 | 
			
		||||
        VkPipeline graphicPipeline;
 | 
			
		||||
        VkShaderModule vertShaderModule;
 | 
			
		||||
        VkShaderModule fragShaderModule;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,460 @@
 | 
			
		|||
#include "hk_swap_chain.hpp"
 | 
			
		||||
 | 
			
		||||
// std
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <limits>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
 | 
			
		||||
namespace hk
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  SwapChain::SwapChain(Device &deviceRef, VkExtent2D extent)
 | 
			
		||||
      : device{deviceRef}, windowExtent{extent}
 | 
			
		||||
  {
 | 
			
		||||
    createSwapChain();
 | 
			
		||||
    createImageViews();
 | 
			
		||||
    createRenderPass();
 | 
			
		||||
    createDepthResources();
 | 
			
		||||
    createFramebuffers();
 | 
			
		||||
    createSyncObjects();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  SwapChain::~SwapChain()
 | 
			
		||||
  {
 | 
			
		||||
    for (auto imageView : swapChainImageViews)
 | 
			
		||||
    {
 | 
			
		||||
      vkDestroyImageView(device.device(), imageView, nullptr);
 | 
			
		||||
    }
 | 
			
		||||
    swapChainImageViews.clear();
 | 
			
		||||
 | 
			
		||||
    if (swapChain != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
      vkDestroySwapchainKHR(device.device(), swapChain, nullptr);
 | 
			
		||||
      swapChain = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < depthImages.size(); i++)
 | 
			
		||||
    {
 | 
			
		||||
      vkDestroyImageView(device.device(), depthImageViews[i], nullptr);
 | 
			
		||||
      vkDestroyImage(device.device(), depthImages[i], nullptr);
 | 
			
		||||
      vkFreeMemory(device.device(), depthImageMemorys[i], nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (auto framebuffer : swapChainFramebuffers)
 | 
			
		||||
    {
 | 
			
		||||
      vkDestroyFramebuffer(device.device(), framebuffer, nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vkDestroyRenderPass(device.device(), renderPass, nullptr);
 | 
			
		||||
 | 
			
		||||
    // cleanup synchronization objects
 | 
			
		||||
    for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
 | 
			
		||||
    {
 | 
			
		||||
      vkDestroySemaphore(device.device(), renderFinishedSemaphores[i], nullptr);
 | 
			
		||||
      vkDestroySemaphore(device.device(), imageAvailableSemaphores[i], nullptr);
 | 
			
		||||
      vkDestroyFence(device.device(), inFlightFences[i], nullptr);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  VkResult SwapChain::acquireNextImage(uint32_t *imageIndex)
 | 
			
		||||
  {
 | 
			
		||||
    vkWaitForFences(
 | 
			
		||||
        device.device(),
 | 
			
		||||
        1,
 | 
			
		||||
        &inFlightFences[currentFrame],
 | 
			
		||||
        VK_TRUE,
 | 
			
		||||
        std::numeric_limits<uint64_t>::max());
 | 
			
		||||
 | 
			
		||||
    VkResult result = vkAcquireNextImageKHR(
 | 
			
		||||
        device.device(),
 | 
			
		||||
        swapChain,
 | 
			
		||||
        std::numeric_limits<uint64_t>::max(),
 | 
			
		||||
        imageAvailableSemaphores[currentFrame], // must be a not signaled semaphore
 | 
			
		||||
        VK_NULL_HANDLE,
 | 
			
		||||
        imageIndex);
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  VkResult SwapChain::submitCommandBuffers(
 | 
			
		||||
      const VkCommandBuffer *buffers, uint32_t *imageIndex)
 | 
			
		||||
  {
 | 
			
		||||
    if (imagesInFlight[*imageIndex] != VK_NULL_HANDLE)
 | 
			
		||||
    {
 | 
			
		||||
      vkWaitForFences(device.device(), 1, &imagesInFlight[*imageIndex], VK_TRUE, UINT64_MAX);
 | 
			
		||||
    }
 | 
			
		||||
    imagesInFlight[*imageIndex] = inFlightFences[currentFrame];
 | 
			
		||||
 | 
			
		||||
    VkSubmitInfo submitInfo = {};
 | 
			
		||||
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
 | 
			
		||||
 | 
			
		||||
    VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]};
 | 
			
		||||
    VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
 | 
			
		||||
    submitInfo.waitSemaphoreCount = 1;
 | 
			
		||||
    submitInfo.pWaitSemaphores = waitSemaphores;
 | 
			
		||||
    submitInfo.pWaitDstStageMask = waitStages;
 | 
			
		||||
 | 
			
		||||
    submitInfo.commandBufferCount = 1;
 | 
			
		||||
    submitInfo.pCommandBuffers = buffers;
 | 
			
		||||
 | 
			
		||||
    VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]};
 | 
			
		||||
    submitInfo.signalSemaphoreCount = 1;
 | 
			
		||||
    submitInfo.pSignalSemaphores = signalSemaphores;
 | 
			
		||||
 | 
			
		||||
    vkResetFences(device.device(), 1, &inFlightFences[currentFrame]);
 | 
			
		||||
    if (vkQueueSubmit(device.graphicsQueue(), 1, &submitInfo, inFlightFences[currentFrame]) !=
 | 
			
		||||
        VK_SUCCESS)
 | 
			
		||||
    {
 | 
			
		||||
      throw std::runtime_error("failed to submit draw command buffer!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VkPresentInfoKHR presentInfo = {};
 | 
			
		||||
    presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
 | 
			
		||||
 | 
			
		||||
    presentInfo.waitSemaphoreCount = 1;
 | 
			
		||||
    presentInfo.pWaitSemaphores = signalSemaphores;
 | 
			
		||||
 | 
			
		||||
    VkSwapchainKHR swapChains[] = {swapChain};
 | 
			
		||||
    presentInfo.swapchainCount = 1;
 | 
			
		||||
    presentInfo.pSwapchains = swapChains;
 | 
			
		||||
 | 
			
		||||
    presentInfo.pImageIndices = imageIndex;
 | 
			
		||||
 | 
			
		||||
    auto result = vkQueuePresentKHR(device.presentQueue(), &presentInfo);
 | 
			
		||||
 | 
			
		||||
    currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void SwapChain::createSwapChain()
 | 
			
		||||
  {
 | 
			
		||||
    SwapChainSupportDetails swapChainSupport = device.getSwapChainSupport();
 | 
			
		||||
 | 
			
		||||
    VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
 | 
			
		||||
    VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
 | 
			
		||||
    VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities);
 | 
			
		||||
 | 
			
		||||
    uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
 | 
			
		||||
    if (swapChainSupport.capabilities.maxImageCount > 0 &&
 | 
			
		||||
        imageCount > swapChainSupport.capabilities.maxImageCount)
 | 
			
		||||
    {
 | 
			
		||||
      imageCount = swapChainSupport.capabilities.maxImageCount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VkSwapchainCreateInfoKHR createInfo = {};
 | 
			
		||||
    createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
 | 
			
		||||
    createInfo.surface = device.surface();
 | 
			
		||||
 | 
			
		||||
    createInfo.minImageCount = imageCount;
 | 
			
		||||
    createInfo.imageFormat = surfaceFormat.format;
 | 
			
		||||
    createInfo.imageColorSpace = surfaceFormat.colorSpace;
 | 
			
		||||
    createInfo.imageExtent = extent;
 | 
			
		||||
    createInfo.imageArrayLayers = 1;
 | 
			
		||||
    createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
 | 
			
		||||
 | 
			
		||||
    QueueFamilyIndices indices = device.findPhysicalQueueFamilies();
 | 
			
		||||
    uint32_t queueFamilyIndices[] = {indices.graphicsFamily, indices.presentFamily};
 | 
			
		||||
 | 
			
		||||
    if (indices.graphicsFamily != indices.presentFamily)
 | 
			
		||||
    {
 | 
			
		||||
      createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
 | 
			
		||||
      createInfo.queueFamilyIndexCount = 2;
 | 
			
		||||
      createInfo.pQueueFamilyIndices = queueFamilyIndices;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
      createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
 | 
			
		||||
      createInfo.queueFamilyIndexCount = 0;     // Optional
 | 
			
		||||
      createInfo.pQueueFamilyIndices = nullptr; // Optional
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
 | 
			
		||||
    createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
 | 
			
		||||
 | 
			
		||||
    createInfo.presentMode = presentMode;
 | 
			
		||||
    createInfo.clipped = VK_TRUE;
 | 
			
		||||
 | 
			
		||||
    createInfo.oldSwapchain = VK_NULL_HANDLE;
 | 
			
		||||
 | 
			
		||||
    if (vkCreateSwapchainKHR(device.device(), &createInfo, nullptr, &swapChain) != VK_SUCCESS)
 | 
			
		||||
    {
 | 
			
		||||
      throw std::runtime_error("failed to create swap chain!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // we only specified a minimum number of images in the swap chain, so the implementation is
 | 
			
		||||
    // allowed to create a swap chain with more. That's why we'll first query the final number of
 | 
			
		||||
    // images with vkGetSwapchainImagesKHR, then resize the container and finally call it again to
 | 
			
		||||
    // retrieve the handles.
 | 
			
		||||
    vkGetSwapchainImagesKHR(device.device(), swapChain, &imageCount, nullptr);
 | 
			
		||||
    swapChainImages.resize(imageCount);
 | 
			
		||||
    vkGetSwapchainImagesKHR(device.device(), swapChain, &imageCount, swapChainImages.data());
 | 
			
		||||
 | 
			
		||||
    swapChainImageFormat = surfaceFormat.format;
 | 
			
		||||
    swapChainExtent = extent;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void SwapChain::createImageViews()
 | 
			
		||||
  {
 | 
			
		||||
    swapChainImageViews.resize(swapChainImages.size());
 | 
			
		||||
    for (size_t i = 0; i < swapChainImages.size(); i++)
 | 
			
		||||
    {
 | 
			
		||||
      VkImageViewCreateInfo viewInfo{};
 | 
			
		||||
      viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
 | 
			
		||||
      viewInfo.image = swapChainImages[i];
 | 
			
		||||
      viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
 | 
			
		||||
      viewInfo.format = swapChainImageFormat;
 | 
			
		||||
      viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
 | 
			
		||||
      viewInfo.subresourceRange.baseMipLevel = 0;
 | 
			
		||||
      viewInfo.subresourceRange.levelCount = 1;
 | 
			
		||||
      viewInfo.subresourceRange.baseArrayLayer = 0;
 | 
			
		||||
      viewInfo.subresourceRange.layerCount = 1;
 | 
			
		||||
 | 
			
		||||
      if (vkCreateImageView(device.device(), &viewInfo, nullptr, &swapChainImageViews[i]) !=
 | 
			
		||||
          VK_SUCCESS)
 | 
			
		||||
      {
 | 
			
		||||
        throw std::runtime_error("failed to create texture image view!");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void SwapChain::createRenderPass()
 | 
			
		||||
  {
 | 
			
		||||
    VkAttachmentDescription depthAttachment{};
 | 
			
		||||
    depthAttachment.format = findDepthFormat();
 | 
			
		||||
    depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
 | 
			
		||||
    depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
 | 
			
		||||
    depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
 | 
			
		||||
    depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
 | 
			
		||||
    depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
 | 
			
		||||
    depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
 | 
			
		||||
    depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 | 
			
		||||
 | 
			
		||||
    VkAttachmentReference depthAttachmentRef{};
 | 
			
		||||
    depthAttachmentRef.attachment = 1;
 | 
			
		||||
    depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 | 
			
		||||
 | 
			
		||||
    VkAttachmentDescription colorAttachment = {};
 | 
			
		||||
    colorAttachment.format = getSwapChainImageFormat();
 | 
			
		||||
    colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
 | 
			
		||||
    colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
 | 
			
		||||
    colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
 | 
			
		||||
    colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
 | 
			
		||||
    colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
 | 
			
		||||
    colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
 | 
			
		||||
    colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
 | 
			
		||||
 | 
			
		||||
    VkAttachmentReference colorAttachmentRef = {};
 | 
			
		||||
    colorAttachmentRef.attachment = 0;
 | 
			
		||||
    colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 | 
			
		||||
 | 
			
		||||
    VkSubpassDescription subpass = {};
 | 
			
		||||
    subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
 | 
			
		||||
    subpass.colorAttachmentCount = 1;
 | 
			
		||||
    subpass.pColorAttachments = &colorAttachmentRef;
 | 
			
		||||
    subpass.pDepthStencilAttachment = &depthAttachmentRef;
 | 
			
		||||
 | 
			
		||||
    VkSubpassDependency dependency = {};
 | 
			
		||||
    dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
 | 
			
		||||
    dependency.srcAccessMask = 0;
 | 
			
		||||
    dependency.srcStageMask =
 | 
			
		||||
        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
 | 
			
		||||
    dependency.dstSubpass = 0;
 | 
			
		||||
    dependency.dstStageMask =
 | 
			
		||||
        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
 | 
			
		||||
    dependency.dstAccessMask =
 | 
			
		||||
        VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
 | 
			
		||||
 | 
			
		||||
    std::array<VkAttachmentDescription, 2> attachments = {colorAttachment, depthAttachment};
 | 
			
		||||
    VkRenderPassCreateInfo renderPassInfo = {};
 | 
			
		||||
    renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
 | 
			
		||||
    renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
 | 
			
		||||
    renderPassInfo.pAttachments = attachments.data();
 | 
			
		||||
    renderPassInfo.subpassCount = 1;
 | 
			
		||||
    renderPassInfo.pSubpasses = &subpass;
 | 
			
		||||
    renderPassInfo.dependencyCount = 1;
 | 
			
		||||
    renderPassInfo.pDependencies = &dependency;
 | 
			
		||||
 | 
			
		||||
    if (vkCreateRenderPass(device.device(), &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS)
 | 
			
		||||
    {
 | 
			
		||||
      throw std::runtime_error("failed to create render pass!");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void SwapChain::createFramebuffers()
 | 
			
		||||
  {
 | 
			
		||||
    swapChainFramebuffers.resize(imageCount());
 | 
			
		||||
    for (size_t i = 0; i < imageCount(); i++)
 | 
			
		||||
    {
 | 
			
		||||
      std::array<VkImageView, 2> attachments = {swapChainImageViews[i], depthImageViews[i]};
 | 
			
		||||
 | 
			
		||||
      VkExtent2D swapChainExtent = getSwapChainExtent();
 | 
			
		||||
      VkFramebufferCreateInfo framebufferInfo = {};
 | 
			
		||||
      framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
 | 
			
		||||
      framebufferInfo.renderPass = renderPass;
 | 
			
		||||
      framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
 | 
			
		||||
      framebufferInfo.pAttachments = attachments.data();
 | 
			
		||||
      framebufferInfo.width = swapChainExtent.width;
 | 
			
		||||
      framebufferInfo.height = swapChainExtent.height;
 | 
			
		||||
      framebufferInfo.layers = 1;
 | 
			
		||||
 | 
			
		||||
      if (vkCreateFramebuffer(
 | 
			
		||||
              device.device(),
 | 
			
		||||
              &framebufferInfo,
 | 
			
		||||
              nullptr,
 | 
			
		||||
              &swapChainFramebuffers[i]) != VK_SUCCESS)
 | 
			
		||||
      {
 | 
			
		||||
        throw std::runtime_error("failed to create framebuffer!");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void SwapChain::createDepthResources()
 | 
			
		||||
  {
 | 
			
		||||
    VkFormat depthFormat = findDepthFormat();
 | 
			
		||||
    VkExtent2D swapChainExtent = getSwapChainExtent();
 | 
			
		||||
 | 
			
		||||
    depthImages.resize(imageCount());
 | 
			
		||||
    depthImageMemorys.resize(imageCount());
 | 
			
		||||
    depthImageViews.resize(imageCount());
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < depthImages.size(); i++)
 | 
			
		||||
    {
 | 
			
		||||
      VkImageCreateInfo imageInfo{};
 | 
			
		||||
      imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
 | 
			
		||||
      imageInfo.imageType = VK_IMAGE_TYPE_2D;
 | 
			
		||||
      imageInfo.extent.width = swapChainExtent.width;
 | 
			
		||||
      imageInfo.extent.height = swapChainExtent.height;
 | 
			
		||||
      imageInfo.extent.depth = 1;
 | 
			
		||||
      imageInfo.mipLevels = 1;
 | 
			
		||||
      imageInfo.arrayLayers = 1;
 | 
			
		||||
      imageInfo.format = depthFormat;
 | 
			
		||||
      imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
 | 
			
		||||
      imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
 | 
			
		||||
      imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
 | 
			
		||||
      imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
 | 
			
		||||
      imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
 | 
			
		||||
      imageInfo.flags = 0;
 | 
			
		||||
 | 
			
		||||
      device.createImageWithInfo(
 | 
			
		||||
          imageInfo,
 | 
			
		||||
          VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
 | 
			
		||||
          depthImages[i],
 | 
			
		||||
          depthImageMemorys[i]);
 | 
			
		||||
 | 
			
		||||
      VkImageViewCreateInfo viewInfo{};
 | 
			
		||||
      viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
 | 
			
		||||
      viewInfo.image = depthImages[i];
 | 
			
		||||
      viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
 | 
			
		||||
      viewInfo.format = depthFormat;
 | 
			
		||||
      viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
 | 
			
		||||
      viewInfo.subresourceRange.baseMipLevel = 0;
 | 
			
		||||
      viewInfo.subresourceRange.levelCount = 1;
 | 
			
		||||
      viewInfo.subresourceRange.baseArrayLayer = 0;
 | 
			
		||||
      viewInfo.subresourceRange.layerCount = 1;
 | 
			
		||||
 | 
			
		||||
      if (vkCreateImageView(device.device(), &viewInfo, nullptr, &depthImageViews[i]) != VK_SUCCESS)
 | 
			
		||||
      {
 | 
			
		||||
        throw std::runtime_error("failed to create texture image view!");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void SwapChain::createSyncObjects()
 | 
			
		||||
  {
 | 
			
		||||
    imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
 | 
			
		||||
    renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
 | 
			
		||||
    inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
 | 
			
		||||
    imagesInFlight.resize(imageCount(), VK_NULL_HANDLE);
 | 
			
		||||
 | 
			
		||||
    VkSemaphoreCreateInfo semaphoreInfo = {};
 | 
			
		||||
    semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
 | 
			
		||||
 | 
			
		||||
    VkFenceCreateInfo fenceInfo = {};
 | 
			
		||||
    fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
 | 
			
		||||
    fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
 | 
			
		||||
    {
 | 
			
		||||
      if (vkCreateSemaphore(device.device(), &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) !=
 | 
			
		||||
              VK_SUCCESS ||
 | 
			
		||||
          vkCreateSemaphore(device.device(), &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) !=
 | 
			
		||||
              VK_SUCCESS ||
 | 
			
		||||
          vkCreateFence(device.device(), &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS)
 | 
			
		||||
      {
 | 
			
		||||
        throw std::runtime_error("failed to create synchronization objects for a frame!");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  VkSurfaceFormatKHR SwapChain::chooseSwapSurfaceFormat(
 | 
			
		||||
      const std::vector<VkSurfaceFormatKHR> &availableFormats)
 | 
			
		||||
  {
 | 
			
		||||
    for (const auto &availableFormat : availableFormats)
 | 
			
		||||
    {
 | 
			
		||||
      if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB &&
 | 
			
		||||
          availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
 | 
			
		||||
      {
 | 
			
		||||
        return availableFormat;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return availableFormats[0];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  VkPresentModeKHR SwapChain::chooseSwapPresentMode(
 | 
			
		||||
      const std::vector<VkPresentModeKHR> &availablePresentModes)
 | 
			
		||||
  {
 | 
			
		||||
    for (const auto &availablePresentMode : availablePresentModes)
 | 
			
		||||
    {
 | 
			
		||||
      if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR)
 | 
			
		||||
      {
 | 
			
		||||
        std::cout << "Present mode: Mailbox" << std::endl;
 | 
			
		||||
        return availablePresentMode;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // for (const auto &availablePresentMode : availablePresentModes) {
 | 
			
		||||
    //   if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) {
 | 
			
		||||
    //     std::cout << "Present mode: Immediate" << std::endl;
 | 
			
		||||
    //     return availablePresentMode;
 | 
			
		||||
    //   }
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    std::cout << "Present mode: V-Sync" << std::endl;
 | 
			
		||||
    return VK_PRESENT_MODE_FIFO_KHR;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  VkExtent2D SwapChain::chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities)
 | 
			
		||||
  {
 | 
			
		||||
    if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max())
 | 
			
		||||
    {
 | 
			
		||||
      return capabilities.currentExtent;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
      VkExtent2D actualExtent = windowExtent;
 | 
			
		||||
      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;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  VkFormat SwapChain::findDepthFormat()
 | 
			
		||||
  {
 | 
			
		||||
    return device.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);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
} // namespace hk
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,83 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "hk_device.hpp"
 | 
			
		||||
 | 
			
		||||
// vulkan headers
 | 
			
		||||
#include <vulkan/vulkan.h>
 | 
			
		||||
 | 
			
		||||
// std lib headers
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace hk
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  class SwapChain
 | 
			
		||||
  {
 | 
			
		||||
  public:
 | 
			
		||||
    static constexpr int MAX_FRAMES_IN_FLIGHT = 2;
 | 
			
		||||
 | 
			
		||||
    SwapChain(Device &deviceRef, VkExtent2D windowExtent);
 | 
			
		||||
    ~SwapChain();
 | 
			
		||||
 | 
			
		||||
    SwapChain(const SwapChain &) = delete;
 | 
			
		||||
    SwapChain &operator=(const SwapChain &) = delete;
 | 
			
		||||
 | 
			
		||||
    VkFramebuffer getFrameBuffer(int index) { return swapChainFramebuffers[index]; }
 | 
			
		||||
    VkRenderPass getRenderPass() { return renderPass; }
 | 
			
		||||
    VkImageView getImageView(int index) { return swapChainImageViews[index]; }
 | 
			
		||||
    size_t imageCount() { return swapChainImages.size(); }
 | 
			
		||||
    VkFormat getSwapChainImageFormat() { return swapChainImageFormat; }
 | 
			
		||||
    VkExtent2D getSwapChainExtent() { return swapChainExtent; }
 | 
			
		||||
    uint32_t width() { return swapChainExtent.width; }
 | 
			
		||||
    uint32_t height() { return swapChainExtent.height; }
 | 
			
		||||
 | 
			
		||||
    float extentAspectRatio()
 | 
			
		||||
    {
 | 
			
		||||
      return static_cast<float>(swapChainExtent.width) / static_cast<float>(swapChainExtent.height);
 | 
			
		||||
    }
 | 
			
		||||
    VkFormat findDepthFormat();
 | 
			
		||||
 | 
			
		||||
    VkResult acquireNextImage(uint32_t *imageIndex);
 | 
			
		||||
    VkResult submitCommandBuffers(const VkCommandBuffer *buffers, uint32_t *imageIndex);
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    void createSwapChain();
 | 
			
		||||
    void createImageViews();
 | 
			
		||||
    void createDepthResources();
 | 
			
		||||
    void createRenderPass();
 | 
			
		||||
    void createFramebuffers();
 | 
			
		||||
    void createSyncObjects();
 | 
			
		||||
 | 
			
		||||
    // Helper functions
 | 
			
		||||
    VkSurfaceFormatKHR chooseSwapSurfaceFormat(
 | 
			
		||||
        const std::vector<VkSurfaceFormatKHR> &availableFormats);
 | 
			
		||||
    VkPresentModeKHR chooseSwapPresentMode(
 | 
			
		||||
        const std::vector<VkPresentModeKHR> &availablePresentModes);
 | 
			
		||||
    VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities);
 | 
			
		||||
 | 
			
		||||
    VkFormat swapChainImageFormat;
 | 
			
		||||
    VkExtent2D swapChainExtent;
 | 
			
		||||
 | 
			
		||||
    std::vector<VkFramebuffer> swapChainFramebuffers;
 | 
			
		||||
    VkRenderPass renderPass;
 | 
			
		||||
 | 
			
		||||
    std::vector<VkImage> depthImages;
 | 
			
		||||
    std::vector<VkDeviceMemory> depthImageMemorys;
 | 
			
		||||
    std::vector<VkImageView> depthImageViews;
 | 
			
		||||
    std::vector<VkImage> swapChainImages;
 | 
			
		||||
    std::vector<VkImageView> swapChainImageViews;
 | 
			
		||||
 | 
			
		||||
    Device &device;
 | 
			
		||||
    VkExtent2D windowExtent;
 | 
			
		||||
 | 
			
		||||
    VkSwapchainKHR swapChain;
 | 
			
		||||
 | 
			
		||||
    std::vector<VkSemaphore> imageAvailableSemaphores;
 | 
			
		||||
    std::vector<VkSemaphore> renderFinishedSemaphores;
 | 
			
		||||
    std::vector<VkFence> inFlightFences;
 | 
			
		||||
    std::vector<VkFence> imagesInFlight;
 | 
			
		||||
    size_t currentFrame = 0;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
} // namespace hk
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
#include "hk_window.hpp"
 | 
			
		||||
 | 
			
		||||
// std
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
 | 
			
		||||
namespace hk
 | 
			
		||||
{
 | 
			
		||||
    Window::Window(int w, int h, const std::string& name)
 | 
			
		||||
         : m_width(w), m_height(h), m_windowName(name)
 | 
			
		||||
    {
 | 
			
		||||
        initWindow();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Window::~Window()
 | 
			
		||||
    {
 | 
			
		||||
        glfwDestroyWindow(m_window);
 | 
			
		||||
        glfwTerminate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Window::createWindowSurface(VkInstance instance, VkSurfaceKHR *surface)
 | 
			
		||||
    {
 | 
			
		||||
        if (glfwCreateWindowSurface(instance, m_window, nullptr, surface) != VK_SUCCESS)
 | 
			
		||||
        {
 | 
			
		||||
            throw std::runtime_error("failed to create window surface!");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Window::initWindow()
 | 
			
		||||
    {
 | 
			
		||||
        glfwInit();
 | 
			
		||||
        glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
 | 
			
		||||
        glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
 | 
			
		||||
 | 
			
		||||
        m_window = glfwCreateWindow(m_width, m_height, m_windowName.c_str(), nullptr, nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,34 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#define GLFW_INCLUDE_VULKAN
 | 
			
		||||
#include <GLFW/glfw3.h>
 | 
			
		||||
 | 
			
		||||
namespace hk
 | 
			
		||||
{
 | 
			
		||||
    class Window
 | 
			
		||||
    {
 | 
			
		||||
        
 | 
			
		||||
    public:
 | 
			
		||||
        Window(int w, int h, const std::string& name);
 | 
			
		||||
        ~Window();
 | 
			
		||||
 | 
			
		||||
        Window(const Window&) = delete;
 | 
			
		||||
        Window &operator=(const Window&) = delete;
 | 
			
		||||
 | 
			
		||||
        bool shouldClose() { return glfwWindowShouldClose(m_window); }
 | 
			
		||||
        VkExtent2D getExtend() { return {static_cast<uint32_t>(m_width), static_cast<uint32_t>(m_height)}; }
 | 
			
		||||
 | 
			
		||||
        void createWindowSurface(VkInstance instance, VkSurfaceKHR *surface);
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        void initWindow();
 | 
			
		||||
 | 
			
		||||
        const int m_width;
 | 
			
		||||
        const int m_height;
 | 
			
		||||
 | 
			
		||||
        std::string m_windowName;
 | 
			
		||||
        GLFWwindow *m_window;
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
} // namespace lve
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
#include "first_app.hpp"
 | 
			
		||||
 | 
			
		||||
// std
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
 | 
			
		||||
int main()
 | 
			
		||||
{
 | 
			
		||||
    hk::FirstApp app{};
 | 
			
		||||
 | 
			
		||||
    try
 | 
			
		||||
    {
 | 
			
		||||
        app.run();
 | 
			
		||||
    }
 | 
			
		||||
    catch(const std::exception& e)
 | 
			
		||||
    {
 | 
			
		||||
        std::cerr << e.what() << '\n';
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
#version 450
 | 
			
		||||
 | 
			
		||||
layout (location = 0) in vec3 fragColor;
 | 
			
		||||
layout (location = 0) out vec4 outColor;
 | 
			
		||||
 | 
			
		||||
void main(){
 | 
			
		||||
    outColor = vec4(fragColor, 1.0);
 | 
			
		||||
}
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
#version 450
 | 
			
		||||
 | 
			
		||||
layout(location = 0) in vec2 position;
 | 
			
		||||
layout(location = 1) in vec3 color;
 | 
			
		||||
 | 
			
		||||
layout(location = 0) out vec3 fragColor;
 | 
			
		||||
 | 
			
		||||
void main(){
 | 
			
		||||
    gl_Position = vec4(position, 0.0, 1.0);
 | 
			
		||||
    fragColor = color;
 | 
			
		||||
}
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							
		Loading…
	
		Reference in New Issue