-2

I use my own Win32 implementation together with Vulkan. I have the following test function that resizes my window.

void Window::Test()
{
    RECT rect = { 0, 0, 1200, 1200 };
    DWORD style = WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | CS_OWNDC | WS_MAXIMIZEBOX;
    DWORD exStyle = WS_EX_APPWINDOW | WS_EX_TOPMOST;
    AdjustWindowRectExForDpi(&rect, style, false, exStyle, GetDpiForWindow(windowHandle));

    SetWindowPos(windowHandle, HWND_TOP, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER);
}

Doing this does resize the window, but both acquireNextImageKHR and presentKHR return vk::eSuccess still. This causes the swap chain to be invalid but not recreated, leading to a crash when I call presentKHR the frame after the window resize.

Acquire image code:

    uint32_t swapChainImageIndex;
    {
        vk::Result acquireResult = device.acquireNextImageKHR(swapchain, UINT64_MAX, *imageAvailableSemaphores[currentFrameIndex], nullptr, &swapChainImageIndex);

        if(acquireResult == vk::Result::eErrorOutOfDateKHR)
        {
            //Recreate swapchain here
            // * DOES NOT GET HIT *
            return;
        }
        else if(acquireResult != vk::Result::eSuccess && acquireResult != vk::Result::eSuboptimalKHR)
        {
            //Crash
            // * DOES NOT GET HIT *
        }
    }

Present code:

    //Below line crashes after window is resized
    vk::Result presentResult = presentQueue.presentKHR(presentInfo);
    if(presentResult == vk::Result::eErrorOutOfDateKHR || presentResult == vk::Result::eSuboptimalKHR)
    {
        //Recreate swapchain here
        // * DOES NOT GET HIT *
    }
    else if(presentResult != vk::Result::eSuccess)
    {
        //Crash
        // * DOES NOT GET HIT *
    }

I have a control project that uses GLFW. When I resize my window in that project the result does become VK_ERROR_OUT_OF_DATE_KHR. I am able to set a bool that tracks if the window is resized manually, but I believe that should not be necessary since the control project does not require such a bool or callback.

I have tried returning 0 in what I believe to be the relevant window callback like this:

case WM_SIZE:
{
    return 0;
}
case WM_SIZING:
{
    return 0;
}

This doesn't seem to affect anything, and I still get the same crash.

6
  • 1
    Note your question will be strengthened greatly by a minimal reproducible example (MRE) that other users can copy and paste into their tools, build, run, experiment with, and test potential solutions against. As an added bonus, creating am MRE often ends early with the discovery of the problem and solution. Make the MRE early in the question-asking process and you rarely get to the question posting stage. Commented May 28 at 18:43
  • Since it is Vulkan, a "minimum" example that gives the expected behavior is going to be hundreds of lines long. I could post it here but I fear scaring off potential readers. I put the code I think is most relevant in the code above to try to compensate. Commented May 28 at 19:46
  • I do expect a buttload of boiler plate when graphics toolkits are involved, but with a MRE you know you have included the problem code in the example. With snippets of code you'll find a similar problem with the readership to having too much code. Folk will look at the snippets, not see an obvious problem, and move on because there is nothing else they can do without a code example they can drop into their tools to play with. Commented May 28 at 20:30
  • One of the great teachings of Stack Overflow is the actual bug is rarely where the asker thinks it is, and that's most of the reason why they can't find it. Commented May 28 at 20:31
  • @user4581301 Hey, that actually helped me find the issue! Thank you very much! Commented May 28 at 22:27

1 Answer 1

1

Found the issue! When the result is not a success and exceptions are enabled in Vulkan-hpp it seems to silently ignore the error and give a success instead. Seems like very weird behavior, but possible to fix by disabling exceptions.

Code to demo for those curious. Scroll to the bottom for the most interesting parts.

#include <chrono>
#include <fstream>
#include <iostream>
#include <optional>
#include <set>
#include <vulkan/vulkan.hpp>
#include <Windows.h>

const char* requestedPhysicalExtension = VK_KHR_SURFACE_EXTENSION_NAME;
const char* requestedLogicalDeviceExtension = VK_KHR_SWAPCHAIN_EXTENSION_NAME;

