Skip to content

Instantly share code, notes, and snippets.

@morgondag
Created February 24, 2018 18:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save morgondag/c461b7f7c2b402046e71d62360f09439 to your computer and use it in GitHub Desktop.
Save morgondag/c461b7f7c2b402046e71d62360f09439 to your computer and use it in GitHub Desktop.
vulkan 101
#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