Add vertex, index and uniform buffers

This commit is contained in:
Thraix
2023-01-11 19:18:16 +01:00
parent 0396318d2d
commit fa207c591c
13 changed files with 1208 additions and 687 deletions
+5
View File
@@ -150,10 +150,15 @@
<ClCompile Include="src\main.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\Buffer.h" />
<ClInclude Include="src\Common.h" />
<ClInclude Include="src\DebugMessenger.h" />
<ClInclude Include="src\FileSystem.h" />
<ClInclude Include="src\Instance.h" />
<ClInclude Include="src\QueueFamilies.h" />
<ClInclude Include="src\SwapChain.h" />
<ClInclude Include="src\VulkanException.h" />
<ClInclude Include="src\Window.h" />
</ItemGroup>
<ItemGroup>
<None Include="res\shaders\compile.bat" />
+15
View File
@@ -32,6 +32,21 @@
<ClInclude Include="src\SwapChain.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\QueueFamilies.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\Buffer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\VulkanException.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\Instance.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\Window.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="res\shaders\shader.frag" />
+12 -14
View File
@@ -1,20 +1,18 @@
#version 450
layout(binding = 0) uniform UniformBufferObject
{
mat4 model;
mat4 view;
mat4 projection;
} ubo;
layout(location = 0) in vec2 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 0) out vec3 fragColor;
vec2 positions[3] = vec2[](
vec2(0.0, -0.5),
vec2(0.5, 0.5),
vec2(-0.5, 0.5)
);
vec3 colors[3] = vec3[](
vec3(1.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0),
vec3(0.0, 0.0, 1.0)
);
void main() {
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
fragColor = colors[gl_VertexIndex];
gl_Position = ubo.projection * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0);
fragColor = inColor;
}
Binary file not shown.
+158
View File
@@ -0,0 +1,158 @@
#pragma once
#include "Common.h"
#include "Instance.h"
#include <optional>
#include <vulkan/vulkan.hpp>
class Buffer
{
private:
Instance& instance;
VkDeviceMemory memory;
VkBuffer handle;
VkDeviceSize size;
int count;
void* mappedData = nullptr;
public:
Buffer(Instance& instance, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkDeviceSize size, int count)
: instance{instance}, size{size}, count{count}
{
VkBufferCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
createInfo.size = size * (VkDeviceSize)count;
createInfo.usage = usage;
createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VK_ASSERT(vkCreateBuffer(instance.GetDevice(), &createInfo, nullptr, &handle), "Failed to initialize buffer");
VkMemoryRequirements memoryRequirements;
vkGetBufferMemoryRequirements(instance.GetDevice(), handle, &memoryRequirements);
VkMemoryAllocateInfo allocateInfo{};
allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocateInfo.allocationSize = memoryRequirements.size;
allocateInfo.memoryTypeIndex = FindMemoryType(instance, memoryRequirements.memoryTypeBits, properties);
VK_ASSERT(vkAllocateMemory(instance.GetDevice(), &allocateInfo, nullptr, &memory), "Failed to allocate buffer memory");
vkBindBufferMemory(instance.GetDevice(), handle, memory, 0);
}
~Buffer()
{
vkFreeMemory(instance.GetDevice(), memory, nullptr);
vkDestroyBuffer(instance.GetDevice(), handle, nullptr);
}
void Update(void* indexData, int index)
{
ASSERT(index >= 0 && index < count, "instance is outside of the buffer");
if (mappedData == nullptr)
{
void* data;
vkMapMemory(instance.GetDevice(), memory, index * size, size, 0, &data);
memcpy(data, indexData, size);
vkUnmapMemory(instance.GetDevice(), memory);
}
else
{
memcpy((char*)mappedData + index * size, indexData, size);
}
}
void Map()
{
ASSERT(mappedData == nullptr, "Mapping an already mapped buffer")
vkMapMemory(instance.GetDevice(), memory, 0, size * count, 0, &mappedData);
}
void Unmap()
{
ASSERT(mappedData != nullptr, "Unmapping an already unmapped buffer")
vkUnmapMemory(instance.GetDevice(), memory);
mappedData = nullptr;
}
VkBuffer GetHandle() const
{
return handle;
}
VkDescriptorBufferInfo GetDescriptorBufferInfo(int instance)
{
VkDescriptorBufferInfo bufferInfo{};
bufferInfo.buffer = handle;
bufferInfo.offset = (VkDeviceSize)instance * size;
bufferInfo.range = size;
return bufferInfo;
}
VkDeviceSize GetSize() const
{
return size;
}
VkDeviceSize GetPosition(int index) const
{
ASSERT(index >= 0 && index < count, "Instance is outside of the buffer");
return size * (VkDeviceSize)index;
}
static void CopyBuffer(Instance& instance, const Buffer& srcBuffer, const Buffer& dstBuffer)
{
ASSERT(srcBuffer.size == dstBuffer.size && srcBuffer.count == dstBuffer.count, "Buffers have different sizes");
VkCommandBufferAllocateInfo allocateInfo{};
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocateInfo.commandPool = instance.GetCommandPool();
allocateInfo.commandBufferCount = 1;
VkCommandBuffer commandBuffer;
VK_ASSERT(vkAllocateCommandBuffers(instance.GetDevice(), &allocateInfo, &commandBuffer), "Failed to initialize command buffer");
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(commandBuffer, &beginInfo);
VkBufferCopy bufferCopy{};
bufferCopy.dstOffset = 0;
bufferCopy.srcOffset = 0;
bufferCopy.size = srcBuffer.size * (VkDeviceSize)srcBuffer.count;
vkCmdCopyBuffer(commandBuffer, srcBuffer.GetHandle(), dstBuffer.GetHandle(), 1, &bufferCopy);
vkEndCommandBuffer(commandBuffer);
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
vkQueueSubmit(instance.GetGraphicsQueue(), 1, &submitInfo, VK_NULL_HANDLE);
vkQueueWaitIdle(instance.GetGraphicsQueue());
vkFreeCommandBuffers(instance.GetDevice(), instance.GetCommandPool(), 1, &commandBuffer);
}
private:
static uint32_t FindMemoryType(Instance& instance, uint32_t typeFilter, VkMemoryPropertyFlags properties)
{
VkPhysicalDeviceMemoryProperties memoryProperties;
vkGetPhysicalDeviceMemoryProperties(instance.GetPhysicalDevice(), &memoryProperties);
for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; ++i)
{
if ((typeFilter & (1 << i)) && (memoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
return i;
}
throw std::runtime_error("Failed to find suitable memory type");
}
};
+2 -1
View File
@@ -1,10 +1,11 @@
#pragma once
#include "VulkanException.h"
#include <vulkan/vulkan.hpp>
#include <iostream>
#define ASSERT(Function, message) if(!(Function)) { throw std::runtime_error(message); } while(false)
#define VK_ASSERT(Function, message) if(Function != VK_SUCCESS) { throw std::runtime_error(message); } while(false)
#define VK_ASSERT(Function, message) if(Function != VK_SUCCESS) { throw VulkanException(message); } while(false)
VkResult vkCreateDebugUtilsMessengerEXT(VkInstance instance,
const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
+4 -2
View File
@@ -38,14 +38,14 @@ public:
DebugMessenger& operator=(DebugMessenger&&) = delete;
DebugMessenger& operator=(const DebugMessenger&) = delete;
void AddRequiredExtensions(std::vector<const char*>* extensions)
static void AddRequiredExtensions(std::vector<const char*>* extensions)
{
#ifndef NDEBUG
extensions->emplace_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
#endif
}
void AddRequiredLayers(std::vector<const char*>* layers)
static void AddRequiredLayers(std::vector<const char*>* layers)
{
#ifndef NDEBUG
layers->emplace_back("VK_LAYER_KHRONOS_validation");
@@ -61,6 +61,8 @@ private:
if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
{
std::cerr << pCallbackData->pMessage << std::endl;
if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
throw VulkanException(pCallbackData->pMessage);
}
return VK_FALSE;
}
+359
View File
@@ -0,0 +1,359 @@
#pragma once
#include <vulkan/vulkan.hpp>
#include <set>
#include "DebugMessenger.h"
#include "QueueFamilies.h"
#include "SwapChain.h"
class Instance final
{
private:
static const int MAX_FRAMES_IN_FLIGHT = 2;
static const int WINDOW_WIDTH = 1920;
static const int WINDOW_HEIGHT = 1080;
VkInstance instance;
GLFWwindow* window;
VkSurfaceKHR surface;
std::unique_ptr<DebugMessenger> debugMessenger;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
VkDevice device;
VkQueue graphicsQueue;
VkQueue presentQueue;
std::unique_ptr<SwapChain> swapChain;
VkCommandPool commandPool;
bool framebufferResized = false;
public:
Instance(const std::string& applicationName)
{
InitializeWindow(applicationName);
InitializeInstance(applicationName);
InitializeDebugMessenger();
InitializeSurface();
SelectPhysicalDevice();
InitializeLogicalDevice();
InitializeSwapChain();
InitializeCommandPool();
}
~Instance()
{
vkDestroyCommandPool(device, commandPool, nullptr);
swapChain.reset();
vkDestroyDevice(device, nullptr);
vkDestroySurfaceKHR(instance, surface, nullptr);
debugMessenger.reset();
vkDestroyInstance(instance, nullptr);
glfwDestroyWindow(window);
}
bool BeginPresent()
{
if (!swapChain->BeginPresent())
return true;
}
bool EndPresent()
{
swapChain->EndPresent(presentQueue, framebufferResized);
return !glfwWindowShouldClose(window);
}
void SubmitGraphicsQueue(const std::vector<VkCommandBuffer>& commandBuffers)
{
VkSemaphore waitSemaphores[] = {swapChain->GetAvailableImageSemaphore()};
VkSemaphore signalSemaphores[] = {swapChain->GetRenderFinishedSemaphore()};
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = commandBuffers.size();
submitInfo.pCommandBuffers = commandBuffers.data();
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
VK_ASSERT(vkQueueSubmit(graphicsQueue, 1, &submitInfo, swapChain->GetInFlightFence()), "Failed to submit command buffer");
}
VkInstance GetInstance() const
{
return instance;
}
VkPhysicalDevice GetPhysicalDevice() const
{
return physicalDevice;
}
VkDevice GetDevice() const
{
return device;
}
VkCommandPool GetCommandPool() const
{
return commandPool;
}
VkQueue GetGraphicsQueue() const
{
return graphicsQueue;
}
int GetMaxFramesInFlight()
{
return MAX_FRAMES_IN_FLIGHT;
}
const SwapChain& GetSwapChain() const
{
return *swapChain;
}
private:
void InitializeWindow(const std::string& applicationName)
{
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, applicationName.c_str(), nullptr, nullptr);
glfwSetWindowUserPointer(window, this);
glfwSetFramebufferSizeCallback(window, FramebufferResizeCallback);
}
void InitializeInstance(const std::string& applicationName)
{
VkApplicationInfo appInfo{};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = applicationName.c_str();
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "Greet Engine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_1;
std::vector<const char*> requiredExtensions = GetRequiredExtensions();
uint32_t extensionCount;
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
std::vector<VkExtensionProperties> extensions{extensionCount};
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
std::cout << "Supported Extensions: " << std::endl;
for (auto&& extension : extensions)
{
std::cout << "\t" << extension.extensionName << std::endl;
}
std::vector<const char*> layers{};
DebugMessenger::AddRequiredLayers(&layers);
ASSERT(CheckLayerSupport(layers), "Some required layers are not supported");
VkInstanceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
createInfo.enabledExtensionCount = requiredExtensions.size();
createInfo.ppEnabledExtensionNames = requiredExtensions.data();
createInfo.enabledLayerCount = layers.size();
createInfo.ppEnabledLayerNames = layers.data();
VK_ASSERT(vkCreateInstance(&createInfo, nullptr, &instance), "Failed to create instance");
}
void InitializeDebugMessenger()
{
debugMessenger = std::make_unique<DebugMessenger>(instance);
}
void InitializeSurface()
{
VK_ASSERT(glfwCreateWindowSurface(instance, window, nullptr, &surface), "Failed to create Vulkan surface");
}
void SelectPhysicalDevice()
{
uint32_t deviceCount;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
ASSERT(deviceCount != 0, "No available devices support Vulkan");
std::vector<VkPhysicalDevice> devices(deviceCount);
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
std::cout << "Available devices: " << std::endl;
for (auto&& device : devices)
{
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(device, &deviceProperties);
std::cout << "\t" << deviceProperties.deviceName << std::endl;
}
for (auto&& device : devices)
{
if (IsPhysicalDeviceSuitable(device))
{
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(device, &deviceProperties);
physicalDevice = device;
std::cout << "Selecting device: " << deviceProperties.deviceName << std::endl;
break;
}
}
ASSERT(physicalDevice != VK_NULL_HANDLE, "Failed to find suitable GPU");
}
void InitializeLogicalDevice()
{
QueueFamilies queueFamilies{surface, physicalDevice};
float queuePriority = 1.0f;
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos{};
std::set<uint32_t> uniqueQueueFamilies{queueFamilies.graphicsFamily.value(), queueFamilies.presentFamily.value()};
for(auto&& queueFamily : uniqueQueueFamilies)
{
VkDeviceQueueCreateInfo queueCreateInfo{};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = queueFamily;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &queuePriority;
queueCreateInfos.emplace_back(queueCreateInfo);
}
std::vector<const char*> deviceExtensions = GetRequiredDeviceExtensions();
VkPhysicalDeviceFeatures deviceFeatures{};
deviceFeatures.fillModeNonSolid = VK_TRUE;
VkDeviceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.pQueueCreateInfos = queueCreateInfos.data();
createInfo.queueCreateInfoCount = queueCreateInfos.size();
createInfo.pEnabledFeatures = &deviceFeatures;
createInfo.ppEnabledExtensionNames = deviceExtensions.data();
createInfo.enabledExtensionCount = deviceExtensions.size();
VK_ASSERT(vkCreateDevice(physicalDevice, &createInfo, nullptr, &device), "Failed to initialize logical device");
vkGetDeviceQueue(device, queueFamilies.graphicsFamily.value(), 0, &graphicsQueue);
vkGetDeviceQueue(device, queueFamilies.presentFamily.value(), 0, &presentQueue);
}
void InitializeSwapChain()
{
swapChain = std::make_unique<SwapChain>(window, surface, device, physicalDevice);
}
void InitializeCommandPool()
{
QueueFamilies queueFamilies{surface, physicalDevice};
VkCommandPoolCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
createInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
createInfo.queueFamilyIndex = queueFamilies.graphicsFamily.value();
VK_ASSERT(vkCreateCommandPool(device, &createInfo, nullptr, &commandPool), "Failed to initialize command pool");
}
std::vector<const char*> GetRequiredExtensions()
{
uint32_t glfwExtensionCount;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
std::vector<const char*> extensions{glfwExtensions, glfwExtensions + glfwExtensionCount};
debugMessenger->AddRequiredExtensions(&extensions);
return extensions;
}
bool CheckLayerSupport(const std::vector<const char*>& layers)
{
uint32_t layerCount;
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
std::vector<VkLayerProperties> availableLayers(layerCount);
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
std::cout << "Supported Layers: " << std::endl;
for (auto&& availableLayer : availableLayers)
{
std::cout << "\t" << availableLayer.layerName << std::endl;
}
for (auto&& layer : layers)
{
bool layerFound = false;
for (auto&& availableLayer : availableLayers)
{
if (std::strcmp(layer, availableLayer.layerName) == 0)
{
layerFound = true;
break;
}
}
if (!layerFound)
return false;
}
return true;
}
bool IsPhysicalDeviceSuitable(VkPhysicalDevice device)
{
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(device, &deviceProperties);
if (deviceProperties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
return false;
VkPhysicalDeviceFeatures deviceFeatures;
vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
if (!deviceFeatures.fillModeNonSolid)
return false;
QueueFamilies queueFamilies{surface, device};
if (!queueFamilies.AllRequiredFamiliesSupported())
return false;
if (!CheckDeviceExtensionSupport(device))
return false;
SwapChainSupportDetails details{surface, device};
if (!details.Valid())
return false;
return true;
}
bool CheckDeviceExtensionSupport(VkPhysicalDevice device)
{
uint32_t extensionCount;
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
std::vector<VkExtensionProperties> extensions{extensionCount};
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, extensions.data());
for (auto&& requiredExtension : GetRequiredDeviceExtensions())
{
bool found = false;
for (auto&& extension : extensions)
{
if (std::strcmp(requiredExtension, extension.extensionName) == 0)
{
found = true;
break;
}
}
if (!found)
return false;
}
return true;
}
std::vector<const char*> GetRequiredDeviceExtensions()
{
return {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
}
static void FramebufferResizeCallback(GLFWwindow* window, int width, int height)
{
Instance* instance = static_cast<Instance*>(glfwGetWindowUserPointer(window));
instance->framebufferResized = true;
}
};
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include <vulkan/vulkan.h>
#include <optional>
#include <vector>
struct QueueFamilies
{
std::optional<uint32_t> graphicsFamily;
std::optional<uint32_t> presentFamily;
QueueFamilies(VkSurfaceKHR surface, VkPhysicalDevice device)
{
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
int i = 0;
for (auto&& queueFamily : queueFamilies)
{
if(queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
{
graphicsFamily = i;
}
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
if (presentSupport)
{
presentFamily = i;
}
i++;
}
}
bool AllRequiredFamiliesSupported()
{
return graphicsFamily.has_value() && presentFamily.has_value();
}
};
+394 -8
View File
@@ -1,20 +1,406 @@
#pragma once
#include "QueueFamilies.h"
#include <glfw/glfw3.h>
#include <vulkan/vulkan.h>
#include <vector>
class SwapChain
struct SwapChainSupportDetails
{
VkSwapchainKHR swapChain;
VkFormat swapChainImageFormat;
VkExtent2D swapChainExtent;
std::vector<VkImageView> swapChainImageViews;
std::vector<VkImage> swapChainImages;
std::vector<VkFramebuffer> swapChainFramebuffers;
VkSurfaceCapabilitiesKHR capabilities;
std::vector<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> presentModes;
SwapChain()
SwapChainSupportDetails(VkSurfaceKHR surface, VkPhysicalDevice device)
{
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &capabilities);
uint32_t formatCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
if (formatCount != 0)
{
formats.resize(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, formats.data());
}
uint32_t presentModeCount;
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
if (presentModeCount != 0)
{
presentModes.resize(presentModeCount);
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, presentModes.data());
}
}
bool Valid()
{
return !formats.empty() && !presentModes.empty();
}
};
class SwapChain
{
// TODO: Remove, replaced by Instance::MAX_FRAMES_IN_FLIGHT
static const int MAX_FRAMES_IN_FLIGHT = 2;
// Needed for recreation and destruction
GLFWwindow* window;
VkSurfaceKHR surface;
VkPhysicalDevice physicalDevice;
VkDevice device;
// Created by the class
VkSwapchainKHR handle;
VkRenderPass renderPass;
VkFormat imageFormat;
VkExtent2D extent;
std::vector<VkImageView> imageViews;
std::vector<VkImage> images;
std::vector<VkFramebuffer> framebuffers;
int flightIndex;
uint32_t imageIndex;
std::vector<VkSemaphore> imageAvailableSemaphores;
std::vector<VkSemaphore> renderFinishedSemaphores;
std::vector<VkFence> inFlightFences;
public:
SwapChain(GLFWwindow* window, VkSurfaceKHR surface, VkDevice device, VkPhysicalDevice physicalDevice)
: window{window}, surface{surface}, physicalDevice{physicalDevice}, device{device}
{
Initialize();
InitializeImageViews();
InitializeRenderPass();
InitializeFramebuffers();
InitializeSyncObjects();
}
~SwapChain()
{
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
{
vkDestroyFence(device, inFlightFences[i], nullptr);
vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
}
Destroy();
vkDestroyRenderPass(device, renderPass, nullptr);
}
VkSwapchainKHR GetHandle() const
{
return handle;
}
VkRenderPass GetRenderPass() const
{
return renderPass;
}
VkExtent2D GetExtent() const
{
return extent;
}
VkFramebuffer GetFramebuffer() const
{
return framebuffers[imageIndex];
}
bool BeginPresent()
{
vkWaitForFences(device, 1, &inFlightFences[flightIndex], VK_TRUE, UINT64_MAX);
VkResult result = vkAcquireNextImageKHR(device, handle, UINT64_MAX, imageAvailableSemaphores[flightIndex], VK_NULL_HANDLE, &imageIndex);
if (result == VK_ERROR_OUT_OF_DATE_KHR)
{
Recreate();
return false;
}
vkResetFences(device, 1, &inFlightFences[flightIndex]);
return true;
}
void EndPresent(VkQueue presentQueue, bool framebufferResized)
{
VkSwapchainKHR swapChains[] = {handle};
VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = &renderFinishedSemaphores[flightIndex];
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &handle;
presentInfo.pImageIndices = &imageIndex;
presentInfo.pResults = nullptr;
VkResult result = vkQueuePresentKHR(presentQueue, &presentInfo);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized)
{
framebufferResized = false;
Recreate();
}
flightIndex = (flightIndex + 1) % MAX_FRAMES_IN_FLIGHT;
}
int GetFlightIndex() const
{
return flightIndex;
}
int GetMaxFramesInFlight() const
{
return MAX_FRAMES_IN_FLIGHT;
}
VkFence GetInFlightFence() const
{
return inFlightFences[flightIndex];
}
VkSemaphore GetAvailableImageSemaphore() const
{
return imageAvailableSemaphores[flightIndex];
}
VkSemaphore GetRenderFinishedSemaphore() const
{
return renderFinishedSemaphores[flightIndex];
}
void Recreate()
{
int width = 0;
int height = 0;
glfwGetFramebufferSize(window, &width, &height);
while (width == 0 || height == 0)
{
glfwGetFramebufferSize(window, &width, &height);
glfwWaitEvents();
}
vkDeviceWaitIdle(device);
Destroy();
Initialize();
InitializeImageViews();
InitializeFramebuffers();
}
private:
void Initialize()
{
SwapChainSupportDetails swapChainSupport{surface, physicalDevice};
VkSurfaceFormatKHR format = SelectSwapSurfaceFormat(swapChainSupport.formats);
VkPresentModeKHR presentMode = SelectSwapPresentMode(swapChainSupport.presentModes);
extent = SelectSwapExtent(window, swapChainSupport.capabilities);
imageFormat = format.format;
uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
if (swapChainSupport.capabilities.maxImageCount != 0)
{
imageCount = std::min(imageCount, swapChainSupport.capabilities.maxImageCount);
}
QueueFamilies queueFamilies{surface, physicalDevice};
std::vector<uint32_t> queueFamilyIndices{queueFamilies.graphicsFamily.value(), queueFamilies.presentFamily.value()};
VkSwapchainCreateInfoKHR createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = surface;
createInfo.minImageCount = imageCount;
createInfo.imageFormat = format.format;
createInfo.imageColorSpace = format.colorSpace;
createInfo.imageExtent = extent;
createInfo.imageArrayLayers = 1;
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
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 (queueFamilies.graphicsFamily != queueFamilies.presentFamily)
{
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
createInfo.queueFamilyIndexCount = 2;
createInfo.pQueueFamilyIndices = queueFamilyIndices.data();
}
else
{
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 0;
createInfo.pQueueFamilyIndices = nullptr;
}
VK_ASSERT(vkCreateSwapchainKHR(device, &createInfo, nullptr, &handle), "Failed to initialize the swapchain");
vkGetSwapchainImagesKHR(device, handle, &imageCount, nullptr);
images.resize(imageCount);
vkGetSwapchainImagesKHR(device, handle, &imageCount, images.data());
}
void InitializeImageViews()
{
imageViews.resize(images.size());
for (size_t i = 0; i < images.size(); i++)
{
VkImageViewCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.image = images[i];
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
createInfo.format = imageFormat;
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
createInfo.subresourceRange.baseMipLevel = 0;
createInfo.subresourceRange.levelCount = 1;
createInfo.subresourceRange.baseArrayLayer = 0;
createInfo.subresourceRange.layerCount = 1;
VK_ASSERT(vkCreateImageView(device, &createInfo, nullptr, &imageViews[i]), "Failed to initialize swapchain image view");
}
}
void InitializeRenderPass()
{
VkAttachmentDescription colorAttachment{};
colorAttachment.format = imageFormat;
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_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;
VkSubpassDependency dependency{};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
VkRenderPassCreateInfo renderPassCreateInfo{};
renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassCreateInfo.attachmentCount = 1;
renderPassCreateInfo.pAttachments = &colorAttachment;
renderPassCreateInfo.subpassCount = 1;
renderPassCreateInfo.pSubpasses = &subpass;
renderPassCreateInfo.dependencyCount = 1;
renderPassCreateInfo.pDependencies = &dependency;
VK_ASSERT(vkCreateRenderPass(device, &renderPassCreateInfo, nullptr, &renderPass), "Failed to initialze render pass");
}
void InitializeFramebuffers()
{
framebuffers.resize(images.size());
for (size_t i = 0; i < imageViews.size(); ++i)
{
VkImageView attachments[] = {imageViews[i]};
VkFramebufferCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
createInfo.renderPass = renderPass;
createInfo.attachmentCount = 1;
createInfo.pAttachments = attachments;
createInfo.width = extent.width;
createInfo.height = extent.height;
createInfo.layers = 1;
VK_ASSERT(vkCreateFramebuffer(device, &createInfo, nullptr, &framebuffers[i]), "Failed to initialize swap chain framebuffer");
}
}
void InitializeSyncObjects()
{
imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
VkSemaphoreCreateInfo semaphoreCreateInfo{};
semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
{
VK_ASSERT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &imageAvailableSemaphores[i]), "Failed to initialize available image semaphore");
VK_ASSERT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &renderFinishedSemaphores[i]), "Failed to initialize render finished semaphore");
VkFenceCreateInfo fenceCreateInfo{};
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
VK_ASSERT(vkCreateFence(device, &fenceCreateInfo, nullptr, &inFlightFences[i]), "Failed to initialize in flight fence");
}
}
void Destroy()
{
for (auto&& framebuffer : framebuffers)
{
vkDestroyFramebuffer(device, framebuffer, nullptr);
}
for (auto&& swapChainImageView : imageViews)
{
vkDestroyImageView(device, swapChainImageView, nullptr);
}
vkDestroySwapchainKHR(device, handle, nullptr);
}
VkSurfaceFormatKHR SelectSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats)
{
for (auto&& availableFormat : availableFormats)
{
if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
{
return availableFormat;
}
}
return availableFormats[0];
}
VkPresentModeKHR SelectSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes)
{
for (auto&& availablePresentMode : availablePresentModes)
{
if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR)
{
return availablePresentMode;
}
}
// VK_PRESENT_MODE_FIFO_KHR is guaranteed to be present
return VK_PRESENT_MODE_FIFO_KHR;
}
VkExtent2D SelectSwapExtent(GLFWwindow* window, const VkSurfaceCapabilitiesKHR& capabilities)
{
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max())
return capabilities.currentExtent;
int width, height;
glfwGetFramebufferSize(window, &width, &height);
VkExtent2D extent{width, height};
extent.width = std::clamp(extent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
extent.height = std::clamp(extent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
return extent;
}
};
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <stdexcept>
class VulkanException : public std::runtime_error
{
public:
VulkanException(const char* str)
: runtime_error{str}
{}
};
+6
View File
@@ -0,0 +1,6 @@
#pragma once
class Window
{
};
+195 -656
View File
File diff suppressed because it is too large Load Diff