HINSTANCE hInstance;
HWND windowHandle;
bool running = true;
vk::UniqueInstance vulkanInstance;
vk::SurfaceKHR vulkanSurface;
vk::PhysicalDevice physicalDevice;
vk::UniqueDevice logicalDevice;
std::optional<uint32_t> graphicsFamily;
std::optional<uint32_t> presentFamily;
vk::Queue graphicsQueue;
vk::Queue presentQueue;
vk::UniqueHandle<vk::DebugUtilsMessengerEXT, vk::detail::DispatchLoaderDynamic> debugMessenger;
vk::detail::DispatchLoaderDynamic vulkanLoaderDynamic;
PFN_vkGetInstanceProcAddr getInstanceProcAddr = nullptr;

vk::UniqueSwapchainKHR swapChain;
std::vector<vk::Image> swapChainImages;
std::vector<vk::UniqueImageView> swapChainImageViews;
vk::Extent2D swapChainExtent;
vk::Format swapChainImageFormat;

vk::UniqueRenderPass renderPass;
std::vector<vk::UniqueFramebuffer> framebuffers;

vk::UniqueCommandPool commandPool;
std::vector<vk::UniqueCommandBuffer> commandBuffers;

vk::UniquePipeline graphicsPipeline;

vk::UniqueSemaphore imageAvailableSemaphore;
vk::UniqueSemaphore renderFinishedSemaphore;
vk::UniqueFence inFlightFence;

const char* enabledLayerName = "VK_LAYER_KHRONOS_validation";

struct SwapChainSupportDetails
{
    vk::SurfaceCapabilitiesKHR capabilities;
    std::vector<vk::SurfaceFormatKHR> formats;
    std::vector<vk::PresentModeKHR> presentModes;
};

vk::Bool32 DebugMessengerCallback(vk::DebugUtilsMessageSeverityFlagBitsEXT severity,
                                  vk::DebugUtilsMessageTypeFlagsEXT types, vk::DebugUtilsMessengerCallbackDataEXT const* callbackData,
                                  void* userData)
{
    switch(severity)
    {
        using SeverityBits = vk::DebugUtilsMessageSeverityFlagBitsEXT;
        case SeverityBits::eVerbose:
            std::cout << "Vulkan Validation Layer" << callbackData->pMessage << std::endl;
        break;
        case SeverityBits::eInfo:
            std::cout << "Vulkan Validation Layer" << callbackData->pMessage << std::endl;
        break;
        case SeverityBits::eWarning:
            std::cerr << "Vulkan Validation Layer" << callbackData->pMessage << std::endl;
        break;
        case SeverityBits::eError:
            std::cerr << "Vulkan Validation Layer" << callbackData->pMessage << std::endl;
        break;
    }

    return VK_TRUE;
}

LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_CLOSE:
        {
            DestroyWindow(hWnd);
            break;
        }
        case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        }
        case WM_CANCELMODE:
        {
            return 0;
        }
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
}

void InitWindow()
{
    WNDCLASS wndClass {};
    wndClass.lpszClassName = L"Test";
    wndClass.hInstance = hInstance;
    wndClass.hIcon = LoadIcon(nullptr, IDI_WINLOGO);
    wndClass.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wndClass.lpfnWndProc = WindowProcedure;
    wndClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;

    RegisterClass(&wndClass);

    DWORD style = WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_MAXIMIZEBOX | WS_VISIBLE;

    RECT rect;
    rect.left = 250;
    rect.top= 250;
    rect.right = rect.left + 800;
    rect.bottom = rect.top + 600;

    AdjustWindowRect(&rect, style, false);

    windowHandle = CreateWindowEx(0,
        L"Test",
        L"Test Window",
        style,
        rect.left,
        rect.top,
        rect.right - rect.left,
        rect.bottom - rect.top,
        nullptr,
        nullptr,
        hInstance,
        nullptr);

    ShowWindow(windowHandle, SW_SHOW);
}

bool ProcessMessages()
{
    MSG message;

    while (PeekMessage(&message, nullptr, 0u, 0u, PM_REMOVE))
    {
        if (message.message == WM_QUIT)
        {
            return false;
        }

        TranslateMessage(&message);
        DispatchMessage(&message);
    }

    return true;
}

void InitVulkan()
{
    HMODULE platformHandle = LoadLibraryA("vulkan-1.dll");
    if(!platformHandle)
    {
        std::cerr << "Failed to load vulkan." << std::endl;
        exit(-1);
    }

    getInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(GetProcAddress(platformHandle, "vkGetInstanceProcAddr"));
    if(!getInstanceProcAddr)
    {
        std::cerr << "Failed to load vulkan." << std::endl;
        exit(-1);
    }
    vulkanLoaderDynamic.init(getInstanceProcAddr);
    
    vk::ApplicationInfo appInfo("Test", VK_MAKE_API_VERSION(0, 1, 0, 0), "No Engine", VK_MAKE_API_VERSION(0, 1, 0, 0), VK_API_VERSION_1_0);

    std::vector<const char*> requiredExtensionNames = { VK_KHR_SURFACE_EXTENSION_NAME, "VK_KHR_win32_surface", VK_EXT_DEBUG_UTILS_EXTENSION_NAME };
    std::vector<vk::ExtensionProperties> availableExtensions = vk::enumerateInstanceExtensionProperties();

    size_t requiredExtensionsFound = 0;
    for(const char* requiredExtensionName : requiredExtensionNames)
    {
        for(const vk::ExtensionProperties& extension : availableExtensions)
        {
            if(strcmp(requiredExtensionName, extension.extensionName) == 0)
            {
                requiredExtensionsFound++;
            }
        }
    }

    if(requiredExtensionsFound < requiredExtensionNames.size())
    {
        throw std::runtime_error("Not enough extensions found");
    }

    vk::DebugUtilsMessengerCreateInfoEXT debugLayerCreateInfo {};
    debugLayerCreateInfo.messageSeverity = vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError;
    debugLayerCreateInfo.messageType =  vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation;
    debugLayerCreateInfo.pfnUserCallback = DebugMessengerCallback;
    vk::InstanceCreateInfo instanceCreateInfo({}, &appInfo, 1, &enabledLayerName, requiredExtensionNames.size(), requiredExtensionNames.data(), &debugLayerCreateInfo);

    vulkanInstance = vk::createInstanceUnique(instanceCreateInfo);
    vulkanLoaderDynamic.init(*vulkanInstance, getInstanceProcAddr);
}

void SetupDebugMessenger()
{
    vk::DebugUtilsMessengerCreateInfoEXT createInfo {};
    createInfo.messageSeverity = vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError;
    createInfo.messageType =  vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation;
    createInfo.pfnUserCallback = DebugMessengerCallback;

    debugMessenger = vulkanInstance->createDebugUtilsMessengerEXTUnique(createInfo, nullptr, vulkanLoaderDynamic);
}

void CreateSurface()
{
    vk::Win32SurfaceCreateInfoKHR surfaceCreateInfo({}, hInstance, windowHandle);
    vulkanSurface = vulkanInstance->createWin32SurfaceKHR(surfaceCreateInfo);
}

void InitPhysicalDevice()
{
    std::vector<vk::PhysicalDevice> vulkanPhysicalDevices = vulkanInstance->enumeratePhysicalDevices();

    for(const vk::PhysicalDevice& vulkanDevice : vulkanPhysicalDevices)
    {
        //Pick first dedicated GPU
        if(vulkanDevice.getProperties().deviceType == vk::PhysicalDeviceType::eDiscreteGpu)
        {
            physicalDevice = vulkanDevice;
            break;
        }
    }

    std::vector<vk::QueueFamilyProperties> queueFamilyProperties = physicalDevice.getQueueFamilyProperties();
    uint32_t i = 0;
    for(const vk::QueueFamilyProperties& queueFamily : queueFamilyProperties)
    {
        if(queueFamily.queueFlags & vk::QueueFlagBits::eGraphics)
            graphicsFamily = i;

        vk::Bool32 hasPresentSupport = physicalDevice.getSurfaceSupportKHR(i, vulkanSurface);
        
        if(hasPresentSupport)
            presentFamily = i;

        if(graphicsFamily && presentFamily)
            return;
        
        i++;
    }
}

void CreateSyncObjects()
{
    vk::SemaphoreCreateInfo semaphoreCreateInfo;
    imageAvailableSemaphore = logicalDevice->createSemaphoreUnique(semaphoreCreateInfo);
    renderFinishedSemaphore = logicalDevice->createSemaphoreUnique(semaphoreCreateInfo);
    vk::FenceCreateInfo fenceCreateInfo(vk::FenceCreateFlagBits::eSignaled);
    inFlightFence = logicalDevice->createFenceUnique(fenceCreateInfo);
}

