Created
June 14, 2018 04:37
-
-
Save kbob/4d240d90f9120384b8ae88d9e3a66356 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
// BEGIN kbob | |
// This is identical to https://vulkan-tutorial.com/Drawing_a_triangle/Swap_chain_recreation | |
// as of 2018-06-13 except for code between the "BEGIN kbob" and "END kbob" comments. | |
// The original code is still there but commented out with "//-". | |
// | |
// Built with MoltenVK 1.1.73 and GLFW 20180519 for MacOS, the original never recreated | |
// the swap chain. It continued to draw at the original 800 by 600 resolution and scaled | |
// the result to fit the resized window. | |
// | |
// Also, according to GLFW doc, the glfwGetWindowSize() is not the right function to call. | |
// glfwGetFramebufferSize() returns the size in pixels, which is the right size to use | |
// for HIDPI/Retina displays. | |
// END kbob | |
#define GLFW_INCLUDE_VULKAN | |
#include <GLFW/glfw3.h> | |
#include <iostream> | |
#include <fstream> | |
#include <stdexcept> | |
#include <algorithm> | |
#include <vector> | |
#include <cstring> | |
#include <cstdlib> | |
#include <set> | |
// BEGIN kbob | |
//-const int WIDTH = 800; | |
//-const int HEIGHT = 600; | |
const int WIDTH = 80; | |
const int HEIGHT = 60; | |
// END kbob | |
const int MAX_FRAMES_IN_FLIGHT = 2; | |
const std::vector<const char*> validationLayers = { | |
"VK_LAYER_LUNARG_standard_validation" | |
}; | |
const std::vector<const char*> deviceExtensions = { | |
VK_KHR_SWAPCHAIN_EXTENSION_NAME | |
}; | |
#ifdef NDEBUG | |
const bool enableValidationLayers = false; | |
#else | |
const bool enableValidationLayers = true; | |
#endif | |
VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) { | |
auto func = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT"); | |
if (func != nullptr) { | |
return func(instance, pCreateInfo, pAllocator, pCallback); | |
} else { | |
return VK_ERROR_EXTENSION_NOT_PRESENT; | |
} | |
} | |
void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator) { | |
auto func = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"); | |
if (func != nullptr) { | |
func(instance, callback, pAllocator); | |
} | |
} | |
struct QueueFamilyIndices { | |
int graphicsFamily = -1; | |
int presentFamily = -1; | |
bool isComplete() { | |
return graphicsFamily >= 0 && presentFamily >= 0; | |
} | |
}; | |
struct SwapChainSupportDetails { | |
VkSurfaceCapabilitiesKHR capabilities; | |
std::vector<VkSurfaceFormatKHR> formats; | |
std::vector<VkPresentModeKHR> presentModes; | |
}; | |
class HelloTriangleApplication { | |
public: | |
void run() { | |
initWindow(); | |
initVulkan(); | |
mainLoop(); | |
cleanup(); | |
} | |
private: | |
GLFWwindow* window; | |
VkInstance instance; | |
VkDebugReportCallbackEXT callback; | |
VkSurfaceKHR surface; | |
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; | |
VkDevice device; | |
VkQueue graphicsQueue; | |
VkQueue presentQueue; | |
VkSwapchainKHR swapChain; | |
std::vector<VkImage> swapChainImages; | |
VkFormat swapChainImageFormat; | |
VkExtent2D swapChainExtent; | |
std::vector<VkImageView> swapChainImageViews; | |
std::vector<VkFramebuffer> swapChainFramebuffers; | |
VkRenderPass renderPass; | |
VkPipelineLayout pipelineLayout; | |
VkPipeline graphicsPipeline; | |
VkCommandPool commandPool; | |
std::vector<VkCommandBuffer> commandBuffers; | |
std::vector<VkSemaphore> imageAvailableSemaphores; | |
std::vector<VkSemaphore> renderFinishedSemaphores; | |
std::vector<VkFence> inFlightFences; | |
size_t currentFrame = 0; | |
void initWindow() { | |
glfwInit(); | |
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); | |
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); | |
} | |
void initVulkan() { | |
createInstance(); | |
setupDebugCallback(); | |
createSurface(); | |
pickPhysicalDevice(); | |
createLogicalDevice(); | |
createSwapChain(); | |
createImageViews(); | |
createRenderPass(); | |
createGraphicsPipeline(); | |
createFramebuffers(); | |
createCommandPool(); | |
createCommandBuffers(); | |
createSyncObjects(); | |
} | |
void mainLoop() { | |
while (!glfwWindowShouldClose(window)) { | |
glfwPollEvents(); | |
drawFrame(); | |
} | |
vkDeviceWaitIdle(device); | |
} | |
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, nullptr); | |
} | |
void cleanup() { | |
cleanupSwapChain(); | |
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { | |
vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); | |
vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); | |
vkDestroyFence(device, inFlightFences[i], nullptr); | |
} | |
vkDestroyCommandPool(device, commandPool, nullptr); | |
vkDestroyDevice(device, nullptr); | |
if (enableValidationLayers) { | |
DestroyDebugReportCallbackEXT(instance, callback, nullptr); | |
} | |
vkDestroySurfaceKHR(instance, surface, nullptr); | |
vkDestroyInstance(instance, nullptr); | |
glfwDestroyWindow(window); | |
glfwTerminate(); | |
} | |
void recreateSwapChain() { | |
// BEGIN kbob | |
//- int width, height; | |
//- glfwGetWindowSize(window, &width, &height); | |
//- if (width == 0 || height == 0) return; | |
// END kbob | |
vkDeviceWaitIdle(device); | |
cleanupSwapChain(); | |
createSwapChain(); | |
createImageViews(); | |
createRenderPass(); | |
createGraphicsPipeline(); | |
createFramebuffers(); | |
createCommandBuffers(); | |
} | |
void createInstance() { | |
if (enableValidationLayers && !checkValidationLayerSupport()) { | |
throw std::runtime_error("validation layers requested, but not available!"); | |
} | |
VkApplicationInfo appInfo = {}; | |
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; | |
appInfo.pApplicationName = "Hello Triangle"; | |
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); | |
appInfo.pEngineName = "No Engine"; | |
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); | |
appInfo.apiVersion = VK_API_VERSION_1_0; | |
VkInstanceCreateInfo createInfo = {}; | |
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; | |
createInfo.pApplicationInfo = &appInfo; | |
auto extensions = getRequiredExtensions(); | |
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size()); | |
createInfo.ppEnabledExtensionNames = extensions.data(); | |
if (enableValidationLayers) { | |
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size()); | |
createInfo.ppEnabledLayerNames = validationLayers.data(); | |
} else { | |
createInfo.enabledLayerCount = 0; | |
} | |
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { | |
throw std::runtime_error("failed to create instance!"); | |
} | |
} | |
void setupDebugCallback() { | |
if (!enableValidationLayers) return; | |
VkDebugReportCallbackCreateInfoEXT createInfo = {}; | |
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; | |
createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; | |
createInfo.pfnCallback = debugCallback; | |
if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { | |
throw std::runtime_error("failed to set up debug callback!"); | |
} | |
} | |
void createSurface() { | |
if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { | |
throw std::runtime_error("failed to create window surface!"); | |
} | |
} | |
void pickPhysicalDevice() { | |
uint32_t deviceCount = 0; | |
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); | |
if (deviceCount == 0) { | |
throw std::runtime_error("failed to find GPUs with Vulkan support!"); | |
} | |
std::vector<VkPhysicalDevice> devices(deviceCount); | |
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); | |
for (const auto& device : devices) { | |
if (isDeviceSuitable(device)) { | |
physicalDevice = device; | |
break; | |
} | |
} | |
if (physicalDevice == VK_NULL_HANDLE) { | |
throw std::runtime_error("failed to find a suitable GPU!"); | |
} | |
} | |
void createLogicalDevice() { | |
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 createInfo = {}; | |
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>(deviceExtensions.size()); | |
createInfo.ppEnabledExtensionNames = deviceExtensions.data(); | |
if (enableValidationLayers) { | |
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size()); | |
createInfo.ppEnabledLayerNames = validationLayers.data(); | |
} else { | |
createInfo.enabledLayerCount = 0; | |
} | |
if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { | |
throw std::runtime_error("failed to create logical device!"); | |
} | |
vkGetDeviceQueue(device, indices.graphicsFamily, 0, &graphicsQueue); | |
vkGetDeviceQueue(device, indices.presentFamily, 0, &presentQueue); | |
} | |
void 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 = {}; | |
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; | |
QueueFamilyIndices indices = findQueueFamilies(physicalDevice); | |
uint32_t queueFamilyIndices[] = {(uint32_t) indices.graphicsFamily, (uint32_t) indices.presentFamily}; | |
if (indices.graphicsFamily != indices.presentFamily) { | |
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; | |
createInfo.queueFamilyIndexCount = 2; | |
createInfo.pQueueFamilyIndices = queueFamilyIndices; | |
} else { | |
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; | |
} | |
createInfo.preTransform = swapChainSupport.capabilities.currentTransform; | |
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; | |
createInfo.presentMode = presentMode; | |
createInfo.clipped = VK_TRUE; | |
if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { | |
throw std::runtime_error("failed to create swap chain!"); | |
} | |
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); | |
swapChainImages.resize(imageCount); | |
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); | |
swapChainImageFormat = surfaceFormat.format; | |
swapChainExtent = extent; | |
} | |
void createImageViews() { | |
swapChainImageViews.resize(swapChainImages.size()); | |
for (size_t i = 0; i < swapChainImages.size(); i++) { | |
VkImageViewCreateInfo createInfo = {}; | |
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; | |
createInfo.image = swapChainImages[i]; | |
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; | |
createInfo.format = swapChainImageFormat; | |
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; | |
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; | |
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; | |
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; | |
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | |
createInfo.subresourceRange.baseMipLevel = 0; | |
createInfo.subresourceRange.levelCount = 1; | |
createInfo.subresourceRange.baseArrayLayer = 0; | |
createInfo.subresourceRange.layerCount = 1; | |
if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { | |
throw std::runtime_error("failed to create image views!"); | |
} | |
} | |
} | |
void createRenderPass() { | |
VkAttachmentDescription colorAttachment = {}; | |
colorAttachment.format = swapChainImageFormat; | |
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; | |
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; | |
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; | |
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; | |
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; | |
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; | |
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; | |
VkAttachmentReference colorAttachmentRef = {}; | |
colorAttachmentRef.attachment = 0; | |
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
VkSubpassDescription subpass = {}; | |
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; | |
subpass.colorAttachmentCount = 1; | |
subpass.pColorAttachments = &colorAttachmentRef; | |
VkSubpassDependency dependency = {}; | |
dependency.srcSubpass = VK_SUBPASS_EXTERNAL; | |
dependency.dstSubpass = 0; | |
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; | |
dependency.srcAccessMask = 0; | |
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; | |
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; | |
VkRenderPassCreateInfo 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 (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { | |
throw std::runtime_error("failed to create render pass!"); | |
} | |
} | |
void createGraphicsPipeline() { | |
auto vertShaderCode = readFile("shaders/vert.spv"); | |
auto fragShaderCode = readFile("shaders/frag.spv"); | |
VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); | |
VkShaderModule 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}; | |
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) { | |
throw std::runtime_error("failed to create pipeline layout!"); | |
} | |
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; | |
if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { | |
throw std::runtime_error("failed to create graphics pipeline!"); | |
} | |
vkDestroyShaderModule(device, fragShaderModule, nullptr); | |
vkDestroyShaderModule(device, vertShaderModule, nullptr); | |
} | |
void createFramebuffers() { | |
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) { | |
throw std::runtime_error("failed to create framebuffer!"); | |
} | |
} | |
} | |
void createCommandPool() { | |
QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); | |
VkCommandPoolCreateInfo poolInfo = {}; | |
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; | |
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; | |
if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { | |
throw std::runtime_error("failed to create command pool!"); | |
} | |
} | |
void createCommandBuffers() { | |
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) { | |
throw std::runtime_error("failed to allocate command buffers!"); | |
} | |
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; | |
if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) { | |
throw std::runtime_error("failed to begin recording command buffer!"); | |
} | |
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) { | |
throw std::runtime_error("failed to record command buffer!"); | |
} | |
} | |
} | |
void createSyncObjects() { | |
imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); | |
renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); | |
inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); | |
VkSemaphoreCreateInfo semaphoreInfo = {}; | |
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; | |
VkFenceCreateInfo fenceInfo = {}; | |
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; | |
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; | |
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { | |
if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS || | |
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS || | |
vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) { | |
throw std::runtime_error("failed to create synchronization objects for a frame!"); | |
} | |
} | |
} | |
void drawFrame() { | |
// BEGIN kbob | |
int width, height; | |
glfwGetFramebufferSize(window, &width, &height); | |
if (width != 0 && height != 0 && | |
(width != swapChainExtent.width || height != swapChainExtent.height)) { | |
std::cout << "window size changed: " << width << " x " << height << std::endl; | |
recreateSwapChain(); | |
} | |
// END kbob | |
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, std::numeric_limits<uint64_t>::max()); | |
vkResetFences(device, 1, &inFlightFences[currentFrame]); | |
uint32_t imageIndex; | |
VkResult result = vkAcquireNextImageKHR(device, swapChain, std::numeric_limits<uint64_t>::max(), imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); | |
// BEGIN kbob | |
if (result != VK_SUCCESS ) { | |
throw std::runtime_error("failed to acquire swap chain image!"); | |
} | |
//- if (result == VK_ERROR_OUT_OF_DATE_KHR) { | |
//- recreateSwapChain(); | |
//- return; | |
//- } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { | |
//- throw std::runtime_error("failed to acquire swap chain image!"); | |
//- } | |
// END kbob | |
VkSubmitInfo submitInfo = {}; | |
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; | |
VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; | |
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[] = {renderFinishedSemaphores[currentFrame]}; | |
submitInfo.signalSemaphoreCount = 1; | |
submitInfo.pSignalSemaphores = signalSemaphores; | |
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) { | |
throw std::runtime_error("failed to submit draw command buffer!"); | |
} | |
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); | |
// BEGIN kbob | |
if (result != VK_SUCCESS) { | |
throw std::runtime_error("failed to present swap chain image!"); | |
} | |
//- if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { | |
//- recreateSwapChain(); | |
//- } else if (result != VK_SUCCESS) { | |
//- throw std::runtime_error("failed to present swap chain image!"); | |
//- } | |
// END kbob | |
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; | |
} | |
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; | |
} | |
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) { | |
if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED) { | |
return {VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}; | |
} | |
for (const auto& availableFormat : availableFormats) { | |
if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { | |
return availableFormat; | |
} | |
} | |
return availableFormats[0]; | |
} | |
VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR> availablePresentModes) { | |
VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR; | |
for (const auto& availablePresentMode : availablePresentModes) { | |
if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { | |
return availablePresentMode; | |
} else if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) { | |
bestMode = availablePresentMode; | |
} | |
} | |
return bestMode; | |
} | |
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { | |
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) { | |
return capabilities.currentExtent; | |
} else { | |
int width, height; | |
// BEGIN kbob | |
//- glfwGetWindowSize(window, &width, &height); | |
glfwGetFramebufferSize(window, &width, &height); | |
// END kbob | |
VkExtent2D actualExtent = { | |
static_cast<uint32_t>(width), | |
static_cast<uint32_t>(height) | |
}; | |
actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); | |
actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); | |
return actualExtent; | |
} | |
} | |
SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) { | |
SwapChainSupportDetails details; | |
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); | |
uint32_t formatCount; | |
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); | |
if (formatCount != 0) { | |
details.formats.resize(formatCount); | |
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); | |
} | |
uint32_t presentModeCount; | |
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); | |
if (presentModeCount != 0) { | |
details.presentModes.resize(presentModeCount); | |
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); | |
} | |
return details; | |
} | |
bool isDeviceSuitable(VkPhysicalDevice device) { | |
QueueFamilyIndices indices = findQueueFamilies(device); | |
bool extensionsSupported = checkDeviceExtensionSupport(device); | |
bool swapChainAdequate = false; | |
if (extensionsSupported) { | |
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); | |
swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); | |
} | |
return indices.isComplete() && extensionsSupported && swapChainAdequate; | |
} | |
bool checkDeviceExtensionSupport(VkPhysicalDevice device) { | |
uint32_t extensionCount; | |
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); | |
std::vector<VkExtensionProperties> availableExtensions(extensionCount); | |
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); | |
std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); | |
for (const auto& extension : availableExtensions) { | |
requiredExtensions.erase(extension.extensionName); | |
} | |
return requiredExtensions.empty(); | |
} | |
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { | |
QueueFamilyIndices indices; | |
uint32_t queueFamilyCount = 0; | |
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); | |
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount); | |
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); | |
int i = 0; | |
for (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; | |
} | |
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_REPORT_EXTENSION_NAME); | |
} | |
return extensions; | |
} | |
bool checkValidationLayerSupport() { | |
uint32_t layerCount; | |
vkEnumerateInstanceLayerProperties(&layerCount, nullptr); | |
std::vector<VkLayerProperties> availableLayers(layerCount); | |
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); | |
for (const char* layerName : validationLayers) { | |
bool layerFound = false; | |
for (const auto& layerProperties : availableLayers) { | |
if (strcmp(layerName, layerProperties.layerName) == 0) { | |
layerFound = true; | |
break; | |
} | |
} | |
if (!layerFound) { | |
return false; | |
} | |
} | |
return true; | |
} | |
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; | |
} | |
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData) { | |
std::cerr << "validation layer: " << msg << std::endl; | |
return VK_FALSE; | |
} | |
}; | |
int main() { | |
HelloTriangleApplication app; | |
try { | |
app.run(); | |
} catch (const std::runtime_error& e) { | |
std::cerr << e.what() << std::endl; | |
return EXIT_FAILURE; | |
} | |
return EXIT_SUCCESS; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment