Skip to content

Instantly share code, notes, and snippets.

@TheVice
Created November 14, 2022 22:30
Show Gist options
  • Save TheVice/0ae55199763301db7d8e5b8a1716d91b to your computer and use it in GitHub Desktop.
Save TheVice/0ae55199763301db7d8e5b8a1716d91b to your computer and use it in GitHub Desktop.
This sample contain code original described at the video "Vulkan is HARD" 03.09.2022 https://www.youtube.com/watch?v=XGmVrjbFVm0 by @lolzdev. Any not visible parts took from next source file - https://github.com/Overv/VulkanTutorial/raw/87803541171579165caa354120157a0cc6c8192f/code/15_hello_triangle.cpp. Shaders, that was not described in the vi…
#version 450
layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0);
}
#version 450
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];
}
#include "App.hpp"
#include <set>
#include <limits>
#include <string>
#include <cstring>
#include <fstream>
#include <iostream>
#include <algorithm>
/*#pragma comment(lib, "glfw3.lib")
#pragma comment(lib, "vulkan-1.lib")*/
static std::vector<char> readFile(const std::string& filename)
{
std::ifstream file(filename, std::ios::ate | std::ios::binary);
if (!file.is_open())
{
throw std::runtime_error("failed to open file!");
}
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 App::run()
{
initWindow();
initVulkan();
mainLoop();
cleanup();
}
void App::initVulkan()
{
createInstance();
createSurface();
choosePhysicalDevice();
createDevice();
createSwapChain();
createImageViews();
createRenderPass();
createGraphicsPipeline();
createFramebuffers();
createCommandPool();
createCommandBuffer();
createSyncObjects();
}
void App::createInstance()
{
VkResult vkResult = VkResult::VK_SUCCESS;
VkApplicationInfo appInfo;
memset(&appInfo, 0, sizeof(appInfo));
//
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "Vulkan Test";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "Vulkan Test";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_0;// VK_API_VERSION_1_3;
//
VkInstanceCreateInfo createInfo;
memset(&createInfo, 0, sizeof(createInfo));
//
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
createInfo.flags = 0;
#if 0
if (!checkValidationLayers())
{
throw std::runtime_error("Cannot find requested validation layers");
}
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data();
#else
std::vector<const char*> vkInstanceLayerNames =
{
/*#ifndef NDEBUG
"VK_LAYER_LUNARG_core_validation"
#endif*/
};
createInfo.enabledLayerCount = static_cast<uint32_t>(vkInstanceLayerNames.size());
createInfo.ppEnabledLayerNames = vkInstanceLayerNames.data();
#endif
//
std::vector<const char*> extensions = getRequiredExtensions();
//
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data();
if (VK_SUCCESS != (vkResult = vkCreateInstance(&createInfo, nullptr, &instance)))
{
throw std::runtime_error("Cannot create Vulkan instance");
}
}
void App::cleanup()
{
vkDestroySemaphore(device, imageAvailableSemaphore, nullptr);
vkDestroySemaphore(device, renderFinishedSemaphore, nullptr);
vkDestroyFence(device, inFlightFence, nullptr);
vkDestroyCommandPool(device, commandPool, nullptr);
for (auto framebuffer : swapChainFramebuffers)
{
vkDestroyFramebuffer(device, framebuffer, nullptr);
}
vkDestroyPipeline(device, graphicsPipeline, nullptr);
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
vkDestroyRenderPass(device, renderPass, nullptr);
for (auto imageView : swapChainImageViews)
{
vkDestroyImageView(device, imageView, nullptr);
}
vkDestroySwapchainKHR(device, swapChain, nullptr);
vkDestroyDevice(device, nullptr);
vkDestroySurfaceKHR(instance, surface, nullptr);
vkDestroyInstance(instance, nullptr);
//
glfwDestroyWindow(window);
//
glfwTerminate();
}
void App::mainLoop()
{
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
drawFrame();
if (GLFW_PRESS == glfwGetKey(window, GLFW_KEY_Q))
{
glfwSetWindowShouldClose(window, true);
}
}
vkDeviceWaitIdle(device);
}
void App::initWindow()
{
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
window = glfwCreateWindow(800, 600, "Vulkan", nullptr, nullptr);
}
void App::choosePhysicalDevice()
{
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
if (0 == deviceCount)
{
throw std::runtime_error("Failed to find any GPUs that support Vulkan API.");
}
std::vector<VkPhysicalDevice> devices(deviceCount);
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
for (const auto& device_ : devices)
{
if (isPhysicalDeviceSuitable(device_))
{
physicalDevice = device_;
break;
}
}
if (VK_NULL_HANDLE == physicalDevice)
{
throw std::runtime_error("GPU not founded.");
}
}
void App::createDevice()
{
QueueIndices queueIndices = getQueueIndices(physicalDevice);
std::vector<VkDeviceQueueCreateInfo> deviceQueues;
std::set<uint32_t> queueFamilies = { queueIndices.graphicsIndex.value(), queueIndices.presentIndex.value() };
const float priority = 1.0f;
for (uint32_t family : queueFamilies)
{
VkDeviceQueueCreateInfo queueCreateInfo;
memset(&queueCreateInfo, 0, sizeof(queueCreateInfo));
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.flags = 0;
queueCreateInfo.queueFamilyIndex = family;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &priority;
deviceQueues.push_back(queueCreateInfo);
}
VkDeviceCreateInfo createInfo;
memset(&createInfo, 0, sizeof(createInfo));
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.flags = 0;
createInfo.queueCreateInfoCount = static_cast<uint32_t>(deviceQueues.size());
createInfo.pQueueCreateInfos = deviceQueues.data();
createInfo.enabledLayerCount = 0;
createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
createInfo.ppEnabledExtensionNames = deviceExtensions.data();
createInfo.pEnabledFeatures = nullptr;
if (VK_SUCCESS != vkCreateDevice(physicalDevice, &createInfo, nullptr, &device))
{
throw std::runtime_error("Cannot create device");
}
vkGetDeviceQueue(device, queueIndices.graphicsIndex.value(), 0, &graphicsQueue);
vkGetDeviceQueue(device, queueIndices.presentIndex.value(), 0, &presentQueue);
}
void App::createSurface()
{
if (VK_SUCCESS != glfwCreateWindowSurface(instance, window, nullptr, &surface))
{
throw std::runtime_error("Cannot create surface");
}
}
void App::createSwapChain()
{
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);
//
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;
memset(&createInfo, 0, sizeof(createInfo));
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = 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;
//
QueueIndices indices = getQueueIndices(physicalDevice);
uint32_t queueFamilyIndicies[] = { indices.graphicsIndex.value(), indices.presentIndex.value() };
if (indices.graphicsIndex != indices.presentIndex)
{
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
createInfo.queueFamilyIndexCount = 2;
createInfo.pQueueFamilyIndices = queueFamilyIndicies;
}
else
{
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 0;
createInfo.pQueueFamilyIndices = nullptr;
}
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 (VK_SUCCESS != vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain))
{
throw std::runtime_error("Cannot create swap chain");
}
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
swapChainImages.resize(imageCount);
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
//
swapChainImageFormat = surfaceFormat.format;
swapChainExtent = extent;
}
void App::createImageViews()
{
swapChainImageViews.resize(swapChainImages.size());
for (size_t i = 0; i < swapChainImages.size(); ++i)
{
VkImageViewCreateInfo createInfo;
memset(&createInfo, 0, sizeof(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;
if (VK_SUCCESS != vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]))
{
throw std::runtime_error("failed to create image views");
}
}
}
void App::createGraphicsPipeline()
{
auto vertShaderCode = readFile("../shaders/vert.spv");
auto fragShaderCode = readFile("../shaders/frag.spv");
//
VkShaderModule vertShaderModule = createShaderModule(vertShaderCode);
VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);
//
VkPipelineShaderStageCreateInfo vertShaderStageInfo;
memset(&vertShaderStageInfo, 0, sizeof(vertShaderStageInfo));
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertShaderStageInfo.module = vertShaderModule;
vertShaderStageInfo.pName = "main";
//
VkPipelineShaderStageCreateInfo fragShaderStageInfo;
memset(&fragShaderStageInfo, 0, sizeof(fragShaderStageInfo));
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragShaderStageInfo.module = fragShaderModule;
fragShaderStageInfo.pName = "main";
//
VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
//
VkPipelineVertexInputStateCreateInfo vertexInputInfo;
memset(&vertexInputInfo, 0, sizeof(vertexInputInfo));
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 0;
vertexInputInfo.pVertexBindingDescriptions = nullptr;
vertexInputInfo.vertexAttributeDescriptionCount = 0;
vertexInputInfo.pVertexAttributeDescriptions = nullptr;
//
VkPipelineInputAssemblyStateCreateInfo inputAssembly;
memset(&inputAssembly, 0, sizeof(inputAssembly));
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssembly.primitiveRestartEnable = VK_FALSE;
//
VkViewport viewport;
memset(&viewport, 0, sizeof(viewport));
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(swapChainExtent.width);
viewport.height = static_cast<float>(swapChainExtent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
//
VkRect2D scissor;
memset(&scissor, 0, sizeof(scissor));
scissor.offset = { 0, 0 };
scissor.extent = swapChainExtent;
//
VkPipelineViewportStateCreateInfo viewportState;
memset(&viewportState, 0, sizeof(viewportState));
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.scissorCount = 1;
viewportState.pScissors = &scissor;
//
VkPipelineRasterizationStateCreateInfo rasterizer;
memset(&rasterizer, 0, sizeof(rasterizer));
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE;
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
rasterizer.depthBiasConstantFactor = 0.0f;
rasterizer.depthBiasClamp = 0.0f;
rasterizer.depthBiasSlopeFactor = 0.0f;
//
VkPipelineMultisampleStateCreateInfo multisampling;
memset(&multisampling, 0, sizeof(multisampling));
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisampling.minSampleShading = 1.0f;
multisampling.pSampleMask = nullptr;
multisampling.alphaToCoverageEnable = VK_FALSE;
multisampling.alphaToOneEnable = VK_FALSE;
//
VkPipelineColorBlendAttachmentState colorBlendAttachment;
memset(&colorBlendAttachment, 0, sizeof(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 colorBlending;
memset(&colorBlending, 0, sizeof(colorBlending));
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
colorBlending.blendConstants[0] = 0.0f;
colorBlending.blendConstants[1] = 0.0f;
colorBlending.blendConstants[2] = 0.0f;
colorBlending.blendConstants[3] = 0.0f;
//
std::vector<VkDynamicState> dynamicStates =
{
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR
};
//
VkPipelineDynamicStateCreateInfo dynamicState;
memset(&dynamicState, 0, sizeof(dynamicState));
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());
dynamicState.pDynamicStates = dynamicStates.data();
//
VkPipelineLayoutCreateInfo pipelineLayoutInfo;
memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo));
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 0;
pipelineLayoutInfo.pSetLayouts = nullptr;
pipelineLayoutInfo.pushConstantRangeCount = 0;
pipelineLayoutInfo.pPushConstantRanges = nullptr;
if (VK_SUCCESS != vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout))
{
throw std::runtime_error("failed to create pipeline layout");
}
VkGraphicsPipelineCreateInfo pipelineInfo;
memset(&pipelineInfo, 0, sizeof(pipelineInfo));
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = shaderStages;
//
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pDepthStencilState = nullptr;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = &dynamicState;
pipelineInfo.layout = pipelineLayout;
pipelineInfo.renderPass = renderPass;
pipelineInfo.subpass = 0;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
pipelineInfo.basePipelineIndex = -1;
if (VK_SUCCESS != vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline))
{
throw std::runtime_error("failed to create graphics pipeline");
}
vkDestroyShaderModule(device, fragShaderModule, nullptr);
vkDestroyShaderModule(device, vertShaderModule, nullptr);
}
VkShaderModule App::createShaderModule(const std::vector<char>& code)
{
VkShaderModuleCreateInfo createInfo;
memset(&createInfo, 0, sizeof(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;
if (VK_SUCCESS != vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule))
{
throw std::runtime_error("failed to create shader module");
}
return shaderModule;
}
void App::createRenderPass()
{
VkAttachmentDescription colorAttachment;
memset(&colorAttachment, 0, sizeof(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;
memset(&colorAttachmentRef, 0, sizeof(colorAttachmentRef));
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
//
VkSubpassDescription subpass;
memset(&subpass, 0, sizeof(subpass));
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
//
VkSubpassDependency dependency;
memset(&dependency, 0, sizeof(dependency));
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstStageMask = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
//
VkRenderPassCreateInfo renderPassInfo;
memset(&renderPassInfo, 0, sizeof(renderPassInfo));
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
renderPassInfo.dependencyCount = 1;
renderPassInfo.pDependencies = &dependency;
if (VK_SUCCESS != vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass))
{
throw std::runtime_error("Cannot create render pass");
}
}
void App::createFramebuffers()
{
swapChainFramebuffers.resize(swapChainImageViews.size());
for (size_t i = 0; i < swapChainImageViews.size(); ++i)
{
VkImageView attachments[] =
{
swapChainImageViews[i]
};
//
VkFramebufferCreateInfo framebufferInfo;
memset(&framebufferInfo, 0, sizeof(framebufferInfo));
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = renderPass;
framebufferInfo.attachmentCount = 1;
framebufferInfo.pAttachments = attachments;
framebufferInfo.width = swapChainExtent.width;
framebufferInfo.height = swapChainExtent.height;
framebufferInfo.layers = 1;
if (VK_SUCCESS != vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]))
{
throw std::runtime_error("Cannot create framebuffer");
}
}
}
std::vector<const char*> App::getRequiredExtensions()
{
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
return extensions;
}
bool App::checkValidationLayers()
{
uint32_t layerCount = 0;
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
std::vector<VkLayerProperties> availableLayers(layerCount);
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
for (const auto& layer : validationLayers)
{
bool layerFound = false;
for (const auto& availableLayer : availableLayers)
{
if (0 == strcmp(availableLayer.layerName, layer))
{
layerFound = true;
break;
}
}
if (!layerFound)
{
return false;
}
}
return true;
}
bool App::isPhysicalDeviceSuitable(VkPhysicalDevice device_)
{
bool extensionsSupported = checkDeviceExtensionSupport(device_);
bool swapChainAdequate = false;
if (extensionsSupported)
{
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device_);
swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
}
QueueIndices indices = getQueueIndices(device_);
return indices.complete() && extensionsSupported && swapChainAdequate;
}
QueueIndices App::getQueueIndices(VkPhysicalDevice device_)
{
QueueIndices indices;
uint32_t queueCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device_, &queueCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamies(queueCount);
vkGetPhysicalDeviceQueueFamilyProperties(device_, &queueCount, queueFamies.data());
uint32_t index = 0;
for (const auto& family : queueFamies)
{
if (indices.complete())
{
break;
}
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(device_, index, surface, &presentSupport);
if (presentSupport)
{
indices.presentIndex = index;
}
if (VK_QUEUE_GRAPHICS_BIT & family.queueFlags)
{
indices.graphicsIndex = index;
}
++index;
}
if (!indices.complete())
{
throw std::runtime_error("Cannot find all the needed queues");
}
return indices;
}
bool App::checkDeviceExtensionSupport(VkPhysicalDevice device_)
{
uint32_t extensionCount = 0;
vkEnumerateDeviceExtensionProperties(device_, nullptr, &extensionCount, nullptr);
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
vkEnumerateDeviceExtensionProperties(device_, nullptr, &extensionCount, availableExtensions.data());
std::set<std::string> requiredExtensions(deviceExtensions.cbegin(), deviceExtensions.cend());
for (const auto& extension : availableExtensions)
{
requiredExtensions.erase(extension.extensionName);
}
return requiredExtensions.empty();
}
SwapChainSupportDetails App::querySwapChainSupport(VkPhysicalDevice device_)
{
SwapChainSupportDetails details;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device_, surface, &details.capabilities);
uint32_t formatCount = 0;
vkGetPhysicalDeviceSurfaceFormatsKHR(device_, surface, &formatCount, nullptr);
if (0 != formatCount)
{
details.formats.resize(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(device_, surface, &formatCount, details.formats.data());
}
uint32_t presentModeCount = 0;
vkGetPhysicalDeviceSurfacePresentModesKHR(device_, surface, &presentModeCount, nullptr);
if (0 != presentModeCount)
{
details.presentModes.resize(presentModeCount);
vkGetPhysicalDeviceSurfacePresentModesKHR(device_, surface, &presentModeCount, details.presentModes.data());
}
return details;
}
VkSurfaceFormatKHR App::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 App::chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes)
{
/*TODO:*/(void)availablePresentModes;
return VK_PRESENT_MODE_FIFO_KHR;
}
VkExtent2D App::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities)
{
if (std::numeric_limits<uint32_t>::max() != capabilities.currentExtent.width)
{
return capabilities.currentExtent;
}
else
{
int width, height;
glfwGetFramebufferSize(window, &width, &height);
//
VkExtent2D actualExtent =
{
static_cast<uint32_t>(width),
static_cast<uint32_t>(height)
};
//
actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
//
return actualExtent;
}
}
void App::createCommandBuffer()
{
VkCommandBufferAllocateInfo allocInfo;
memset(&allocInfo, 0, sizeof(allocInfo));
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = commandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = 1;
if (VK_SUCCESS != vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer))
{
throw std::runtime_error("Cannot allocate command buffers");
}
}
void App::createCommandPool()
{
QueueIndices queueIndices = getQueueIndices(physicalDevice);
//
VkCommandPoolCreateInfo poolInfo;
memset(&poolInfo, 0, sizeof(poolInfo));
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
poolInfo.queueFamilyIndex = queueIndices.graphicsIndex.value();
if (VK_SUCCESS != vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool))
{
throw std::runtime_error("failed to create command pool!");
}
}
void App::recordCommandBuffer(VkCommandBuffer commandBuffer_, uint32_t imageIndex)
{
VkCommandBufferBeginInfo beginInfo;
memset(&beginInfo, 0, sizeof(beginInfo));
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = 0;
beginInfo.pInheritanceInfo = nullptr;
if (VK_SUCCESS != vkBeginCommandBuffer(commandBuffer_, &beginInfo))
{
throw std::runtime_error("failed to begin recording command buffer");
}
VkRenderPassBeginInfo renderPassInfo;
memset(&renderPassInfo, 0, sizeof(renderPassInfo));
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = renderPass;
renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex];
renderPassInfo.renderArea.offset = { 0, 0 };
renderPassInfo.renderArea.extent = swapChainExtent;
//
VkClearValue clearColor = {{{ 0.0f, 0.0f, 0.0f, 1.0f }}};
renderPassInfo.clearValueCount = 1;
renderPassInfo.pClearValues = &clearColor;
//
vkCmdBeginRenderPass(commandBuffer_, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(commandBuffer_, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
//
VkViewport viewport;
memset(&viewport, 0, sizeof(viewport));
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(swapChainExtent.width);
viewport.height = static_cast<float>(swapChainExtent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(commandBuffer_, 0, 1, &viewport);
//
VkRect2D scissor;
memset(&scissor, 0, sizeof(scissor));
scissor.offset = { 0, 0 };
scissor.extent = swapChainExtent;
vkCmdSetScissor(commandBuffer_, 0, 1, &scissor);
//
vkCmdDraw(commandBuffer_, 3, 1, 0, 0);
vkCmdEndRenderPass(commandBuffer_);
if (VK_SUCCESS != vkEndCommandBuffer(commandBuffer_))
{
throw std::runtime_error("Cannot record command buffer");
}
}
void App::drawFrame()
{
vkWaitForFences(device, 1, &inFlightFence, VK_TRUE, UINT64_MAX);
vkResetFences(device, 1, &inFlightFence);
uint32_t imageIndex;
vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
vkResetCommandBuffer(commandBuffer, 0);
recordCommandBuffer(commandBuffer, imageIndex);
//
VkSubmitInfo submitInfo;
memset(&submitInfo, 0, sizeof(submitInfo));
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
//
VkSemaphore waitSemaphores[] = { imageAvailableSemaphore };
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
//
VkSemaphore signalSemaphores[] = { renderFinishedSemaphore };
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
if (VK_SUCCESS != vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFence))
{
throw std::runtime_error("Cannot submit draw command buffer");
}
VkPresentInfoKHR presentInfo;
memset(&presentInfo, 0, sizeof(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;
presentInfo.pResults = nullptr;
vkQueuePresentKHR(presentQueue, &presentInfo);
}
void App::createSyncObjects()
{
VkSemaphoreCreateInfo semaphoreInfo;
memset(&semaphoreInfo, 0, sizeof(semaphoreInfo));
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
//
VkFenceCreateInfo fenceInfo;
memset(&fenceInfo, 0, sizeof(fenceInfo));
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
if (VK_SUCCESS != vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) ||
VK_SUCCESS != vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) ||
VK_SUCCESS != vkCreateFence(device, &fenceInfo, nullptr, &inFlightFence))
{
throw std::runtime_error("Cannot create semaphores");
}
}
int main()
{
App app;
//
try
{
app.run();
}
catch (const std::exception& e)
{
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
#ifndef __APP_HPP__
#define __APP_HPP__
#ifdef _WIN32
#define NOMINMAX
#endif
#include <vector>
#include <optional>
#ifdef _WIN32
#define VK_USE_PLATFORM_WIN32_KHR
#endif
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
const std::vector<const char*> validationLayers =
{
"VK_LAYER_KHRONOS_validation"
};
const std::vector<const char*> deviceExtensions =
{
VK_KHR_SWAPCHAIN_EXTENSION_NAME
};
struct QueueIndices
{
std::optional<uint32_t> graphicsIndex;
std::optional<uint32_t> presentIndex;
bool complete()
{
return graphicsIndex.has_value() && presentIndex.has_value();
}
};
struct SwapChainSupportDetails
{
VkSurfaceCapabilitiesKHR capabilities;
std::vector<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> presentModes;
};
class App {
public:
void run();
private:
void initWindow();
GLFWwindow* window;
void mainLoop();
void initVulkan();
void cleanup();
void createInstance();
VkInstance instance;
void choosePhysicalDevice();
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
void createSurface();
VkSurfaceKHR surface;
VkQueue graphicsQueue;
VkQueue presentQueue;
void createSwapChain();
void createImageViews();
VkSwapchainKHR swapChain;
std::vector<VkImage> swapChainImages;
VkFormat swapChainImageFormat;
VkExtent2D swapChainExtent;
std::vector<VkImageView> swapChainImageViews;
void createGraphicsPipeline();
VkShaderModule createShaderModule(const std::vector<char>& code);
VkPipelineLayout pipelineLayout;
VkPipeline graphicsPipeline;
void createRenderPass();
VkRenderPass renderPass;
void createFramebuffers();
std::vector<VkFramebuffer> swapChainFramebuffers;
void createCommandPool();
VkCommandPool commandPool;
void createCommandBuffer();
VkCommandBuffer commandBuffer;
void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex);
void drawFrame();
void createSyncObjects();
VkSemaphore imageAvailableSemaphore;
VkSemaphore renderFinishedSemaphore;
VkFence inFlightFence;
void createDevice();
VkDevice device;
std::vector<const char*> getRequiredExtensions();
bool checkValidationLayers();
bool isPhysicalDeviceSuitable(VkPhysicalDevice device);
QueueIndices getQueueIndices(VkPhysicalDevice device);
bool checkDeviceExtensionSupport(VkPhysicalDevice device);
SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device);
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes);
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities);
};
#endif
cmake_minimum_required(VERSION 3.15)
project(vulkan_test)
if(MINGW)
message(WARNING "MinGW environment not tested.")
endif()
list(APPEND SOURCES "${CMAKE_SOURCE_DIR}/App.cpp")
list(APPEND HEADERS "${CMAKE_SOURCE_DIR}/App.hpp")
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS})
if(CMAKE_VERSION VERSION_LESS 3.1 OR ";${CMAKE_CXX_COMPILE_FEATURES};" MATCHES ";cxx_std_17;")
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17)
else()
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17)
endif()
target_compile_options(${PROJECT_NAME} PRIVATE
$<$<CXX_COMPILER_ID:Clang>:-Wall -Wextra -Werror>
$<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra -Werror>
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
)
# glfw
set(glfw3_FOUND 0)
find_package(glfw3)
if(NOT glfw3_FOUND)
message(WARNING "glfw3 not FOUND, will search via GLFW_PATH variable.")
if(DEFINED ENV{GLFW_PATH})
string(REPLACE "\\" "/" glfw_Path $ENV{GLFW_PATH})
elseif(DEFINED GLFW_PATH)
string(REPLACE "\\" "/" glfw_Path ${GLFW_PATH})
else()
message(FATAL_ERROR "Could not find glfw library, please check it at the https://github.com/glfw/glfw/releases")
endif()
if(EXISTS "${glfw_Path}/CMakeLists.txt")
set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
add_subdirectory(${glfw_Path} ${CMAKE_BINARY_DIR}/glfw)
else()
if(MINGW)
set(glfw_HINT_PATH ${glfw_Path}/lib-mingw-w64)
elseif(MSVC)
if(NOT (MSVC_VERSION LESS 1930))
set(glfw_HINT_PATH ${glfw_Path}/lib-vc2022)
elseif(NOT (MSVC_VERSION LESS 1920))
set(glfw_HINT_PATH ${glfw_Path}/lib-vc2019)
elseif(NOT (MSVC_VERSION LESS 1910))
set(glfw_HINT_PATH ${glfw_Path}/lib-vc2017)
elseif(NOT (MSVC_VERSION LESS 1900))
set(glfw_HINT_PATH ${glfw_Path}/lib-vc2015)
elseif(NOT (MSVC_VERSION LESS 1800))
set(glfw_HINT_PATH ${glfw_Path}/lib-vc2013)
elseif(NOT (MSVC_VERSION LESS 1700))
set(glfw_HINT_PATH ${glfw_Path}/lib-vc2012)
endif()
endif()
find_library(
glfw_full_path
NAMES glfw3
PATHS "${glfw_HINT_PATH}")
add_library(glfw INTERFACE)
target_link_libraries(glfw INTERFACE ${glfw_full_path})
target_include_directories(glfw INTERFACE SYSTEM ${glfw_Path}/include)
endif()
endif()
if(UNIX)
add_compile_definitions(GLFW_EXPOSE_NATIVE_X11)
add_compile_definitions(GLFW_EXPOSE_NATIVE_GLX)
else()
add_compile_definitions(GLFW_EXPOSE_NATIVE_WIN32)
add_compile_definitions(GLFW_EXPOSE_NATIVE_WGL)
endif()
target_link_libraries(${PROJECT_NAME} glfw)
# Vulkan
set(Vulkan_FOUND 0)
find_package(Vulkan)
if(NOT Vulkan_FOUND)
message(WARNING "Vulkan not FOUND, will search via VULKAN_SDK variable.")
if(DEFINED ENV{VULKAN_SDK})
string(REPLACE "\\" "/" VULKAN_SDK $ENV{VULKAN_SDK})
elseif(DEFINED VULKAN_SDK)
string(REPLACE "\\" "/" VULKAN_SDK ${VULKAN_SDK})
else()
message(FATAL_ERROR "Could not find VULKAN SDK, please check it at the https://vulkan.lunarg.com/sdk/home")
endif()
find_library(
vulkan_full_path
NAMES vulkan-1
PATHS "${VULKAN_SDK}/Lib" "${VULKAN_SDK}/Bin")
add_library(vulkan INTERFACE)
target_link_libraries(vulkan INTERFACE ${vulkan_full_path})
target_include_directories(vulkan INTERFACE SYSTEM ${VULKAN_SDK}/include)
endif()
target_link_libraries(${PROJECT_NAME} vulkan)
# ${PROJECT_NAME}_shader
add_executable(glslang::validator IMPORTED)
find_program(GLSLANG_VALIDATOR "glslangValidator" HINTS $ENV{VULKAN_SDK}/bin ${VULKAN_SDK}/bin REQUIRED)
set_property(TARGET glslang::validator PROPERTY IMPORTED_LOCATION "${GLSLANG_VALIDATOR}")
function(add_shaders_target TARGET)
set(SHADERS_DIR ${CMAKE_BINARY_DIR}/shaders)
add_custom_command(
OUTPUT ${SHADERS_DIR}
COMMAND ${CMAKE_COMMAND} -E make_directory ${SHADERS_DIR})
add_custom_command(
OUTPUT ${SHADERS_DIR}/frag.spv
COMMAND glslang::validator
ARGS ${CMAKE_SOURCE_DIR}/09_shader_base.frag -V -o ${SHADERS_DIR}/frag.spv
WORKING_DIRECTORY ${SHADERS_DIR}
DEPENDS ${SHADERS_DIR} ${CMAKE_SOURCE_DIR}/09_shader_base.frag
COMMENT "Compiling fragment Shader"
VERBATIM)
add_custom_command(
OUTPUT ${SHADERS_DIR}/vert.spv
COMMAND glslang::validator
ARGS ${CMAKE_SOURCE_DIR}/09_shader_base.vert -V -o ${SHADERS_DIR}/vert.spv
WORKING_DIRECTORY ${SHADERS_DIR}
DEPENDS ${SHADERS_DIR} ${CMAKE_SOURCE_DIR}/09_shader_base.vert
COMMENT "Compiling vertical Shader"
VERBATIM)
add_custom_target(${TARGET} DEPENDS ${SHADERS_DIR}/frag.spv ${SHADERS_DIR}/vert.spv)
endfunction()
add_shaders_target(${PROJECT_NAME}_shader)
add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}_shader)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment