handle window resizing

This commit is contained in:
IlyaShurupov 2024-11-18 11:32:41 +03:00
parent 2d9c2a45b5
commit 494e455bf4

View file

@ -8,6 +8,17 @@
#include <vector>
#include <cstring>
#include <fstream>
#include <cassert>
#include <chrono>
using TimePoint = std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>;
using TimeMs = long;
TimeMs timeDeltaMs(const TimePoint& point) {
auto timeDelta = std::chrono::system_clock::now() - point;
auto timeDelayMs = std::chrono::duration_cast<std::chrono::milliseconds>(timeDelta).count();
return timeDelayMs;
}
const std::vector<const char *> deviceExtensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
@ -35,13 +46,27 @@ public:
cleanup();
}
void scheduleWindowResize(int sizeX, int sizeY) {
mWindowSizeDirtyFlagTime = std::chrono::system_clock::now();
mWindowSizeDirtyFlag = true;
mWindowFramebufferSize = std::make_pair(sizeX, sizeY);
assert(!(sizeX <= 0 || sizeY <= 0));
}
private:
void initWindow() {
// glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11);
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
// glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
mWindow = glfwCreateWindow(800, 600, "App", nullptr, nullptr);
scheduleWindowResize(800, 600);
glfwSetWindowUserPointer(mWindow, this);
glfwSetFramebufferSizeCallback(mWindow, [](GLFWwindow* window, int sizeX, int sizeY){
((Application*) glfwGetWindowUserPointer(window))->scheduleWindowResize(sizeX, sizeY);
});
}
void initVulkan() {
@ -52,7 +77,10 @@ private:
createLogicalDevice();
getQueues();
createSwapChain();
int width, height;
glfwGetFramebufferSize(mWindow, &width, &height);
createSwapChain(width, height);
createSwapChainImageViews();
createRenderPass();
@ -69,6 +97,15 @@ private:
void mainLoop() {
while (!glfwWindowShouldClose(mWindow)) {
glfwPollEvents();
if (mWindowSizeDirtyFlag) {
auto timeDelayMs = timeDeltaMs(mWindowSizeDirtyFlagTime);
if (timeDelayMs > mWindowSizeApplyMinDelay) {
recreateSwapChain(mWindowFramebufferSize.first, mWindowFramebufferSize.second);
mWindowSizeDirtyFlag = false;
}
}
drawFrame();
}
@ -291,12 +328,24 @@ private:
}
}
void createSwapChain() {
void recreateSwapChain(int sizeX, int sizeY) {
vkDeviceWaitIdle(mDevice);
destroySwapChainFramebuffers();
destroySwapChainImageViews();
destroySwapChain();
createSwapChain(sizeX, sizeY);
createSwapChainImageViews();
createSwapChainFramebuffers();
}
void createSwapChain(int sizeX, int sizeY) {
SwapChainSupportDetails details = querySwapChainSupportDetails(mPhysicalDevice);
VkSurfaceFormatKHR surfaceFormat = pickSwapChainSurfaceFormat(details);
VkPresentModeKHR presentMode = pickSwapChainPresentMode(details);
VkExtent2D extent2D = pickSwapChainExtent(details.capabilities);
VkExtent2D extent2D = pickSwapChainExtent(details.capabilities, sizeX, sizeY);
// +1 so will not have to wait for device to finish frame to query an image to render to
uint32_t imageCount = details.capabilities.minImageCount + 1;
@ -363,16 +412,13 @@ private:
return VK_PRESENT_MODE_FIFO_KHR; // guaranteed exists
}
VkExtent2D pickSwapChainExtent(const VkSurfaceCapabilitiesKHR &capabilities) {
static VkExtent2D pickSwapChainExtent(const VkSurfaceCapabilitiesKHR &capabilities, int sizeX, int sizeY) {
// if set by vulkan just keep it
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
return capabilities.currentExtent;
}
int width, height;
glfwGetFramebufferSize(mWindow, &width, &height);
VkExtent2D out = {(uint32_t) width, (uint32_t) height};
VkExtent2D out = {(uint32_t) sizeX, (uint32_t) sizeY};
out.width = std::clamp(out.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
out.height = std::clamp(out.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
@ -445,6 +491,10 @@ private:
}
}
void destroySwapChain() {
vkDestroySwapchainKHR(mDevice, mSwapChain, nullptr);
}
void destroySwapChainImageViews() {
for (const auto &imageView: mSwapChainImageViews) {
vkDestroyImageView(mDevice, imageView, nullptr);
@ -816,9 +866,14 @@ private:
.pImageIndices = &imageIndex,
};
if (vkQueuePresentKHR(mPresentQueue, &presentInfo) != VK_SUCCESS) {
throw std::runtime_error("failed to submit to presentation queue");
vkQueuePresentKHR(mPresentQueue, &presentInfo);
/* Somehow res is always VK_SUCCESS
if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) {
recreateSwapChain();
} else if (res != VK_SUCCESS) {
throw std::runtime_error("cannot acquire new khr image");
}
*/
}
void destroySynchronizationObjects() {
@ -838,8 +893,8 @@ private:
destroyGraphicsPipeline();
destroySwapChainImageViews();
destroySwapChain();
vkDestroySwapchainKHR(mDevice, mSwapChain, nullptr);
vkDestroyDevice(mDevice, nullptr);
vkDestroySurfaceKHR(mInstance, mSurface, nullptr);
vkDestroyInstance(mInstance, nullptr);
@ -850,6 +905,11 @@ private:
private:
GLFWwindow *mWindow = nullptr;
bool mWindowSizeDirtyFlag = true;
TimePoint mWindowSizeDirtyFlagTime = std::chrono::system_clock::now();
TimeMs mWindowSizeApplyMinDelay = 200;
std::pair<int, int> mWindowFramebufferSize = { 0, 0 };
uint32_t mGraphicsQueueFamilyIndex = -1;
uint32_t mPresentationQueueFamilyIndex = -1;