// Blaze.cpp : This file contains the 'main' function. Program execution begins and ends there. // #include #include #include #include #include #include #include #include #include #define ASSERT(COND) assert((COND)) #define VK_CHECK(RESULT) ASSERT((RESULT) == VK_SUCCESS) constexpr uint32_t WIDTH = 1280; constexpr uint32_t HEIGHT = 720; constexpr uint32_t NUM_FRAMES = 3; uint32_t Clamp(uint32_t const val, uint32_t const minVal, uint32_t const maxVal) { return std::min(maxVal, std::max(val, minVal)); } int main() { volkInitialize(); SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); SDL_Window* window = SDL_CreateWindow("Blaze Test", WIDTH, HEIGHT, SDL_WINDOW_VULKAN); VkApplicationInfo constexpr applicationInfo = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pNext = nullptr, .pApplicationName = "Test", .applicationVersion = VK_MAKE_API_VERSION(0, 0, 1, 0), .pEngineName = "Blaze", .engineVersion = VK_MAKE_API_VERSION(0, 0, 1, 0), .apiVersion = VK_API_VERSION_1_3, }; uint32_t instanceExtensionCount; char const* const* instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceExtensionCount); VkInstanceCreateInfo const instanceCreateInfo = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pNext = nullptr, .flags = 0, .pApplicationInfo = &applicationInfo, .enabledLayerCount = 0, .ppEnabledLayerNames = nullptr, .enabledExtensionCount = instanceExtensionCount, .ppEnabledExtensionNames = instanceExtensions, }; VkInstance instance; VK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); volkLoadInstance(instance); VkSurfaceKHR surface; ASSERT(SDL_Vulkan_CreateSurface(window, instance, nullptr, &surface)); VkPhysicalDevice physicalDeviceInUse = nullptr; VkDevice device = nullptr; uint32_t directQueueFamilyIndex = 0; VkQueue directQueue; { uint32_t physicalDeviceCount; VK_CHECK(vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr)); SDL_Log("Found %u GPUs", physicalDeviceCount); std::vector physicalDevices(physicalDeviceCount); VK_CHECK(vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices.data())); std::vector queueFamilyProperties; for (VkPhysicalDevice const physicalDevice : physicalDevices) { VkPhysicalDeviceProperties properties; vkGetPhysicalDeviceProperties(physicalDevice, &properties); SDL_Log("GPU: %s", properties.deviceName); SDL_Log("- API Version %d.%d.%d", VK_API_VERSION_MAJOR(properties.apiVersion), VK_API_VERSION_MINOR(properties.apiVersion), VK_API_VERSION_PATCH(properties.apiVersion)); constexpr static uint32_t API_PATCH_BITS = 0xFFF; if ((properties.apiVersion & (~API_PATCH_BITS)) < VK_API_VERSION_1_3) { continue; } if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) { continue; } uint32_t queueFamilyCount; vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); queueFamilyProperties.resize(queueFamilyCount); vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilyProperties.data()); for (uint32_t queueFamilyIndex = 0; VkQueueFamilyProperties const qfp : queueFamilyProperties) { bool hasGraphicsSupport = false; bool hasComputeSupport = false; bool hasTransferSupport = false; bool hasPresentSupport = false; SDL_Log("- Queue [%d]", queueFamilyIndex); if (qfp.queueFlags & VK_QUEUE_GRAPHICS_BIT) { hasGraphicsSupport = true; SDL_Log("-- Graphic"); } if (qfp.queueFlags & VK_QUEUE_COMPUTE_BIT) { hasComputeSupport = true; SDL_Log("-- Compute"); } if (qfp.queueFlags & VK_QUEUE_TRANSFER_BIT) { hasTransferSupport = true; SDL_Log("-- Transfer"); } VkBool32 isSurfaceSupported; VK_CHECK(vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface, &isSurfaceSupported)); if (isSurfaceSupported) { hasPresentSupport = true; SDL_Log("-- Present"); } if (hasGraphicsSupport and hasComputeSupport and hasTransferSupport and hasPresentSupport) { physicalDeviceInUse = physicalDevice; directQueueFamilyIndex = queueFamilyIndex; break; } ++queueFamilyIndex; } } ASSERT(physicalDeviceInUse); float priority = 1.0f; VkDeviceQueueCreateInfo queueCreateInfo = { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .pNext = nullptr, .flags = 0, .queueFamilyIndex = directQueueFamilyIndex, .queueCount = 1, .pQueuePriorities = &priority, }; VkPhysicalDeviceVulkan13Features constexpr features13 = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, .pNext = nullptr, .synchronization2 = true, .dynamicRendering = true, }; VkPhysicalDeviceFeatures features = { .depthClamp = true, .samplerAnisotropy = true, }; std::array enabledDeviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; VkDeviceCreateInfo const deviceCreateInfo = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = &features13, .flags = 0, .queueCreateInfoCount = 1, .pQueueCreateInfos = &queueCreateInfo, .enabledLayerCount = 0, .ppEnabledLayerNames = nullptr, .enabledExtensionCount = enabledDeviceExtensions.size(), .ppEnabledExtensionNames = enabledDeviceExtensions.data(), .pEnabledFeatures = &features, }; VK_CHECK(vkCreateDevice(physicalDeviceInUse, &deviceCreateInfo, nullptr, &device)); vkGetDeviceQueue(device, directQueueFamilyIndex, 0, &directQueue); } VkFormat swapchainFormat; VkExtent2D swapchainExtent = { WIDTH, HEIGHT }; VkSwapchainKHR swapchain; std::vector swapchainImages; std::vector swapchainViews; { VkSurfaceCapabilitiesKHR capabilities; VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDeviceInUse, surface, &capabilities)); // Image Count Calculation uint32_t swapchainImageCount = NUM_FRAMES; if (capabilities.maxImageCount > 0) { swapchainImageCount = std::min(swapchainImageCount, capabilities.maxImageCount); } swapchainImageCount = std::max(swapchainImageCount, capabilities.minImageCount); // Image Size calculation { auto [minWidth, minHeight] = capabilities.minImageExtent; auto [maxWidth, maxHeight] = capabilities.maxImageExtent; swapchainExtent.width = Clamp(swapchainExtent.width, minWidth, maxWidth); swapchainExtent.height = Clamp(swapchainExtent.height, minHeight, maxHeight); } uint32_t surfaceFormatCount; vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDeviceInUse, surface, &surfaceFormatCount, nullptr); std::vector surfaceFormats(surfaceFormatCount); vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDeviceInUse, surface, &surfaceFormatCount, surfaceFormats.data()); VkSurfaceFormatKHR format; format.format = VK_FORMAT_UNDEFINED; for (auto& surfaceFormat : surfaceFormats) { if (surfaceFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { SDL_Log("Color Space SRGB Found %d", surfaceFormat.format); if (surfaceFormat.format == VK_FORMAT_R8G8B8A8_SRGB) { format = surfaceFormat; break; } if (surfaceFormat.format == VK_FORMAT_B8G8R8A8_SRGB) { format = surfaceFormat; break; } if (surfaceFormat.format == VK_FORMAT_R8G8B8A8_UNORM) { format = surfaceFormat; } } } ASSERT(format.format != VK_FORMAT_UNDEFINED); swapchainFormat = format.format; uint32_t presentModeCount; vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDeviceInUse, surface, &presentModeCount, nullptr); std::vector presentModes(presentModeCount); vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDeviceInUse, surface, &presentModeCount, presentModes.data()); VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; for (VkPresentModeKHR presentModeIter : presentModes) { if (presentModeIter == VK_PRESENT_MODE_FIFO_RELAXED_KHR) { presentMode = presentModeIter; break; } if (presentModeIter == VK_PRESENT_MODE_MAILBOX_KHR) { presentMode = presentModeIter; } } VkSwapchainCreateInfoKHR const swapchainCreateInfo = { .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .pNext = nullptr, .flags = 0, .surface = surface, .minImageCount = swapchainImageCount, .imageFormat = format.format, .imageColorSpace = format.colorSpace, .imageExtent = swapchainExtent, .imageArrayLayers = 1, .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, .presentMode = presentMode, .clipped = false, .oldSwapchain = nullptr, }; VK_CHECK(vkCreateSwapchainKHR(device, &swapchainCreateInfo, nullptr, &swapchain)); swapchainImageCount = 0; vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, nullptr); swapchainImages.resize(swapchainImageCount); vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, swapchainImages.data()); for (VkImage image : swapchainImages) { VkImageViewCreateInfo const viewCreateInfo = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .pNext = nullptr, .flags = 0, .image = image, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = format.format, .components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, } }; VkImageView view; VK_CHECK(vkCreateImageView(device, &viewCreateInfo, nullptr, &view)); swapchainViews.push_back(view); } } VkPipelineLayout pipelineLayout; VkPipeline trianglePipeline; { size_t dataSize; void* rawData = SDL_LoadFile("Triangle.spv", &dataSize); ASSERT(dataSize % 4 == 0); if (not rawData) { SDL_LogError(SDL_LOG_CATEGORY_SYSTEM, "%s", SDL_GetError()); abort(); } auto data = static_cast(rawData); VkShaderModuleCreateInfo const shaderModuleCreateInfo = { .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .pNext = nullptr, .flags = 0, .codeSize = dataSize, .pCode = data, }; VkShaderModule shaderModule; VK_CHECK(vkCreateShaderModule(device, &shaderModuleCreateInfo, nullptr, &shaderModule)); VkPipelineLayoutCreateInfo constexpr pipelineLayoutCreateInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .pNext = nullptr, .flags = 0, .setLayoutCount = 0, .pSetLayouts = nullptr, .pushConstantRangeCount = 0, .pPushConstantRanges = nullptr, }; VK_CHECK(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); std::array stages = { VkPipelineShaderStageCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_VERTEX_BIT, .module = shaderModule, .pName = "VertexMain", .pSpecializationInfo = nullptr, }, VkPipelineShaderStageCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = shaderModule, .pName = "FragmentMain", .pSpecializationInfo = nullptr, } }; VkPipelineVertexInputStateCreateInfo constexpr vertexInputState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .vertexBindingDescriptionCount = 0, .pVertexBindingDescriptions = nullptr, .vertexAttributeDescriptionCount = 0, .pVertexAttributeDescriptions = nullptr, }; VkPipelineInputAssemblyStateCreateInfo constexpr inputAssembly = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, .primitiveRestartEnable = VK_FALSE, }; VkPipelineTessellationStateCreateInfo constexpr tessellationState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .patchControlPoints = 0, }; VkPipelineViewportStateCreateInfo constexpr viewportState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .viewportCount = 1, .pViewports = nullptr, .scissorCount = 1, .pScissors = nullptr, }; VkPipelineRasterizationStateCreateInfo constexpr rasterizationState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .depthClampEnable = VK_TRUE, .rasterizerDiscardEnable = VK_FALSE, .polygonMode = VK_POLYGON_MODE_FILL, .cullMode = VK_CULL_MODE_NONE, .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, .depthBiasEnable = VK_FALSE, .depthBiasConstantFactor = 0.0f, .depthBiasClamp = 0.0f, .depthBiasSlopeFactor = 0.0f, .lineWidth = 1.0f, }; VkPipelineMultisampleStateCreateInfo constexpr multisampleState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, .sampleShadingEnable = VK_FALSE, .minSampleShading = 0.0f, .pSampleMask = nullptr, .alphaToCoverageEnable = VK_FALSE, .alphaToOneEnable = VK_FALSE, }; VkPipelineDepthStencilStateCreateInfo constexpr depthStencilState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .depthTestEnable = VK_FALSE, .depthWriteEnable = VK_FALSE, .depthCompareOp = VK_COMPARE_OP_ALWAYS, .depthBoundsTestEnable = VK_FALSE, .stencilTestEnable = VK_FALSE, .front = {}, .back = {}, .minDepthBounds = 0.0f, .maxDepthBounds = 1.0f, }; VkPipelineColorBlendAttachmentState constexpr colorBlendAttachmentState = { .blendEnable = VK_FALSE, .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, .colorBlendOp = VK_BLEND_OP_ADD, .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, .alphaBlendOp = VK_BLEND_OP_ADD, .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, }; VkPipelineColorBlendStateCreateInfo const colorBlendState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .logicOpEnable = VK_FALSE, .logicOp = VK_LOGIC_OP_COPY, .attachmentCount = 1, .pAttachments = &colorBlendAttachmentState, .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, }; std::array constexpr dynamicStates = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo const dynamicStateCreateInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .dynamicStateCount = static_cast(dynamicStates.size()), .pDynamicStates = dynamicStates.data() }; VkPipelineRenderingCreateInfoKHR const renderingCreateInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR, .colorAttachmentCount = 1, .pColorAttachmentFormats = &swapchainFormat, }; VkGraphicsPipelineCreateInfo const graphicsPipelineCreateInfo = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = &renderingCreateInfo, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), .pVertexInputState = &vertexInputState, .pInputAssemblyState = &inputAssembly, .pTessellationState = &tessellationState, .pViewportState = &viewportState, .pRasterizationState = &rasterizationState, .pMultisampleState = &multisampleState, .pDepthStencilState = &depthStencilState, .pColorBlendState = &colorBlendState, .pDynamicState = &dynamicStateCreateInfo, .layout = pipelineLayout, .renderPass = nullptr, .subpass = 0, .basePipelineHandle = nullptr, .basePipelineIndex = 0, }; VK_CHECK(vkCreateGraphicsPipelines(device, nullptr, 1, &graphicsPipelineCreateInfo, nullptr, &trianglePipeline)); vkDestroyShaderModule(device, shaderModule, nullptr); SDL_free(rawData); } // Create 1 command pool per frame. std::array commandPools; { VkCommandPoolCreateInfo const commandPoolCreateInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .pNext = nullptr, .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, .queueFamilyIndex = directQueueFamilyIndex, }; for (auto& commandPool : commandPools) { VK_CHECK(vkCreateCommandPool(device, &commandPoolCreateInfo, nullptr, &commandPool)); } } std::array commandBuffers; { uint32_t index = 0; for (auto& commandPool : commandPools) { VkCommandBufferAllocateInfo const commandBufferAllocateInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .pNext = nullptr, .commandPool = commandPool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = 1, }; VK_CHECK(vkAllocateCommandBuffers(device, &commandBufferAllocateInfo, &commandBuffers[index])); ++index; } } std::array imageAcquiredSemaphores; std::array renderFinishedSemaphores; std::array frameInUseFences; { VkSemaphoreCreateInfo constexpr semaphoreCreateInfo = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, .pNext = nullptr, .flags = 0 }; for (auto& semaphore : imageAcquiredSemaphores) { VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &semaphore)); } for (auto& semaphore : renderFinishedSemaphores) { VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &semaphore)); } VkFenceCreateInfo constexpr fenceCreateInfo = { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = nullptr, .flags = VK_FENCE_CREATE_SIGNALED_BIT, }; for (auto& fence : frameInUseFences) { VK_CHECK(vkCreateFence(device, &fenceCreateInfo, nullptr, &fence)); } } VkImageMemoryBarrier2 acquireToRenderBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, .pNext = nullptr, .srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, .srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, .dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, .dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, } }; VkDependencyInfo acquireToRenderDependency = { .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, .pNext = nullptr, .dependencyFlags = 0, .memoryBarrierCount = 0, .pMemoryBarriers = nullptr, .bufferMemoryBarrierCount = 0, .pBufferMemoryBarriers = nullptr, .imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &acquireToRenderBarrier, }; VkImageMemoryBarrier2 renderToPresentBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, .pNext = nullptr, .srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, .srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, .dstStageMask = VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT, .dstAccessMask = VK_ACCESS_2_NONE, .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, } }; VkDependencyInfo renderToPresentDependency = { .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, .pNext = nullptr, .dependencyFlags = 0, .memoryBarrierCount = 0, .pMemoryBarriers = nullptr, .bufferMemoryBarrierCount = 0, .pBufferMemoryBarriers = nullptr, .imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &renderToPresentBarrier, }; uint32_t frameIndex = 0; bool isRunning = true; while (isRunning) { SDL_PumpEvents(); SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_EVENT_QUIT: isRunning = false; break; default: break; } } VkFence fence = frameInUseFences[frameIndex]; VK_CHECK(vkWaitForFences(device, 1, &fence, VK_TRUE, std::numeric_limits::max())); // All resources of frame 'frameIndex' are free. VkSemaphore imageAcquiredSemaphore = imageAcquiredSemaphores[frameIndex]; uint32_t currentImageIndex; VK_CHECK(vkAcquireNextImageKHR(device, swapchain, std::numeric_limits::max(), imageAcquiredSemaphore, nullptr, ¤tImageIndex)); VK_CHECK(vkResetFences(device, 1, &fence)); VK_CHECK(vkResetCommandPool(device, commandPools[frameIndex], 0)); acquireToRenderBarrier.image = swapchainImages[currentImageIndex]; renderToPresentBarrier.image = swapchainImages[currentImageIndex]; VkCommandBuffer cmd = commandBuffers[frameIndex]; VkCommandBufferBeginInfo constexpr beginInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .pNext = nullptr, .flags = 0, .pInheritanceInfo = nullptr, }; VkClearColorValue constexpr static BLACK_CLEAR = { .float32 = { 0.0f, 0.0f, 0.0f, 1.0f }, }; VK_CHECK(vkBeginCommandBuffer(cmd, &beginInfo)); { VkRenderingAttachmentInfo const attachmentInfo = { .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, .pNext = nullptr, .imageView = swapchainViews[currentImageIndex], .imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, .resolveMode = VK_RESOLVE_MODE_NONE, .resolveImageView = nullptr, .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, .clearValue = {.color = BLACK_CLEAR}, }; VkRenderingInfo renderingInfo = { .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, .pNext = nullptr, .flags = 0, .renderArea = {.offset = {0,0}, .extent = swapchainExtent}, .layerCount = 1, .viewMask = 0, .colorAttachmentCount = 1, .pColorAttachments = &attachmentInfo, .pDepthAttachment = nullptr, .pStencilAttachment = nullptr, }; vkCmdPipelineBarrier2(cmd, &acquireToRenderDependency); vkCmdBeginRendering(cmd, &renderingInfo); { VkViewport viewport = { .x = 0, .y = static_cast(swapchainExtent.height), .width = static_cast(swapchainExtent.width), .height = -static_cast(swapchainExtent.height), .minDepth = 0.0f, .maxDepth = 1.0f, }; vkCmdSetViewport(cmd, 0, 1, &viewport); VkRect2D scissor = { .offset = {0, 0}, .extent = swapchainExtent, }; vkCmdSetScissor(cmd, 0, 1, &scissor); // Render Something? vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, trianglePipeline); vkCmdDraw(cmd, 3, 1, 0, 0); } vkCmdEndRendering(cmd); vkCmdPipelineBarrier2(cmd, &renderToPresentDependency); } VK_CHECK(vkEndCommandBuffer(cmd)); VkSemaphore renderingFinishedSemaphore = renderFinishedSemaphores[frameIndex]; VkPipelineStageFlags stageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo const submitInfo = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = nullptr, .waitSemaphoreCount = 1, .pWaitSemaphores = &imageAcquiredSemaphore, .pWaitDstStageMask = &stageMask, .commandBufferCount = 1, .pCommandBuffers = &cmd, .signalSemaphoreCount = 1, .pSignalSemaphores = &renderingFinishedSemaphore, }; VK_CHECK(vkQueueSubmit(directQueue, 1, &submitInfo, fence)); VkPresentInfoKHR const presentInfo = { .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .pNext = nullptr, .waitSemaphoreCount = 1, .pWaitSemaphores = &renderingFinishedSemaphore, .swapchainCount = 1, .pSwapchains = &swapchain, .pImageIndices = ¤tImageIndex, .pResults = nullptr, }; VK_CHECK(vkQueuePresentKHR(directQueue, &presentInfo)); frameIndex = (frameIndex + 1) % NUM_FRAMES; } VK_CHECK(vkDeviceWaitIdle(device)); for (VkFence fence : frameInUseFences) { vkDestroyFence(device, fence, nullptr); } for (VkSemaphore semaphore : imageAcquiredSemaphores) { vkDestroySemaphore(device, semaphore, nullptr); } for (VkSemaphore semaphore : renderFinishedSemaphores) { vkDestroySemaphore(device, semaphore, nullptr); } for (VkCommandPool commandPool : commandPools) { vkDestroyCommandPool(device, commandPool, nullptr); } for (VkImageView view : swapchainViews) { vkDestroyImageView(device, view, nullptr); } vkDestroyPipeline(device, trianglePipeline, nullptr); vkDestroyPipelineLayout(device, pipelineLayout, nullptr); vkDestroySwapchainKHR(device, swapchain, nullptr); vkDestroyDevice(device, nullptr); SDL_Vulkan_DestroySurface(instance, surface, nullptr); vkDestroyInstance(instance, nullptr); volkFinalize(); SDL_DestroyWindow(window); SDL_Quit(); }