void CreateLogicalDevice()
{
    std::set<uint32_t> uniqueQueueFamilies = {*graphicsFamily, *presentFamily};
    std::vector<vk::DeviceQueueCreateInfo> deviceQueueCreateInfos(uniqueQueueFamilies.size());
    float queuePriority = 1.0f;
    size_t i = 0;
    for(uint32_t queueFamily : uniqueQueueFamilies)
    {
        deviceQueueCreateInfos[i] = vk::DeviceQueueCreateInfo({}, queueFamily, 1, &queuePriority);
        i++;
    }

    vk::PhysicalDeviceFeatures deviceFeatures {};

    vk::DeviceCreateInfo deviceCreateInfo({}, static_cast<uint32_t>(deviceQueueCreateInfos.size()), deviceQueueCreateInfos.data(), 0, nullptr, 1, &requestedLogicalDeviceExtension, &deviceFeatures);

    logicalDevice = physicalDevice.createDeviceUnique(deviceCreateInfo);

    graphicsQueue = logicalDevice->getQueue(*graphicsFamily, 0);
    presentQueue = logicalDevice->getQueue(*presentFamily, 0);
}

vk::SurfaceFormatKHR PickSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& availableFormats)
{
    for(const vk::SurfaceFormatKHR& availableFormat : availableFormats)
    {
        if(availableFormat.format == vk::Format::eB8G8R8A8Srgb && availableFormat.colorSpace == vk::ColorSpaceKHR::eVkColorspaceSrgbNonlinear)
            return availableFormat;
    }

    return availableFormats[0];
}
vk::PresentModeKHR PickPresentMode(const std::vector<vk::PresentModeKHR>& availablePresentModes)
{
    for(const vk::PresentModeKHR& availablePresentMode : availablePresentModes)
    {
        if(availablePresentMode == vk::PresentModeKHR::eMailbox)
            return availablePresentMode;
    }

    return vk::PresentModeKHR::eFifo;
}
vk::Extent2D PickExtent(const vk::SurfaceCapabilitiesKHR& capabilities)
{
    if(capabilities.currentExtent.width != UINT_MAX)
    {
        return capabilities.currentExtent;
    }
    
    vk::Extent2D actualExtent(800, 600);

    actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
    actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);

    return actualExtent;
}

vk::UniqueImageView CreateImageView(const vk::UniqueDevice& logicalDevice, const vk::Image& image, vk::Format format, vk::ImageAspectFlagBits aspectFlags, uint32_t mipLevels)
{
    vk::ImageSubresourceRange subresourceRange(aspectFlags, 0, mipLevels, 0, 1);
    vk::ComponentMapping componentMapping;
    
    vk::ImageViewCreateInfo createInfo({}, image, vk::ImageViewType::e2D, format, componentMapping, subresourceRange);

    return logicalDevice->createImageViewUnique(createInfo);
}

void CreateSwapChain()
{
    SwapChainSupportDetails details;
    details.capabilities = physicalDevice.getSurfaceCapabilitiesKHR(vulkanSurface);
    details.formats = physicalDevice.getSurfaceFormatsKHR(vulkanSurface);
    details.presentModes = physicalDevice.getSurfacePresentModesKHR(vulkanSurface);

    uint32_t imageCount = details.capabilities.minImageCount + 1;

    if(details.capabilities.maxImageCount > 0 && imageCount > details.capabilities.maxImageCount)
        imageCount = details.capabilities.maxImageCount;
    
    const vk::SurfaceFormatKHR surfaceFormat = PickSurfaceFormat(details.formats);
    swapChainImageFormat = surfaceFormat.format;

    swapChainExtent = PickExtent(details.capabilities);
    vk::PresentModeKHR presentMode = PickPresentMode(details.presentModes);

    const bool isGraphicsFamilyAlsoPresent = *graphicsFamily == *presentFamily;
    vk::SharingMode sharingMode;
    std::vector<uint32_t> queueFamilyIndices;
    if(isGraphicsFamilyAlsoPresent)
    {
        sharingMode = vk::SharingMode::eExclusive;
        queueFamilyIndices = { *graphicsFamily };
    }
    else
    {
        sharingMode = vk::SharingMode::eConcurrent;
        queueFamilyIndices = { *graphicsFamily, *presentFamily };
    }

    vk::SwapchainCreateInfoKHR createInfo({}, vulkanSurface, imageCount, surfaceFormat.format, surfaceFormat.colorSpace, swapChainExtent, 1, vk::ImageUsageFlagBits::eColorAttachment,
                                          sharingMode, static_cast<uint32_t>(queueFamilyIndices.size()), queueFamilyIndices.data(), details.capabilities.currentTransform, vk::CompositeAlphaFlagBitsKHR::eOpaque,
                                          presentMode, vk::True, nullptr);

    swapChain = logicalDevice->createSwapchainKHRUnique(createInfo);

    swapChainImages = logicalDevice->getSwapchainImagesKHR(*swapChain);

    swapChainImageViews.reserve(swapChainImages.size());
    for(const vk::Image& image : swapChainImages)
    {
        swapChainImageViews.emplace_back(CreateImageView(logicalDevice, image, surfaceFormat.format, vk::ImageAspectFlagBits::eColor, 1));
    }
}

