Created
February 24, 2018 18:49
-
-
Save morgondag/c461b7f7c2b402046e71d62360f09439 to your computer and use it in GitHub Desktop.
vulkan 101
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <SDL2/SDL.h> | |
#include <SDL_vulkan.h> | |
#include <vulkan/vulkan.h> | |
#include <stdio.h> | |
#include <chrono> | |
#include <iostream> | |
#include <thread> | |
#include <vector> | |
#include <array> | |
#include <stdexcept> | |
#include <fstream> | |
#include <set> | |
struct QueueFamilyIndices { | |
int graphicsFamily = -1; | |
int presentFamily = -1; | |
bool isComplete() { | |
return graphicsFamily >= 0 && presentFamily >= 0; | |
} | |
}; | |
class VulkanRenderer { | |
public: | |
void init(){ | |
window = SDL_CreateWindow("SpaceTalk - Vulkan Rendering", SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED, 1920/2, 1080/2, SDL_WINDOW_VULKAN); | |
SDL_SetWindowResizable(window, SDL_TRUE); | |
/** CREATE A VULKAN INSTANCE **/ | |
// create vulkan app with custom info about our app + engine | |
VkApplicationInfo applicationInfo = {}; | |
applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; | |
applicationInfo.pApplicationName = "SpaceTalk"; | |
applicationInfo.applicationVersion = VK_MAKE_VERSION(0,0,1); | |
applicationInfo.pEngineName = "SpaceTalk"; | |
applicationInfo.engineVersion = VK_MAKE_VERSION(0,0,1); | |
applicationInfo.apiVersion = VK_API_VERSION_1_0; | |
// get the window extention count and names from sdl | |
SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, NULL); | |
extentionNames = new const char *[extensionCount]; | |
SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, extentionNames); | |
// merge the applicationInfo and windowExtensions into the info creator | |
VkInstanceCreateInfo createInfo = {}; | |
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; | |
createInfo.pApplicationInfo = &applicationInfo; | |
createInfo.enabledExtensionCount = extensionCount; | |
createInfo.ppEnabledExtensionNames = extentionNames; | |
createInfo.enabledLayerCount = 0; | |
// create the vulkan instance | |
VkResult createResult = vkCreateInstance(&createInfo,NULL, &instance); | |
if(createResult != VK_SUCCESS){ | |
std::cout << "instancing vulkan failed" << std::endl; | |
} | |
/** CREATE A SURFACE **/ | |
if(SDL_Vulkan_CreateSurface(window, instance, &surface) != SDL_TRUE){ | |
std::cout << "SDL_Vulkan_CreateSurface failed hard!" << std::endl; | |
} | |
/** GET GRAPHICS CARD DEVICE! **/ | |
uint32_t deviceCount = 0; | |
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); | |
if (deviceCount == 0) { | |
std::cout << "failed to find GPUs with Vulkan support!" << std::endl; | |
} | |
std::vector<VkPhysicalDevice> devices(deviceCount); | |
// pickup all the devices available | |
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); | |
std::cout << "devices: " << deviceCount <<std::endl; | |
physicalDevice = devices[0]; | |
/** CREATE LOGICAL DEVICE **/ | |
QueueFamilyIndices indices = findQueueFamilies(physicalDevice); | |
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos; | |
std::set<int> uniqueQueueFamilies = {indices.graphicsFamily, indices.presentFamily}; | |
float queuePriority = 1.0f; | |
for (int 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 = {}; | |
VkDeviceCreateInfo createInfoDevice = {}; | |
createInfoDevice.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; | |
createInfoDevice.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size()); | |
createInfoDevice.pQueueCreateInfos = queueCreateInfos.data(); | |
createInfoDevice.pEnabledFeatures = &deviceFeatures; | |
createInfoDevice.enabledExtensionCount = 0; | |
createInfoDevice.enabledLayerCount = 0; | |
if (vkCreateDevice(physicalDevice, &createInfoDevice, NULL, &device) != VK_SUCCESS) { | |
std::cout <<"failed to create logical device!" <<std::endl; | |
} | |
// create queues! | |
vkGetDeviceQueue(device, indices.graphicsFamily, 0, &graphicsQueue); | |
vkGetDeviceQueue(device, indices.presentFamily, 0, &presentQueue); | |
createSwapchain(); | |
createImageViews(); | |
createRenderPass(); | |
createGraphicsPipeline(); | |
createFramebuffers(); | |
/** COMMAND POOLS **/ | |
QueueFamilyIndices queueFamilyIndicesPool = findQueueFamilies(physicalDevice); | |
VkCommandPoolCreateInfo poolInfo = {}; | |
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; | |
poolInfo.queueFamilyIndex = queueFamilyIndicesPool.graphicsFamily; | |
poolInfo.flags = 0; // Optional | |
if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { | |
std::cout << "failed to create command pool!" << std::endl; | |
} | |
createCommandBuffers(); | |
VkSemaphoreCreateInfo semaphoreInfo = {}; | |
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; | |
if ( | |
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS || | |
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) | |
{ | |
std::cout << "failed to create semaphores!" << std::endl; | |
} | |
}; | |
void createSwapchain(){ | |
/** CREATE SWAPCHAIN **/ | |
VkSurfaceCapabilitiesKHR surfaceCap = {}; | |
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfaceCap); | |
swapchainExtent = {}; | |
int w; | |
int h; | |
SDL_GetWindowSize(window, &w, &h); | |
swapchainExtent.width = w; | |
swapchainExtent.height = h; | |
uint32_t presentModesCount = 0; | |
vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModesCount, NULL); | |
std::vector<VkPresentModeKHR> presentModes(presentModesCount); | |
vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModesCount, presentModes.data()); | |
VkPresentModeKHR supportedPresentMode = VK_PRESENT_MODE_FIFO_KHR; | |
for(uint32_t i = 0; i < presentModesCount; i++){ | |
if(presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR){ | |
supportedPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; | |
break; | |
} | |
if(presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR){ | |
supportedPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; | |
} | |
} | |
// just pick default.. | |
swapChainImageFormat = VK_FORMAT_B8G8R8A8_UNORM; | |
uint32_t imageCount = surfaceCap.minImageCount + 1; | |
VkSwapchainCreateInfoKHR createInfoSwap = {}; | |
createInfoSwap.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; | |
createInfoSwap.surface = surface; | |
createInfoSwap.minImageCount = imageCount; | |
createInfoSwap.imageFormat = swapChainImageFormat; | |
createInfoSwap.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; | |
createInfoSwap.imageExtent = swapchainExtent; | |
createInfoSwap.imageArrayLayers = 1; | |
createInfoSwap.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; | |
createInfoSwap.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; | |
createInfoSwap.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; | |
createInfoSwap.presentMode = supportedPresentMode; | |
createInfoSwap.clipped = VK_TRUE; | |
createInfoSwap.oldSwapchain = VK_NULL_HANDLE; | |
QueueFamilyIndices indices = findQueueFamilies(physicalDevice); | |
uint32_t queueFamilyIndices[] = {(uint32_t) indices.graphicsFamily, (uint32_t) indices.presentFamily}; | |
if (indices.graphicsFamily != indices.presentFamily) { | |
createInfoSwap.imageSharingMode = VK_SHARING_MODE_CONCURRENT; | |
createInfoSwap.queueFamilyIndexCount = 2; | |
createInfoSwap.pQueueFamilyIndices = queueFamilyIndices; | |
} else { | |
createInfoSwap.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; | |
createInfoSwap.queueFamilyIndexCount = 0; | |
createInfoSwap.pQueueFamilyIndices = NULL; | |
} | |
if (vkCreateSwapchainKHR(device, &createInfoSwap, NULL, &swapChain) != VK_SUCCESS) { | |
std::cout <<"failed to create swap chain!" <<std::endl; | |
} | |
/* SWAPCHAIN IMAGES */ | |
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); | |
swapChainImages.resize(imageCount); | |
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); | |
} | |
void createImageViews(){ | |
/*IMAGE VIEWS */ | |
swapChainImageViews.resize(swapChainImages.size()); | |
for (size_t i = 0; i < swapChainImages.size(); i++) { | |
VkImageViewCreateInfo createImageInfo = {}; | |
createImageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; | |
createImageInfo.image = swapChainImages[i]; | |
createImageInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; | |
createImageInfo.format = swapChainImageFormat; | |
createImageInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; | |
createImageInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; | |
createImageInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; | |
createImageInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; | |
createImageInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | |
createImageInfo.subresourceRange.baseMipLevel = 0; | |
createImageInfo.subresourceRange.levelCount = 1; | |
createImageInfo.subresourceRange.baseArrayLayer = 0; | |
createImageInfo.subresourceRange.layerCount = 1; | |
if (vkCreateImageView(device, &createImageInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { | |
std::cout <<"failed to create image views!" <<std::endl; | |
} | |
} | |
} | |
void createRenderPass(){ | |
/* RENDERPASS */ | |
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; | |
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, &renderPass) != VK_SUCCESS) { | |
std::cout << "failed to create render pass!" << std::endl; | |
} | |
} | |
void createGraphicsPipeline(){ | |
/** SHADERS! **/ | |
auto vertShaderCode = readFile("vert.spv"); | |
auto fragShaderCode = readFile("frag.spv"); | |
VkShaderModule vertShaderModule; | |
VkShaderModule fragShaderModule; | |
vertShaderModule = createShaderModule(vertShaderCode); | |
fragShaderModule = createShaderModule(fragShaderCode); | |
VkPipelineShaderStageCreateInfo 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 = {}; | |
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}; | |
/** FIXED PIPELINE - pipelineLayout **/ | |
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; | |
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; | |
vertexInputInfo.vertexBindingDescriptionCount = 0; | |
vertexInputInfo.vertexAttributeDescriptionCount = 0; | |
VkPipelineInputAssemblyStateCreateInfo 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 = {}; | |
viewport.x = 0.0f; | |
viewport.y = 0.0f; | |
viewport.width = (float) swapchainExtent.width; | |
viewport.height = (float) swapchainExtent.height; | |
viewport.minDepth = 0.0f; | |
viewport.maxDepth = 1.0f; | |
VkRect2D scissor = {}; | |
scissor.offset = {0, 0}; | |
scissor.extent = swapchainExtent; | |
VkPipelineViewportStateCreateInfo 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 = {}; | |
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; | |
VkPipelineMultisampleStateCreateInfo multisampling = {}; | |
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; | |
multisampling.sampleShadingEnable = VK_FALSE; | |
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; | |
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; | |
VkPipelineColorBlendStateCreateInfo 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; | |
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; | |
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; | |
pipelineLayoutInfo.setLayoutCount = 0; | |
pipelineLayoutInfo.pushConstantRangeCount = 0; | |
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { | |
std::cout << "failed to create pipeline layout!" << std::endl; | |
} | |
VkGraphicsPipelineCreateInfo 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.pColorBlendState = &colorBlending; | |
pipelineInfo.layout = pipelineLayout; | |
pipelineInfo.renderPass = renderPass; | |
pipelineInfo.subpass = 0; | |
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; | |
// finally create the pipeline! | |
if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { | |
std::cout << "failed to create graphics pipeline!" << std::endl; | |
} | |
// remove temp shaders | |
vkDestroyShaderModule(device, fragShaderModule, nullptr); | |
vkDestroyShaderModule(device, vertShaderModule, nullptr); | |
} | |
void createFramebuffers(){ | |
/** FRAMEBUFFERS **/ | |
swapChainFramebuffers.resize(swapChainImageViews.size()); | |
for (size_t i = 0; i < swapChainImageViews.size(); i++) { | |
VkImageView attachments[] = { | |
swapChainImageViews[i] | |
}; | |
VkFramebufferCreateInfo 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 (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { | |
std::cout << "failed to create framebuffer!" << std::endl; | |
} | |
} | |
} | |
void createCommandBuffers(){ | |
/**COMMAND BUFFERS! SO CLOSE NOW */ | |
commandBuffers.resize(swapChainFramebuffers.size()); | |
VkCommandBufferAllocateInfo allocInfo = {}; | |
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; | |
allocInfo.commandPool = commandPool; | |
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; | |
allocInfo.commandBufferCount = (uint32_t) commandBuffers.size(); | |
if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) { | |
std::cout << "failed to allocate command buffers!" << std::endl; | |
} | |
/**COMMAND BUFFER RECORDING */ | |
for (size_t i = 0; i < commandBuffers.size(); i++) { | |
VkCommandBufferBeginInfo beginInfo = {}; | |
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; | |
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; | |
beginInfo.pInheritanceInfo = nullptr; // Optional | |
vkBeginCommandBuffer(commandBuffers[i], &beginInfo); | |
VkRenderPassBeginInfo renderPassInfo = {}; | |
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; | |
renderPassInfo.renderPass = renderPass; | |
renderPassInfo.framebuffer = swapChainFramebuffers[i]; | |
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(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); | |
vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); | |
vkCmdDraw(commandBuffers[i], 3, 1, 0, 0); | |
vkCmdEndRenderPass(commandBuffers[i]); | |
if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) { | |
std::cout << "failed to record command buffer!" << std::endl; | |
} | |
} | |
} | |
void recreateSwapChain(){ | |
vkDeviceWaitIdle(device); | |
cleanupSwapChain(); | |
createSwapchain(); | |
createImageViews(); | |
createRenderPass(); | |
createGraphicsPipeline(); | |
createFramebuffers(); | |
createCommandBuffers(); | |
} | |
void cleanupSwapChain() { | |
for (auto framebuffer : swapChainFramebuffers) { | |
vkDestroyFramebuffer(device, framebuffer, nullptr); | |
} | |
vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data()); | |
vkDestroyPipeline(device, graphicsPipeline, nullptr); | |
vkDestroyPipelineLayout(device, pipelineLayout, nullptr); | |
vkDestroyRenderPass(device, renderPass, nullptr); | |
for (auto imageView : swapChainImageViews) { | |
vkDestroyImageView(device, imageView, nullptr); | |
} | |
vkDestroySwapchainKHR(device, swapChain, NULL); | |
} | |
void render(){ | |
uint32_t imageIndex; | |
VkResult result = vkAcquireNextImageKHR(device, swapChain, std::numeric_limits<uint64_t>::max(), imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex); | |
if (result == VK_ERROR_OUT_OF_DATE_KHR) { | |
recreateSwapChain(); | |
return; | |
} else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { | |
std::cout << "failed to acquire swap chain image!" << std::endl; | |
return; | |
} | |
VkSubmitInfo 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 = &commandBuffers[imageIndex]; | |
VkSemaphore signalSemaphores[] = {renderFinishedSemaphore}; | |
submitInfo.signalSemaphoreCount = 1; | |
submitInfo.pSignalSemaphores = signalSemaphores; | |
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) { | |
std::cout << "failed to submit draw command buffer!" << std::endl; | |
} | |
VkPresentInfoKHR presentInfo = {}; | |
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; | |
presentInfo.waitSemaphoreCount = 1; | |
presentInfo.pWaitSemaphores = signalSemaphores; | |
VkSwapchainKHR swapChains[] = {swapChain}; | |
presentInfo.swapchainCount = 1; | |
presentInfo.pSwapchains = swapChains; | |
presentInfo.pImageIndices = &imageIndex; | |
result = vkQueuePresentKHR(presentQueue, &presentInfo); | |
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { | |
recreateSwapChain(); | |
} else if (result != VK_SUCCESS) { | |
std::cout << "failed to present swap chain image!" << std::endl; | |
} | |
vkQueueWaitIdle(presentQueue); | |
}; | |
VkShaderModule createShaderModule(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; | |
if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { | |
throw std::runtime_error("failed to create shader module!"); | |
} | |
return shaderModule; | |
} | |
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 = (size_t) file.tellg(); | |
std::vector<char> buffer(fileSize); | |
file.seekg(0); | |
file.read(buffer.data(), fileSize); | |
file.close(); | |
return buffer; | |
} | |
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { | |
QueueFamilyIndices indices; | |
uint32_t queueFamilyCount = 0; | |
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, NULL); | |
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount); | |
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); | |
int i = 0; | |
for (const auto& queueFamily : queueFamilies) { | |
if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { | |
indices.graphicsFamily = i; | |
} | |
VkBool32 presentSupport = false; | |
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); | |
if (queueFamily.queueCount > 0 && presentSupport) { | |
indices.presentFamily = i; | |
} | |
if (indices.isComplete()) { | |
break; | |
} | |
i++; | |
} | |
return indices; | |
} | |
void quit(){ | |
vkDeviceWaitIdle(device); | |
cleanupSwapChain(); | |
vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); | |
vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); | |
vkDestroyCommandPool(device, commandPool, nullptr); | |
vkDestroyDevice(device, NULL); | |
vkDestroySurfaceKHR(instance, surface, NULL); | |
vkDestroyInstance(instance, NULL); | |
SDL_DestroyWindow(window); | |
window = NULL; | |
}; | |
private: | |
VkSemaphore imageAvailableSemaphore; | |
VkSemaphore renderFinishedSemaphore; | |
std::vector<VkCommandBuffer> commandBuffers; | |
VkCommandPool commandPool; | |
std::vector<VkFramebuffer> swapChainFramebuffers; | |
VkPipeline graphicsPipeline; | |
VkRenderPass renderPass; | |
VkPipelineLayout pipelineLayout; | |
std::vector<VkImageView> swapChainImageViews; | |
VkFormat swapChainImageFormat; | |
VkExtent2D swapchainExtent = {}; | |
std::vector<VkImage> swapChainImages; | |
VkSwapchainKHR swapChain; | |
VkQueue graphicsQueue; | |
VkQueue presentQueue; | |
VkDevice device; | |
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; | |
const char **extentionNames; | |
unsigned int extensionCount = 0; | |
VkSurfaceKHR surface; | |
VkInstance instance; | |
SDL_Window *window; | |
}; | |
int main( int argc, char* args[]){ | |
typedef std::chrono::high_resolution_clock Clock; | |
bool isRunning = true; | |
int FRAME_RENDER_SKIP_TICKS = 1000/ 60; | |
Clock::time_point timeCurrent = Clock::now(); | |
Clock::time_point timeStart = Clock::now(); | |
if(SDL_Init(SDL_INIT_EVERYTHING) != 0){ | |
printf("SDL_Init: failed\n"); | |
} else { | |
printf("SDL_Init: ok\n"); | |
} | |
VulkanRenderer vulkan; | |
vulkan.init(); | |
while(isRunning){ | |
timeCurrent = Clock::now(); | |
int tickCount = std::chrono::duration_cast<std::chrono::milliseconds>(timeCurrent - timeStart).count(); | |
bool shouldDraw = true; | |
SDL_Event event; | |
SDL_PollEvent(&event); | |
switch (event.type){ | |
case SDL_QUIT: | |
case SDL_WINDOWEVENT_CLOSE: | |
isRunning = false; | |
break; | |
case SDL_WINDOWEVENT: | |
switch(event.window.event){ | |
case SDL_WINDOWEVENT_MINIMIZED: | |
shouldDraw = false; | |
break; | |
case SDL_WINDOWEVENT_SIZE_CHANGED: | |
vulkan.recreateSwapChain(); | |
break; | |
} | |
break; | |
case SDL_KEYDOWN: | |
switch(event.key.keysym.sym){ | |
case SDLK_ESCAPE: | |
isRunning = false; | |
break; | |
} | |
break; | |
} | |
if(shouldDraw){ | |
vulkan.render(); | |
} | |
auto frameEnd = std::chrono::duration_cast<std::chrono::milliseconds>(timeCurrent - timeStart).count(); | |
int frameTime = frameEnd - tickCount; | |
if(FRAME_RENDER_SKIP_TICKS > frameTime){ | |
std::this_thread::sleep_for(std::chrono::milliseconds(FRAME_RENDER_SKIP_TICKS-frameTime)); | |
} | |
} | |
vulkan.quit(); | |
SDL_Quit(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment