here is my code
#define GLFW_INCLUDE_VULKAN
#undef _DEBUG
#include <glfw3.h>
#include < stdexcept>
#include < cstdlib>
#include < vector>
#include < cstring>
#include < algorithm>
#include < glm/glm.hpp>
#include < set>
#include < optional>
#include < iostream>
#include < memory>
#include < shaderc/shaderc.hpp>
#include < vulkan/vk_enum_string_helper.h>
const int WIDTH = 800;
const int HEIGHT = 600;
static const char* validationLayer[] = {
"VK_LAYER_KHRONOS_validation", // standard validation layer
//"VK_LAYER_LUNARG_standard_validation", // add if not on MacOS
"VK_LAYER_RENDERDOC_Capture", // add this if you have installed RenderDoc
"VK_LAYER_KHRONOS_synchronization2"
};
#ifdef NDEBUG
const bool enableValidationLayers = false;
#else
const bool enableValidationLayers = true;
#endif
#ifdef _WIN32 // must use MT platform DLL libraries on windows
#pragma comment(lib, "shaderc_combined.lib")
#endif
#define ARRAY_SIZE_IN_ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
#define uint unsigned int
#define CHECK_VK_RESULT(res) \
{\
VkResult result = (res); \
std::string res_s = string_VkResult(result); \
if (result != VK_SUCCESS) { \
throw std::runtime_error(res_s); \
} \
}
using namespace std;
class VulkanRenderer {
private:
struct QueueFamilyIndices {
uint32_t graphicsFamily;
uint32_t presentFamily;
bool graphicsFamilyHasValue = false;
bool presentFamilyHasValue = false;
bool isComplete() {
return graphicsFamilyHasValue && presentFamilyHasValue;
}
};
struct PipelineConfigInfo {
VkViewport viewport;
VkRect2D scissor;
VkPipelineInputAssemblyStateCreateInfo vkPipelineInputAssemblyStateCreateInfo;
VkPipelineRasterizationStateCreateInfo rasterizationInfo;
VkPipelineMultisampleStateCreateInfo multisampleInfo;
VkPipelineColorBlendAttachmentState colorBlendAttachment;
VkPipelineColorBlendStateCreateInfo colorBlendInfo;
VkPipelineDepthStencilStateCreateInfo depthStencilInfo;
VkPipelineLayout pipelineLayout = nullptr;
VkRenderPass renderPass = nullptr;
uint32_t subpass = 0;
};
//HELPERS
VkSurfaceFormatKHR 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];
}
QueueFamilyIndices findQueueFamilies() {
QueueFamilyIndices indices;
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data());
int i = 0;
for (const auto& queueFamily : queueFamilies) {
if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
indices.graphicsFamily = i;
indices.graphicsFamilyHasValue = true;
}
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &presentSupport);
if (queueFamily.queueCount > 0 && presentSupport) {
indices.presentFamily = i;
indices.presentFamilyHasValue = true;
}
if (indices.isComplete()) {
break;
}
i++;
}
return indices;
}
VkSurfaceFormatKHR getFormat() {
uint32_t formatCount;
vector<VkSurfaceFormatKHR> vkSurfaceFormatKHRs;
vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, nullptr);
vkSurfaceFormatKHRs.resize(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, vkSurfaceFormatKHRs.data());
VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(vkSurfaceFormatKHRs);
return surfaceFormat;
}
PipelineConfigInfo defaultPipelineConfigInfo() {
PipelineConfigInfo configInfo = {};
configInfo.vkPipelineInputAssemblyStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
configInfo.vkPipelineInputAssemblyStateCreateInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
configInfo.vkPipelineInputAssemblyStateCreateInfo.primitiveRestartEnable = VK_FALSE;
configInfo.viewport.x = 0.0f;
configInfo.viewport.y = 0.0f;
configInfo.viewport.width = static_cast<float>(WIDTH);
configInfo.viewport.height = static_cast<float>(HEIGHT);
configInfo.viewport.minDepth = 0.0f;
configInfo.viewport.maxDepth = 1.0f;
configInfo.scissor.extent = {WIDTH,HEIGHT};
configInfo.scissor.offset = { 0,0};
configInfo.rasterizationInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
configInfo.rasterizationInfo.depthClampEnable = VK_FALSE;
configInfo.rasterizationInfo.rasterizerDiscardEnable = VK_FALSE;
configInfo.rasterizationInfo.polygonMode = VK_POLYGON_MODE_FILL;
configInfo.rasterizationInfo.lineWidth = 1.0f;
configInfo.rasterizationInfo.cullMode = VK_CULL_MODE_NONE;
configInfo.rasterizationInfo.frontFace = VK_FRONT_FACE_CLOCKWISE;
configInfo.rasterizationInfo.depthBiasEnable = VK_FALSE;
configInfo.rasterizationInfo.depthBiasConstantFactor = 0.0f; // Optional
configInfo.rasterizationInfo.depthBiasClamp = 0.0f; // Optional
configInfo.rasterizationInfo.depthBiasSlopeFactor = 0.0f; // Optional
configInfo.multisampleInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
configInfo.multisampleInfo.sampleShadingEnable = VK_FALSE;
configInfo.multisampleInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
configInfo.multisampleInfo.minSampleShading = 1.0f; // Optional
configInfo.multisampleInfo.pSampleMask = nullptr; // Optional
configInfo.multisampleInfo.alphaToCoverageEnable = VK_FALSE; // Optional
configInfo.multisampleInfo.alphaToOneEnable = VK_FALSE; // Optional
configInfo.colorBlendAttachment.colorWriteMask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT;
configInfo.colorBlendAttachment.blendEnable = VK_FALSE;
configInfo.colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
configInfo.colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
configInfo.colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
configInfo.colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
configInfo.colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
configInfo.colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
configInfo.colorBlendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
configInfo.colorBlendInfo.logicOpEnable = VK_FALSE;
configInfo.colorBlendInfo.logicOp = VK_LOGIC_OP_COPY;
configInfo.colorBlendInfo.attachmentCount = 1;
configInfo.colorBlendInfo.pAttachments = &configInfo.colorBlendAttachment;
configInfo.colorBlendInfo.blendConstants[0] = 0.0f;
configInfo.colorBlendInfo.blendConstants[1] = 0.0f;
configInfo.colorBlendInfo.blendConstants[2] = 0.0f;
configInfo.colorBlendInfo.blendConstants[3] = 0.0f;
configInfo.depthStencilInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
configInfo.depthStencilInfo.depthTestEnable = VK_TRUE;
configInfo.depthStencilInfo.depthWriteEnable = VK_TRUE;
configInfo.depthStencilInfo.depthCompareOp = VK_COMPARE_OP_LESS;
configInfo.depthStencilInfo.depthBoundsTestEnable = VK_FALSE;
configInfo.depthStencilInfo.minDepthBounds = 0.0f; // Optional
configInfo.depthStencilInfo.maxDepthBounds = 1.0f; // Optional
configInfo.depthStencilInfo.stencilTestEnable = VK_FALSE;
configInfo.depthStencilInfo.front = {}; // Optional
configInfo.depthStencilInfo.back = {}; // Optional
return configInfo;
}
int findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {
VkPhysicalDeviceMemoryProperties memProperties;
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
return i;
}
}
throw std::runtime_error("Failed to find suitable memory type!");
}
void createInstance() {
VkApplicationInfo appInfo{};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "App Name Here";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "Gear Engine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_0;
auto extensions = getRequiredExtensions();
VkInstanceCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = NULL,
.pApplicationInfo = &appInfo,
.enabledLayerCount = static_cast<uint32_t>(2),
.ppEnabledLayerNames = validationLayer,
.enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
.ppEnabledExtensionNames = extensions.data()
};
//VkAllocationCallbacks* vkAllocationCallbacks = nullptr;
CHECK_VK_RESULT(vkCreateInstance(&createInfo, nullptr, &instance));
}
void createLogicDevice() {
QueueFamilyIndices indices = findQueueFamilies();
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
std::set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily, indices.presentFamily };
float queuePriority = 1.0f;
for (uint32_t queueFamily : uniqueQueueFamilies) {
VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = queueFamily;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &queuePriority;
queueCreateInfos.push_back(queueCreateInfo);
}
VkPhysicalDeviceFeatures deviceFeatures = {};
deviceFeatures.samplerAnisotropy = VK_TRUE;
VkDeviceCreateInfo createInfo = {};
memset(&createInfo, 0, sizeof(VkDeviceCreateInfo));
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
createInfo.pQueueCreateInfos = queueCreateInfos.data();
createInfo.pEnabledFeatures = &deviceFeatures;
createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensionNames.size());
createInfo.ppEnabledExtensionNames = deviceExtensionNames.data();
createInfo.enabledLayerCount = static_cast<uint32_t>(2);
createInfo.ppEnabledLayerNames = validationLayer;
CHECK_VK_RESULT(vkCreateDevice(physicalDevice, &createInfo, nullptr, &device))
vkGetDeviceQueue(device, indices.graphicsFamily, 0, &graphicsQueue);
vkGetDeviceQueue(device, indices.presentFamily, 0, &presentQueue);
}
void createCommandPool() {
QueueFamilyIndices queueFamilyIndices = findQueueFamilies();
VkCommandPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily;
poolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; // Optional: Allow resetting command buffers
CHECK_VK_RESULT(vkCreateCommandPool(device, &poolInfo, nullptr, &m_cmdBufPool));
cout << "created command pool successfully\n";
}
void createPipelineLayout() {
VkPipelineLayoutCreateInfo vkPipelineLayoutCreateInfo;
vkPipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
vkPipelineLayoutCreateInfo.setLayoutCount = 0;
vkPipelineLayoutCreateInfo.pSetLayouts = nullptr;
vkPipelineLayoutCreateInfo.pushConstantRangeCount = 0;
vkPipelineLayoutCreateInfo.pPushConstantRanges = nullptr;
vkPipelineLayoutCreateInfo.flags = 0;
vkPipelineLayoutCreateInfo.pNext = nullptr;
CHECK_VK_RESULT(vkCreatePipelineLayout(device, &vkPipelineLayoutCreateInfo, nullptr, &pipelineLayout));
cout << "created graphics pipeline layout successfully\n";
}
void createGraphicsPipeline() {
createShaders();
createPipelineLayout();
createRenderPass();
PipelineConfigInfo pipelineConfigInfo = defaultPipelineConfigInfo();
pipelineConfigInfo.renderPass = m_renderPass;
pipelineConfigInfo.pipelineLayout = pipelineLayout;
VkPipelineShaderStageCreateInfo vkPipelineShaderStageCreateInfo[2];
vkPipelineShaderStageCreateInfo[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vkPipelineShaderStageCreateInfo[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
vkPipelineShaderStageCreateInfo[0].module = vertexShader;
vkPipelineShaderStageCreateInfo[0].pName = "main";
vkPipelineShaderStageCreateInfo[0].flags = 0;
vkPipelineShaderStageCreateInfo[0].pNext = nullptr;
vkPipelineShaderStageCreateInfo[0].pSpecializationInfo = nullptr;
vkPipelineShaderStageCreateInfo[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vkPipelineShaderStageCreateInfo[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
vkPipelineShaderStageCreateInfo[1].module = pixelShader;
vkPipelineShaderStageCreateInfo[1].pName = "main";
vkPipelineShaderStageCreateInfo[1].flags = 0;
vkPipelineShaderStageCreateInfo[1].pNext = nullptr;
vkPipelineShaderStageCreateInfo[1].pSpecializationInfo = nullptr;
VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexAttributeDescriptionCount =0 ;
vertexInputInfo.vertexBindingDescriptionCount = 0;
vertexInputInfo.pVertexAttributeDescriptions = nullptr;
vertexInputInfo.pVertexBindingDescriptions = nullptr;
VkSurfaceCapabilitiesKHR SurfaceCaps;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &SurfaceCaps);
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float)SurfaceCaps.currentExtent.width;
viewport.height = (float)SurfaceCaps.currentExtent.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor{};
scissor.offset = { 0, 0 };
scissor.extent = SurfaceCaps.currentExtent;
VkPipelineViewportStateCreateInfo viewportInfo{};
viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportInfo.viewportCount = 1;
viewportInfo.pViewports = &viewport;
viewportInfo.scissorCount = 1;
viewportInfo.pScissors = &scissor;
VkGraphicsPipelineCreateInfo pipelineInfo{};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = vkPipelineShaderStageCreateInfo;
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &pipelineConfigInfo.vkPipelineInputAssemblyStateCreateInfo;
pipelineInfo.pViewportState = &viewportInfo;
pipelineInfo.pRasterizationState = &pipelineConfigInfo.rasterizationInfo;
pipelineInfo.pMultisampleState = &pipelineConfigInfo.multisampleInfo;
pipelineInfo.pColorBlendState = &pipelineConfigInfo.colorBlendInfo;
pipelineInfo.pDepthStencilState = &pipelineConfigInfo.depthStencilInfo;
pipelineInfo.pDynamicState = nullptr;
pipelineInfo.layout = pipelineConfigInfo.pipelineLayout;
pipelineInfo.renderPass = pipelineConfigInfo.renderPass;
pipelineInfo.subpass = pipelineConfigInfo.subpass;
pipelineInfo.basePipelineIndex = -1;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
CHECK_VK_RESULT(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &m_pipeline));
cout << "created graphics pipeline successfully\n";
}
void createSwapChain() {
VkSurfaceCapabilitiesKHR SurfaceCaps;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &SurfaceCaps);
uint32_t formatCount;
vector<VkSurfaceFormatKHR> vkSurfaceFormatKHRs;
vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, nullptr);
vkSurfaceFormatKHRs.resize(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, vkSurfaceFormatKHRs.data());
VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(vkSurfaceFormatKHRs);
assert(SurfaceCaps.currentExtent.width != -1);
unsigned int NumImages = 2;
assert(NumImages >= SurfaceCaps.minImageCount);
assert(NumImages <= SurfaceCaps.maxImageCount);
VkSwapchainCreateInfoKHR SwapChainCreateInfo = {};
SwapChainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
SwapChainCreateInfo.surface = surface;
SwapChainCreateInfo.minImageCount = NumImages;
SwapChainCreateInfo.imageFormat = surfaceFormat.format;
SwapChainCreateInfo.imageColorSpace = surfaceFormat.colorSpace;
SwapChainCreateInfo.imageExtent = SurfaceCaps.currentExtent;
SwapChainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
SwapChainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;// ADNROID ORIENTATION STUFF
SwapChainCreateInfo.imageArrayLayers = 1;//AR VR STUFF
SwapChainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
SwapChainCreateInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;//v-sync stuff
SwapChainCreateInfo.clipped = true;
SwapChainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;//how the surface interacts with the bg(OS stuff)
VkResult res = vkCreateSwapchainKHR(device, &SwapChainCreateInfo, NULL, &m_swapChainKHR);
CHECK_VK_RESULT(res);
unsigned int NumSwapChainImages = 0;
res = vkGetSwapchainImagesKHR(device, m_swapChainKHR, &NumSwapChainImages, NULL);
CHECK_VK_RESULT(res);
m_images.resize(NumSwapChainImages);
m_cmdBufs.resize(NumSwapChainImages);
VkImage s;
res = vkGetSwapchainImagesKHR(device, m_swapChainKHR, &NumSwapChainImages, &(m_images[0]));
CHECK_VK_RESULT(res);
cout << "swapchain created successfully\n";
};
void createSyncObjects( ) {
VkSemaphoreCreateInfo semaphoreInfo = {};
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS) {
throw std::runtime_error("Failed to create semaphore!");
}
if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderCompleteSemaphore) != VK_SUCCESS) {
throw std::runtime_error("Failed to create semaphore!");
}
VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = 1; // Ensure the fence starts unsignaled
if (vkCreateFence(device, &fenceInfo, nullptr, &drawFence) != VK_SUCCESS) {
throw std::runtime_error("Failed to create fence!");
}
cout << "semaphore and fence created successfully\n";
}
void createRenderPass() {
VkAttachmentDescription colorAttachment{};
colorAttachment.format = getFormat().format;
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;
VkRenderPassCreateInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &m_renderPass) != VK_SUCCESS) {
throw std::runtime_error("Failed to create render pass!");
}
cout << "created render pass succesfully\n";
}
void createCommandBuffers() {
createCommandPool();
uint32_t swapchainImageCount;
vkGetSwapchainImagesKHR(device, m_swapChainKHR, &swapchainImageCount, nullptr);
m_cmdBufs.resize(swapchainImageCount);
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandPool = m_cmdBufPool;
allocInfo.commandBufferCount = static_cast<uint32_t>(m_cmdBufs.size());
CHECK_VK_RESULT(vkAllocateCommandBuffers(device, &allocInfo, m_cmdBufs.data()));
cout << "allocated command buffers succesfully\n";
for (int i = 0; i < m_cmdBufs.size();i++) {
VkCommandBufferBeginInfo vkCommandBufferBeginInfo{};
vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
CHECK_VK_RESULT(vkBeginCommandBuffer(m_cmdBufs[i], &vkCommandBufferBeginInfo));
VkRenderPassBeginInfo vkRenderPassBeginInfo{};
vkRenderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
vkRenderPassBeginInfo.renderPass = m_renderPass;
vkRenderPassBeginInfo.framebuffer = m_fbs[i];
VkSurfaceCapabilitiesKHR SurfaceCaps;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &SurfaceCaps);
vkRenderPassBeginInfo.renderArea.offset = { 0,0 };
vkRenderPassBeginInfo.renderArea.extent = SurfaceCaps.currentExtent;
VkClearValue clearValues[2];
clearValues[0].color = { 0.001f,0.101f,0.001f,1.0f };//background color
clearValues[1].depthStencil = { 1.0f,0 };
vkRenderPassBeginInfo.clearValueCount = static_cast<uint32_t>(sizeof(clearValues) / sizeof(VkClearValue));
vkRenderPassBeginInfo.pClearValues = clearValues;
vkCmdBeginRenderPass(m_cmdBufs[i], &vkRenderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(m_cmdBufs[i], VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline);
vkCmdDraw(m_cmdBufs[i], 3, 1, 0, 0);
vkCmdEndRenderPass(m_cmdBufs[i]);
CHECK_VK_RESULT(vkEndCommandBuffer(m_cmdBufs[i]));
}
cout << "created command buffers succesfully\n";
}
void createImageViews() {
uint32_t swapchainImageCount;
vkGetSwapchainImagesKHR(device, m_swapChainKHR, &swapchainImageCount, nullptr);
std::vector<VkImage> swapchainImages(swapchainImageCount);
vkGetSwapchainImagesKHR(device, m_swapChainKHR, &swapchainImageCount, swapchainImages.data());
m_views.resize(swapchainImageCount);
for (size_t i = 0; i < swapchainImageCount; 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 = getFormat().format;
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 (vkCreateImageView(device, &createInfo, nullptr, &m_views[i]) != VK_SUCCESS) {
throw std::runtime_error("Failed to create image views!");
}
}
cout << "created Image views succesfully\n";
};
void createFramebuffers() {
uint32_t swapchainImageCount;
vkGetSwapchainImagesKHR(device, m_swapChainKHR, &swapchainImageCount, nullptr);
m_fbs.resize(swapchainImageCount);
for (size_t i = 0; i < swapchainImageCount; i++) {
VkImageView attachments[] = {
m_views[i]
//,depthView
};
VkFramebufferCreateInfo framebufferInfo{};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = m_renderPass;
framebufferInfo.attachmentCount = 1;
framebufferInfo.pAttachments = attachments;
framebufferInfo.width = WIDTH;
framebufferInfo.height = HEIGHT;
framebufferInfo.layers = 1;
if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &m_fbs[i]) != VK_SUCCESS) {
throw std::runtime_error("Failed to create framebuffer!");
}
}
cout << "frame buffer create successfully\n";
}
void cleanupSwapChain() {
for (size_t i = 0; i < m_fbs.size(); i++) {
vkDestroyFramebuffer(device, m_fbs[i], nullptr);
}
for (size_t i = 0; i < m_views.size(); i++) {
vkDestroyImageView(device, m_views[i], nullptr);
}
vkDestroySwapchainKHR(device, m_swapChainKHR, nullptr);
}
void recreateSwapChain() {
auto extent = getExtent();
while (extent.width == 0 || extent.height == 0) {
extent = getExtent();
glfwWaitEvents();
}
vkDeviceWaitIdle(device);
createSwapChain();
}
void drawFrame() {
// Wait for the fence with a timeout
constexpr uint64_t timeout = 100000000; // 100 ms
const VkResult fenceResult = vkWaitForFences(device, 1, &drawFence, VK_TRUE, timeout);
if (fenceResult == VK_TIMEOUT) {
// Handle timeout situation
throw std::runtime_error("Fence wait timed out!");
}
else if (fenceResult != VK_SUCCESS) {
throw std::runtime_error("Failed to wait for fence!");
}
vkResetFences(device, 1, &drawFence);
uint32_t imageIndex;
VkResult acquireResult = vkAcquireNextImageKHR(device, m_swapChainKHR, UINT64_MAX, imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
if (acquireResult == VK_ERROR_OUT_OF_DATE_KHR) {
// Handle swapchain out of date
recreateSwapChain();
return;
}
else if (acquireResult != VK_SUCCESS) {
throw std::runtime_error("Failed to acquire next image!");
}
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &m_cmdBufs[imageIndex];
VkSemaphore waitSemaphores[] = { imageAvailableSemaphore };
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
VkSemaphore signalSemaphores[] = { renderCompleteSemaphore };
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
CHECK_VK_RESULT(vkQueueSubmit(graphicsQueue, 1, &submitInfo, drawFence));
VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
VkSwapchainKHR swapChains[] = { m_swapChainKHR };
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex;
VkResult queuePresentResult = vkQueuePresentKHR(presentQueue, &presentInfo);
if (queuePresentResult == VK_ERROR_OUT_OF_DATE_KHR || queuePresentResult == VK_SUBOPTIMAL_KHR) {
// Handle swapchain out of date or suboptimal
//recreateSwapChain();
}
else if (queuePresentResult != VK_SUCCESS) {
throw std::runtime_error("Failed to present image!");
}
}
public:
GLFWwindow* window;
VkInstance instance;
VkSurfaceKHR surface;
VkQueue graphicsQueue;
VkDevice device;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
std::vector<VkImageView> m_views;
VkRenderPass m_renderPass;
std::vector<VkFramebuffer> m_fbs;
VkPipeline m_pipeline;
VkPipelineLayout pipelineLayout;
VkSwapchainKHR m_swapChainKHR;
VkQueue presentQueue;
VkCommandPool m_cmdBufPool;
VkSemaphore imageAvailableSemaphore;
VkSemaphore renderCompleteSemaphore;
VkFence drawFence;
std::vector<VkImage> m_images;
VkShaderModule vertexShader = nullptr;
VkShaderModule pixelShader = nullptr;
std::vector<VkCommandBuffer> m_cmdBufs;
VkImage depthImage;
VkDeviceMemory depthMemory;
VkImageView depthView;
vector<const char*> deviceExtensionNames = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
std::vector<const char*> getRequiredExtensions() {
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
if (enableValidationLayers) {
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
return extensions;
}
void init() {
createInstance();
createSurface();
createPhysicalDevice();
createLogicDevice();
createSwapChain();
createSyncObjects();
createGraphicsPipeline();
createImageViews();
createFramebuffers();
createCommandBuffers();
}
};