void  CreateRenderPass()
{
    //Color
    vk::AttachmentDescription colorAttachment({}, swapChainImageFormat, vk::SampleCountFlagBits::e1, vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore,
                                              vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare, vk::ImageLayout::eUndefined, vk::ImageLayout::ePresentSrcKHR);
    vk::AttachmentReference colorAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal);

    //Subpasses
    vk::SubpassDescription subpass({}, vk::PipelineBindPoint::eGraphics, 0, nullptr, 1, &colorAttachmentRef,
                                   nullptr, nullptr, 0, nullptr);

    std::vector<vk::AttachmentDescription> attachments = { colorAttachment };
    vk::RenderPassCreateInfo renderPassCreateInfo({}, static_cast<uint32_t>(attachments.size()), attachments.data(), 1, &subpass, 0, nullptr);

    renderPass = logicalDevice->createRenderPassUnique(renderPassCreateInfo);
}

void CreateCommandPool()
{
    vk::CommandPoolCreateInfo createInfo(vk::CommandPoolCreateFlagBits::eResetCommandBuffer, *graphicsFamily);
    commandPool = logicalDevice->createCommandPoolUnique(createInfo);
}

void CreateCommandBuffer()
{
    vk::CommandBufferAllocateInfo allocInfo(*commandPool, vk::CommandBufferLevel::ePrimary, 1);

    commandBuffers = logicalDevice->allocateCommandBuffersUnique(allocInfo);
}

void CreateFramebuffers()
{
    framebuffers.reserve(swapChainImageViews.size());
    for(const vk::UniqueImageView& imageView : swapChainImageViews)
    {
        vk::ImageView attachments[] = { *imageView };

        vk::FramebufferCreateInfo framebufferCreateInfo({}, *renderPass, 1, attachments, swapChainExtent.width, swapChainExtent.height, 1);
        framebuffers.emplace_back(logicalDevice->createFramebufferUnique(framebufferCreateInfo));
    }
}

std::vector<char> LoadShaderFile(const std::string& fileName)
{
    std::ifstream file(fileName, std::ios::ate | std::ios::binary);

    if(!file.is_open())
    {
        char errorBuffer[256];
        strerror_s(errorBuffer, sizeof(errorBuffer), errno);
        std::cerr << "Vulkan Shaders" << std::format("Failed trying to open shader file \"{}\"! Reason: {}", fileName, errorBuffer) << std::endl;
    }

    size_t fileSize = static_cast<size_t>(file.tellg());
    std::vector<char> fileBuffer(fileSize);

    file.seekg(0);
    file.read(fileBuffer.data(), fileSize);

    file.close();

    return fileBuffer;
}

vk::UniqueShaderModule CreateShaderStage(const std::vector<char>& code)
{
    vk::ShaderModuleCreateInfo createInfo({}, code.size(), reinterpret_cast<const uint32_t*>(code.data()));
    return logicalDevice->createShaderModuleUnique(createInfo);
}

