Initial commit

This commit is contained in:
Thraix
2023-01-07 21:09:01 +01:00
commit 0396318d2d
441 changed files with 70331 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#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)
VkResult vkCreateDebugUtilsMessengerEXT(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);
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
void vkDestroyDebugUtilsMessengerEXT(VkInstance instance,
VkDebugUtilsMessengerEXT debugMessenger,
const VkAllocationCallbacks* pAllocator) {
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
if (func != nullptr) {
func(instance, debugMessenger, pAllocator);
}
}
+68
View File
@@ -0,0 +1,68 @@
#pragma once
#include "Common.h"
class DebugMessenger
{
public:
VkInstance instance;
VkDebugUtilsMessengerEXT debugMessenger;
DebugMessenger(VkInstance instance)
: instance{instance}
{
#ifndef NDEBUG
VkDebugUtilsMessengerCreateInfoEXT createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
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;
VK_ASSERT(vkCreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger), "Failed to initialze debug messenger");
#endif
}
~DebugMessenger()
{
#ifndef NDEBUG
vkDestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
#endif
}
DebugMessenger(DebugMessenger&&) = delete;
DebugMessenger(const DebugMessenger&) = delete;
DebugMessenger& operator=(DebugMessenger&&) = delete;
DebugMessenger& operator=(const DebugMessenger&) = delete;
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)
{
#ifndef NDEBUG
layers->emplace_back("VK_LAYER_KHRONOS_validation");
#endif
}
private:
static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData)
{
if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
{
std::cerr << pCallbackData->pMessage << std::endl;
}
return VK_FALSE;
}
};
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include "Common.h"
#include <fstream>
namespace FileSystem
{
static std::vector<char> ReadFile(const std::string& filename)
{
std::ifstream file(filename, std::ios::ate | std::ios::binary);
ASSERT(file.is_open(), "Failed to open file");
size_t fileSize = (size_t) file.tellg();
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), fileSize);
return buffer;
}
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <vulkan/vulkan.h>
#include <vector>
class SwapChain
{
VkSwapchainKHR swapChain;
VkFormat swapChainImageFormat;
VkExtent2D swapChainExtent;
std::vector<VkImageView> swapChainImageViews;
std::vector<VkImage> swapChainImages;
std::vector<VkFramebuffer> swapChainFramebuffers;
SwapChain()
{
}
};
+932
View File
@@ -0,0 +1,932 @@
#include "DebugMessenger.h"
#include "FileSystem.h"
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <iostream>
#include <vector>
#include <optional>
#include <set>
static const int WINDOW_WIDTH = 1920;
static const int WINDOW_HEIGHT = 1080;
static const int MAX_FRAMES_IN_FLIGHT = 2;
struct QueueFamilyIndices
{
std::optional<uint32_t> graphicsFamily;
std::optional<uint32_t> presentFamily;
bool AllRequiredFamiliesSupported()
{
return graphicsFamily.has_value() && presentFamily.has_value();
}
};
struct SwapChainSupportDetails
{
VkSurfaceCapabilitiesKHR capabilities;
std::vector<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> presentModes;
bool Valid()
{
return !formats.empty() && !presentModes.empty();
}
};
class Application final
{
private:
GLFWwindow* window;
VkInstance instance;
VkSurfaceKHR surface;
DebugMessenger* debugMessenger;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
VkDevice device;
VkQueue graphicsQueue;
VkQueue presentQueue;
VkSwapchainKHR swapChain;
VkFormat swapChainImageFormat;
VkExtent2D swapChainExtent;
std::vector<VkImageView> swapChainImageViews;
std::vector<VkImage> swapChainImages;
std::vector<VkFramebuffer> swapChainFramebuffers;
VkPipelineLayout pipelineLayout;
VkRenderPass renderPass;
VkPipeline graphicsPipeline;
VkCommandPool commandPool;
std::vector<VkCommandBuffer> commandBuffers;
std::vector<VkSemaphore> imageAvailableSemaphores;
std::vector<VkSemaphore> renderFinishedSemaphores;
std::vector<VkFence> inFlightFences;
int currentFrame = 0;
bool framebufferResized = false;
public:
Application()
{
InitializeWindow();
InitializeVulkan();
}
~Application()
{
DestroyVulkan();
DestroyWindow();
}
Application(Application&&) = delete;
Application(const Application&) = delete;
Application& operator=(Application&&) = delete;
Application& operator=(const Application&) = delete;
bool Update()
{
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
uint32_t imageIndex;
VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
if (result == VK_ERROR_OUT_OF_DATE_KHR)
{
RecreateSwapChain();
return true;
}
vkResetFences(device, 1, &inFlightFences[currentFrame]);
RecordCommandBuffer(commandBuffers[currentFrame], imageIndex);
VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]};
VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]};
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 = 1;
submitInfo.pCommandBuffers = &commandBuffers[currentFrame];
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
VK_ASSERT(vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]), "Failed to submit command buffer");
VkSwapchainKHR swapChains[] = {swapChain};
VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex;
presentInfo.pResults = nullptr;
result = vkQueuePresentKHR(presentQueue, &presentInfo);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized)
{
framebufferResized = false;
RecreateSwapChain();
}
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
return !glfwWindowShouldClose(window);
}
private:
void InitializeWindow()
{
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Vulkan Window", nullptr, nullptr);
glfwSetWindowUserPointer(window, this);
glfwSetFramebufferSizeCallback(window, FramebufferResizeCallback);
}
void InitializeVulkan()
{
InitializeInstance();
InitializeDebugMessenger();
InitializeSurface();
SelectPhysicalDevice();
InitializeLogicalDevice();
InitializeSwapChain();
InitializeSwapChainImageViews();
InitializeRenderPass();
InitializeGraphicsPipeline();
InitializeSwapChainFramebuffers();
InitializeCommandPool();
InitializeCommandBuffer();
InitializeSyncObjects();
}
void InitializeInstance()
{
VkApplicationInfo appInfo{};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "Vulkan Tutorial";
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 = new 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()
{
QueueFamilyIndices indices = FindQueueFamilies(physicalDevice);
float queuePriority = 1.0f;
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos{};
std::set<uint32_t> uniqueQueueFamilies{indices.graphicsFamily.value(), indices.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, indices.graphicsFamily.value(), 0, &graphicsQueue);
vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
}
void InitializeSwapChain()
{
SwapChainSupportDetails swapChainSupport = FindSwapChainSupport(physicalDevice);
VkSurfaceFormatKHR format = SelectSwapSurfaceFormat(swapChainSupport.formats);
VkPresentModeKHR presentMode = SelectSwapPresentMode(swapChainSupport.presentModes);
swapChainExtent = SelectSwapExtent(swapChainSupport.capabilities);
swapChainImageFormat = format.format;
uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
if (swapChainSupport.capabilities.maxImageCount != 0)
{
imageCount = std::min(imageCount, swapChainSupport.capabilities.maxImageCount);
}
QueueFamilyIndices indices = FindQueueFamilies(physicalDevice);
std::vector<uint32_t> queueFamilyIndices{indices.graphicsFamily.value(), indices.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 = swapChainExtent;
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 (indices.graphicsFamily != indices.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, &swapChain), "Failed to initialize the swapchain");
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
swapChainImages.resize(imageCount);
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
}
void InitializeSwapChainImageViews()
{
swapChainImageViews.resize(swapChainImages.size());
for (size_t i = 0; i < swapChainImages.size(); i++)
{
VkImageViewCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.image = swapChainImages[i];
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
createInfo.format = swapChainImageFormat;
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, &swapChainImageViews[i]), "Failed to initialize swapchain image view");
}
}
void InitializeRenderPass()
{
VkAttachmentDescription colorAttachment{};
colorAttachment.format = swapChainImageFormat;
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 InitializeGraphicsPipeline()
{
std::vector<char> vertShaderCode = FileSystem::ReadFile("res/shaders/vert.spv");
std::vector<char> fragShaderCode = FileSystem::ReadFile("res/shaders/frag.spv");
VkShaderModule vertShaderModule = InitializeShaderModule(vertShaderCode);
VkShaderModule fragShaderModule = InitializeShaderModule(fragShaderCode);
VkPipelineShaderStageCreateInfo shaderStages[2];
shaderStages[0] = {};
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[1] = {};
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";
VkPipelineVertexInputStateCreateInfo vertexInputCreateInfo{};
vertexInputCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputCreateInfo.vertexBindingDescriptionCount = 0;
vertexInputCreateInfo.pVertexBindingDescriptions = nullptr;
vertexInputCreateInfo.vertexAttributeDescriptionCount = 0;
vertexInputCreateInfo.pVertexAttributeDescriptions = nullptr;
VkPipelineInputAssemblyStateCreateInfo inputAssemblyCreateInfo{};
inputAssemblyCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssemblyCreateInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssemblyCreateInfo.primitiveRestartEnable = VK_FALSE;
VkViewport viewport{};
viewport.x = 0;
viewport.y = 0;
viewport.width = swapChainExtent.width;
viewport.height = swapChainExtent.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor{};
scissor.offset = {0, 0};
scissor.extent = swapChainExtent;
std::vector<VkDynamicState> dynamicStates = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo{};
dynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicStateCreateInfo.dynamicStateCount = dynamicStates.size();
dynamicStateCreateInfo.pDynamicStates = dynamicStates.data();
VkPipelineViewportStateCreateInfo viewportStateCreateInfo{};
viewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportStateCreateInfo.viewportCount = 1;
viewportStateCreateInfo.pViewports = &viewport;
viewportStateCreateInfo.scissorCount = 1;
viewportStateCreateInfo.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizerCreateInfo{};
rasterizerCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizerCreateInfo.depthClampEnable = VK_FALSE;
rasterizerCreateInfo.rasterizerDiscardEnable = VK_FALSE;
rasterizerCreateInfo.polygonMode = VK_POLYGON_MODE_FILL;
rasterizerCreateInfo.lineWidth = 1.0f;
rasterizerCreateInfo.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizerCreateInfo.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizerCreateInfo.depthBiasEnable = VK_FALSE;
rasterizerCreateInfo.depthBiasConstantFactor = 0.0f;
rasterizerCreateInfo.depthBiasClamp = 0.0f;
rasterizerCreateInfo.depthBiasSlopeFactor = 0.0f;
VkPipelineMultisampleStateCreateInfo multisampleCreateInfo{};
multisampleCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleCreateInfo.sampleShadingEnable = VK_FALSE;
multisampleCreateInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisampleCreateInfo.minSampleShading = 1.0f;
multisampleCreateInfo.pSampleMask = nullptr;
multisampleCreateInfo.alphaToCoverageEnable = VK_FALSE;
multisampleCreateInfo.alphaToOneEnable = VK_FALSE;
VkPipelineColorBlendAttachmentState colorBlendAttachment{};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
VkPipelineColorBlendStateCreateInfo colorBlendCreateInfo{};
colorBlendCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlendCreateInfo.logicOpEnable = VK_FALSE;
colorBlendCreateInfo.logicOp = VK_LOGIC_OP_COPY;
colorBlendCreateInfo.attachmentCount = 1;
colorBlendCreateInfo.pAttachments = &colorBlendAttachment;
colorBlendCreateInfo.blendConstants[0] = 0.0f;
colorBlendCreateInfo.blendConstants[1] = 0.0f;
colorBlendCreateInfo.blendConstants[2] = 0.0f;
colorBlendCreateInfo.blendConstants[3] = 0.0f;
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{};
pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutCreateInfo.setLayoutCount = 0;
pipelineLayoutCreateInfo.pSetLayouts = nullptr;
pipelineLayoutCreateInfo.pushConstantRangeCount = 0;
pipelineLayoutCreateInfo.pPushConstantRanges = nullptr;
VK_ASSERT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout), "Failed to initialize pipeline layout");
VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfo{};
graphicsPipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
graphicsPipelineCreateInfo.stageCount = 2;
graphicsPipelineCreateInfo.pStages = shaderStages;
graphicsPipelineCreateInfo.pVertexInputState = &vertexInputCreateInfo;
graphicsPipelineCreateInfo.pInputAssemblyState = &inputAssemblyCreateInfo;
graphicsPipelineCreateInfo.pViewportState = &viewportStateCreateInfo;
graphicsPipelineCreateInfo.pRasterizationState = &rasterizerCreateInfo;
graphicsPipelineCreateInfo.pMultisampleState = &multisampleCreateInfo;
graphicsPipelineCreateInfo.pDepthStencilState = nullptr;
graphicsPipelineCreateInfo.pColorBlendState = &colorBlendCreateInfo;
graphicsPipelineCreateInfo.pDynamicState = &dynamicStateCreateInfo;
graphicsPipelineCreateInfo.layout = pipelineLayout;
graphicsPipelineCreateInfo.renderPass = renderPass;
graphicsPipelineCreateInfo.subpass = 0;
graphicsPipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE;
graphicsPipelineCreateInfo.basePipelineIndex = -1;
VK_ASSERT(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &graphicsPipelineCreateInfo, nullptr, &graphicsPipeline), "Failed to initialize graphics pipeline");
vkDestroyShaderModule(device, vertShaderModule, nullptr);
vkDestroyShaderModule(device, fragShaderModule, nullptr);
}
void InitializeSwapChainFramebuffers()
{
swapChainFramebuffers.resize(swapChainImages.size());
for (size_t i = 0; i < swapChainImageViews.size(); ++i)
{
VkImageView attachments[] = {swapChainImageViews[i]};
VkFramebufferCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
createInfo.renderPass = renderPass;
createInfo.attachmentCount = 1;
createInfo.pAttachments = attachments;
createInfo.width = swapChainExtent.width;
createInfo.height = swapChainExtent.height;
createInfo.layers = 1;
VK_ASSERT(vkCreateFramebuffer(device, &createInfo, nullptr, &swapChainFramebuffers[i]), "Failed to initialize swap chain framebuffer");
}
}
void InitializeCommandPool()
{
QueueFamilyIndices indices = FindQueueFamilies(physicalDevice);
VkCommandPoolCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
createInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
createInfo.queueFamilyIndex = indices.graphicsFamily.value();
VK_ASSERT(vkCreateCommandPool(device, &createInfo, nullptr, &commandPool), "Failed to initialize command pool");
}
void InitializeCommandBuffer()
{
commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
VkCommandBufferAllocateInfo allocateInfo{};
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocateInfo.commandPool = commandPool;
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocateInfo.commandBufferCount = commandBuffers.size();
VK_ASSERT(vkAllocateCommandBuffers(device, &allocateInfo, commandBuffers.data()), "Failed to initialize command buffer");
}
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 RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex)
{
vkResetCommandBuffer(commandBuffer, 0);
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = 0;
beginInfo.pInheritanceInfo = nullptr;
VK_ASSERT(vkBeginCommandBuffer(commandBuffer, &beginInfo), "Failed to begin command buffer");
VkClearValue clearValue = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
VkRenderPassBeginInfo renderPassBeginInfo{};
renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassBeginInfo.renderPass = renderPass;
renderPassBeginInfo.framebuffer = swapChainFramebuffers[imageIndex];
renderPassBeginInfo.renderArea.offset = {0, 0};
renderPassBeginInfo.renderArea.extent = swapChainExtent;
renderPassBeginInfo.clearValueCount = 1;
renderPassBeginInfo.pClearValues = &clearValue;
vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = swapChainExtent.width;
viewport.height = swapChainExtent.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
VkRect2D scissor{};
scissor.offset = {0, 0};
scissor.extent = swapChainExtent;
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
vkCmdDraw(commandBuffer, 3, 1, 0, 0);
vkCmdEndRenderPass(commandBuffer);
VK_ASSERT(vkEndCommandBuffer(commandBuffer), "Failed to end command buffer");
}
VkShaderModule InitializeShaderModule(const std::vector<char>& code)
{
VkShaderModuleCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.codeSize = code.size();
createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
VkShaderModule shaderModule;
VK_ASSERT(vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule), "Failed to initialize shader module");
return shaderModule;
}
QueueFamilyIndices 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 (auto&& queueFamily : queueFamilies)
{
if(queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
{
indices.graphicsFamily = i;
}
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
if (presentSupport)
{
indices.presentFamily = i;
}
i++;
}
return indices;
}
SwapChainSupportDetails FindSwapChainSupport(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;
}
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;
if (!FindQueueFamilies(device).AllRequiredFamiliesSupported())
return false;
if (!CheckDeviceExtensionSupport(device))
return false;
if (!FindSwapChainSupport(device).Valid())
return false;
return true;
}
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 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;
}
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(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;
}
void RecreateSwapChain()
{
int width = 0;
int height = 0;
glfwGetFramebufferSize(window, &width, &height);
while (width == 0 || height == 0)
{
glfwGetFramebufferSize(window, &width, &height);
glfwWaitEvents();
}
vkDeviceWaitIdle(device);
DestroySwapChain();
InitializeSwapChain();
InitializeSwapChainImageViews();
InitializeSwapChainFramebuffers();
}
void DestroySwapChain()
{
for (auto&& framebuffer : swapChainFramebuffers)
{
vkDestroyFramebuffer(device, framebuffer, nullptr);
}
for (auto&& swapChainImageView : swapChainImageViews)
{
vkDestroyImageView(device, swapChainImageView, nullptr);
}
vkDestroySwapchainKHR(device, swapChain, nullptr);
}
void DestroyVulkan()
{
vkDeviceWaitIdle(device);
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);
}
vkDestroyCommandPool(device, commandPool, nullptr);
vkDestroyPipeline(device, graphicsPipeline, nullptr);
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
vkDestroyRenderPass(device, renderPass, nullptr);
DestroySwapChain();
vkDestroyDevice(device, nullptr);
vkDestroySurfaceKHR(instance, surface, nullptr);
delete debugMessenger;
vkDestroyInstance(instance, nullptr);
}
void DestroyWindow()
{
glfwDestroyWindow(window);
}
std::vector<const char*> GetRequiredDeviceExtensions()
{
return {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
}
static void FramebufferResizeCallback(GLFWwindow* window, int width, int height)
{
Application* application = static_cast<Application*>(glfwGetWindowUserPointer(window));
application->framebufferResized = true;
}
};
int main()
{
glfwInit();
try
{
Application application;
while (application.Update())
{
glfwPollEvents();
}
}
catch(std::exception& e)
{
std::cerr << "Exception: " << e.what() << std::endl;
return 1;
}
glfwTerminate();
return 0;
}