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.
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;
}