void CreateGraphicsPipeline()
{
    std::vector<char> vertexShaderCode = LoadShaderFile("triangle_vertex.spv");
    std::vector<char> fragmentShaderCode = LoadShaderFile("triangle_fragment.spv");

    vk::UniqueShaderModule vertexShaderModule = CreateShaderStage(vertexShaderCode);
    vk::UniqueShaderModule fragmentShaderModule = CreateShaderStage(fragmentShaderCode);

    vk::PipelineShaderStageCreateInfo vertexShaderStage({}, vk::ShaderStageFlagBits::eVertex, *vertexShaderModule, "main");
    vk::PipelineShaderStageCreateInfo fragmentShaderStage({}, vk::ShaderStageFlagBits::eFragment, *fragmentShaderModule, "main");

    vk::PipelineShaderStageCreateInfo shaderStages[] = { vertexShaderStage, fragmentShaderStage };

    vk::PipelineVertexInputStateCreateInfo vertexInputCreateInfo({}, 0, nullptr, 0, nullptr);
    
    std::vector<vk::DynamicState> dynamicStates = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
    vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo({}, static_cast<uint32_t>(dynamicStates.size()), dynamicStates.data());

    vk::PipelineInputAssemblyStateCreateInfo inputAssemblyCreateInfo({}, vk::PrimitiveTopology::eTriangleList, vk::False);

    vk::Viewport viewport(0.0f, 0.0f, static_cast<float>(swapChainExtent.width), static_cast<float>(swapChainExtent.height), 0.0f, 1.0f);
    vk::Rect2D scissor(vk::Offset2D(0, 0), swapChainExtent);
    vk::PipelineViewportStateCreateInfo viewportStateCreateInfo({}, 1, &viewport, 1, &scissor);

    //Fixed state
    vk::PipelineRasterizationStateCreateInfo rasterizerCreateInfo({}, vk::False, vk::False, vk::PolygonMode::eFill, vk::CullModeFlagBits::eBack,
                                                                  vk::FrontFace::eClockwise, vk::False, 0.0f, 0.0f, 0.0f, 1.0f);
    vk::PipelineMultisampleStateCreateInfo multisampleCreateInfo({}, vk::SampleCountFlagBits::e1, vk::False, 1.0f, nullptr, vk::False, vk::False);
    vk::PipelineDepthStencilStateCreateInfo depthStencilCreateInfo {};

    //Color blending
    vk::PipelineColorBlendAttachmentState colorBlendAttachment(vk::False, vk::BlendFactor::eOne, vk::BlendFactor::eZero, vk::BlendOp::eAdd,
                                                                vk::BlendFactor::eOne, vk::BlendFactor::eZero, vk::BlendOp::eAdd, 
                                                                vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA);
    vk::PipelineColorBlendStateCreateInfo colorBlendingCreateInfo({}, vk::False, vk::LogicOp::eClear, 1, &colorBlendAttachment, {0, 0, 0, 0});

    vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo({}, 0, nullptr, 0, nullptr);
    auto pipelineLayout = logicalDevice->createPipelineLayoutUnique(pipelineLayoutCreateInfo);

    vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo({}, 2, shaderStages, &vertexInputCreateInfo, &inputAssemblyCreateInfo, nullptr, &viewportStateCreateInfo, &rasterizerCreateInfo,
                                                              &multisampleCreateInfo, &depthStencilCreateInfo, &colorBlendingCreateInfo, &dynamicStateCreateInfo, *pipelineLayout, *renderPass, 0, nullptr, -1);

    auto [result, pipelines] = logicalDevice->createGraphicsPipelinesUnique(nullptr, graphicsPipelineCreateInfo);
    if(result != vk::Result::eSuccess)
        std::cerr << "Failed to create graphics pipeline!" << std::endl;

    graphicsPipeline = std::move(pipelines.front());
}

void Render()
{
    logicalDevice->waitForFences(1, &*inFlightFence, vk::True, UINT64_MAX);
    logicalDevice->resetFences(1, &*inFlightFence);

    uint32_t swapChainImageIndex;
    {
        //Always returns success if exceptions are enabled
        vk::Result acquireResult = logicalDevice->acquireNextImageKHR(*swapChain, UINT64_MAX, *imageAvailableSemaphore, nullptr, &swapChainImageIndex);

        if(acquireResult == vk::Result::eErrorOutOfDateKHR)
        {
            std::cout << "Time to recreate swapchain!" << std::endl;
            //Recreate swapchain here
            return;
        }
        else if(acquireResult != vk::Result::eSuccess && acquireResult != vk::Result::eSuboptimalKHR)
        {
            //Something went wrong, crash
        }
    }

    vk::CommandBuffer commandBuffer = *commandBuffers[0];
    commandBuffer.reset();
    vk::CommandBufferBeginInfo beginInfo;

    commandBuffer.begin(beginInfo);

    vk::Rect2D renderArea(vk::Offset2D(0, 0), swapChainExtent);
    vk::ClearValue clearValue(vk::ClearColorValue(0, 0, 0, 1));
    vk::RenderPassBeginInfo renderPassBeginInfo(*renderPass, *framebuffers[swapChainImageIndex], renderArea, 1, &clearValue);

    commandBuffer.beginRenderPass(&renderPassBeginInfo, vk::SubpassContents::eInline);
    commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, *graphicsPipeline);
    
    vk::Viewport viewport(0.0f, 0.0f, static_cast<float>(swapChainExtent.width), static_cast<float>(swapChainExtent.height), 0.0f, 1.0f);
    commandBuffer.setViewport(0, 1, &viewport);

    vk::Rect2D scissor(vk::Offset2D(0, 0), swapChainExtent);
    commandBuffer.setScissor(0, 1, &scissor);

    commandBuffer.draw(3, 1, 0, 0);

    commandBuffer.endRenderPass();
    commandBuffer.end();

    std::array<vk::PipelineStageFlags, 1> waitStages = { vk::PipelineStageFlagBits::eColorAttachmentOutput };
    vk::Semaphore waitSemaphores[] = { *imageAvailableSemaphore };
    vk::Semaphore signalSemaphores[] = { *renderFinishedSemaphore };
    
    vk::SubmitInfo submitInfo(1, waitSemaphores, waitStages.data(), 1, &commandBuffer, 1, signalSemaphores);
    if(graphicsQueue.submit(1, &submitInfo, *inFlightFence) != vk::Result::eSuccess)
        std::cerr << "Failed to submit draw command buffer!" << std::endl;

    vk::SwapchainKHR swapChains[] = { *swapChain };
    vk::Result results[] = { vk::Result::eSuccess, vk::Result::eSuboptimalKHR, vk::Result::eErrorOutOfDateKHR };
    vk::PresentInfoKHR presentInfo(1, signalSemaphores, 1, swapChains, &swapChainImageIndex, nullptr);
    vk::Result presentResult = presentQueue.presentKHR(presentInfo);
    if(presentResult == vk::Result::eErrorOutOfDateKHR || presentResult == vk::Result::eSuboptimalKHR)
    {
        std::cout << "Time to recreate swapchain!" << std::endl;
        //Recreate swapchain here
    }
    else if(presentResult != vk::Result::eSuccess)
    {
        //Something went wrong, crash
    }
}

int main(int argc, char* argv[])
{
    InitWindow();
    InitVulkan();
    SetupDebugMessenger();
    CreateSurface();
    InitPhysicalDevice();
    CreateLogicalDevice();
    CreateSyncObjects();
    CreateSwapChain();
    CreateRenderPass();
    CreateFramebuffers();
    CreateCommandPool();
    CreateCommandBuffer();
    CreateGraphicsPipeline();

    //Uncommenting the try/catch will print the exception
    try
    {
    std::chrono::time_point previousTime = std::chrono::high_resolution_clock::now();
    double resizeTimer = 5.f;
        while (running)
        {
            if (!ProcessMessages())
                running = false;

            std::chrono::time_point currentTime = std::chrono::high_resolution_clock::now();
            float deltaTime = std::chrono::duration(currentTime - previousTime).count();
            resizeTimer -= deltaTime;

            if(resizeTimer <= 0)
            {
                SetWindowPos(windowHandle, nullptr, 0, 0, 1200, 1200, SWP_NOMOVE | SWP_NOZORDER);
                UpdateWindow(windowHandle);
            }

            Render();
        }
    }
    catch(std::exception e)
    {
        //Prints "vk::Queue::presentKHR: ErrorOutOfDateKHR" when window is resized
        std::cout << e.what();
    }

    logicalDevice->waitIdle();
    vulkanInstance.release();
    return 0;
}

Sign up to request clarification or add additional context in comments.

1 Comment

This a ridiculously large amount of code

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.