Add swapchain recreation

This commit is contained in:
Grayson Riffe 2025-02-13 16:33:45 -06:00
parent 3088a42a07
commit 798be67cc5
3 changed files with 89 additions and 40 deletions

View File

@ -29,6 +29,8 @@ namespace nf::client {
void Window::setDisplay(DisplayConfig& config) {
NFLog("Setting window display");
bool wasShown = IsWindowVisible(m_handle);
show(false);
// TODO: Only use "active" monitor when starting windowed
POINT cursor = {};
@ -55,12 +57,15 @@ namespace nf::client {
SetWindowLongPtr(m_handle, GWL_STYLE, m_styleFullscreen);
windowX = monitorX, windowY = monitorY;
windowWidth = monitorWidth, windowHeight = monitorHeight;
m_currentWidth = windowWidth, m_currentHeight = windowHeight;
config.width = windowWidth, config.height = windowHeight;
break;
}
}
SetWindowPos(m_handle, nullptr, windowX, windowY, windowWidth, windowHeight, SWP_NOZORDER | SWP_FRAMECHANGED);
show(wasShown);
}
void Window::runLoop() {

View File

@ -20,6 +20,7 @@ namespace nf::client::render {
, m_renderPassOutput()
, m_swapchain()
, m_swapchainImageFormat()
, m_swapchainExtent()
, m_swapchainImages()
, m_swapchainImageViews()
, m_swapchainFramebuffers()
@ -39,6 +40,7 @@ namespace nf::client::render {
createDevice();
createSwapchain();
createOutputRenderPass();
createSwapchainFramebuffers();
createOutputPipeline();
createFrameExecutors();
@ -209,9 +211,9 @@ namespace nf::client::render {
if (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)
chosenPresentMode = presentModes[i];
VkExtent2D swapchainExtent = surfaceCapabilities.currentExtent;
if (swapchainExtent.width == std::numeric_limits<uint32_t>::max())
swapchainExtent.width = m_display.width, swapchainExtent.height = m_display.height;
m_swapchainExtent = surfaceCapabilities.currentExtent;
if (m_swapchainExtent.width == std::numeric_limits<uint32_t>::max())
m_swapchainExtent = { m_display.width, m_display.height };
uint32_t numRequestedImages = surfaceCapabilities.minImageCount + (surfaceCapabilities.minImageCount != surfaceCapabilities.maxImageCount ? 1 : 0);
@ -220,7 +222,7 @@ namespace nf::client::render {
swapchainCI.minImageCount = numRequestedImages;
swapchainCI.imageFormat = m_swapchainImageFormat = chosenSurfaceFormat.format;
swapchainCI.imageColorSpace = chosenSurfaceFormat.colorSpace;
swapchainCI.imageExtent = swapchainExtent;
swapchainCI.imageExtent = m_swapchainExtent;
swapchainCI.imageArrayLayers = 1;
swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
@ -297,6 +299,22 @@ namespace nf::client::render {
NFError("Could not create Vulkan output render pass.");
}
void RenderEngine::createSwapchainFramebuffers() {
// TODO: Won't need these forever
m_swapchainFramebuffers.resize(m_swapchainImageViews.size());
for (int i = 0; i < m_swapchainImageViews.size(); i++) {
VkFramebufferCreateInfo framebufferCI = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
framebufferCI.renderPass = m_renderPassOutput;
framebufferCI.attachmentCount = 1;
framebufferCI.pAttachments = &m_swapchainImageViews[i];
framebufferCI.width = m_swapchainExtent.width, framebufferCI.height = m_swapchainExtent.height;
framebufferCI.layers = 1;
if (vkCreateFramebuffer(m_device, &framebufferCI, nullptr, &m_swapchainFramebuffers[i]) != VK_SUCCESS)
NFError("Could not create framebuffer.");
}
}
void RenderEngine::createOutputPipeline() {
// First, create shader modules
std::string outputShaderVertexBinary, outputShaderFragmentBinary;
@ -315,10 +333,10 @@ namespace nf::client::render {
outputShaderStages[1].module = outputShaderFragmentModule.getHandle();
// Specify dynamic state
VkDynamicState dynamicState = VK_DYNAMIC_STATE_VIEWPORT;
std::vector<VkDynamicState> dynamicStates = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
VkPipelineDynamicStateCreateInfo dynamicStateCI = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
dynamicStateCI.dynamicStateCount = 1;
dynamicStateCI.pDynamicStates = &dynamicState;
dynamicStateCI.dynamicStateCount = dynamicStates.size();
dynamicStateCI.pDynamicStates = dynamicStates.data();
// Specify vertex input state
VkPipelineVertexInputStateCreateInfo vertexInputStateCI = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
@ -328,11 +346,9 @@ namespace nf::client::render {
inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
// Specify viewport state
VkRect2D scissor = { {0, 0}, {m_display.width, m_display.height} };
VkPipelineViewportStateCreateInfo viewportStateCI = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
viewportStateCI.viewportCount = 1;
viewportStateCI.scissorCount = 1;
viewportStateCI.pScissors = &scissor;
// Specify rasterization state
VkPipelineRasterizationStateCreateInfo rasterizationCI = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
@ -373,20 +389,6 @@ namespace nf::client::render {
if (vkCreateGraphicsPipelines(m_device, nullptr, 1, &pipelineCI, nullptr, &m_pipelineOutput) != VK_SUCCESS)
NFError("Could not create graphics pipeline.");
// TODO: For now, create swapchain framebuffers
m_swapchainFramebuffers.resize(m_swapchainImageViews.size());
for (int i = 0; i < m_swapchainImageViews.size(); i++) {
VkFramebufferCreateInfo framebufferCI = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
framebufferCI.renderPass = m_renderPassOutput;
framebufferCI.attachmentCount = 1;
framebufferCI.pAttachments = &m_swapchainImageViews[i];
framebufferCI.width = m_display.width, framebufferCI.height = m_display.height;
framebufferCI.layers = 1;
if (vkCreateFramebuffer(m_device, &framebufferCI, nullptr, &m_swapchainFramebuffers[i]) != VK_SUCCESS)
NFError("Could not create framebuffer.");
}
}
void RenderEngine::createFrameExecutors() {
@ -424,11 +426,19 @@ namespace nf::client::render {
// First, wait for previous frame to complete
vkWaitForFences(m_device, 1, &fenceFrameInFlight, VK_TRUE, UINT64_MAX);
vkResetFences(m_device, 1, &fenceFrameInFlight);
// Get next swapchain image
// Get next swapchain image and recreate if necessary
uint32_t nextImageIndex = 0;
vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, semaphoreImageAvailable, nullptr, &nextImageIndex);
VkResult result = vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, semaphoreImageAvailable, nullptr, &nextImageIndex);
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
recreateSwapchain();
return;
}
else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
NFError("Could not aquire next swapchain image.");
// Reset fence for this frame
vkResetFences(m_device, 1, &fenceFrameInFlight);
// Begin recording
VkCommandBufferBeginInfo commandBufferBI = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
@ -441,7 +451,7 @@ namespace nf::client::render {
VkRenderPassBeginInfo renderPassBI = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
renderPassBI.renderPass = m_renderPassOutput;
renderPassBI.framebuffer = m_swapchainFramebuffers[nextImageIndex];
renderPassBI.renderArea.extent = { m_display.width, m_display.height };
renderPassBI.renderArea.extent = m_swapchainExtent;
renderPassBI.clearValueCount = 1;
renderPassBI.pClearValues = &black;
vkCmdBeginRenderPass(commandBuffer, &renderPassBI, VK_SUBPASS_CONTENTS_INLINE);
@ -449,11 +459,13 @@ namespace nf::client::render {
// Bind output pipeline
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineOutput);
// Set viewport
// Set viewport and scissor
VkViewport viewport = {};
viewport.width = static_cast<float>(m_display.width), viewport.height = static_cast<float>(m_display.height);
viewport.width = static_cast<float>(m_swapchainExtent.width), viewport.height = static_cast<float>(m_swapchainExtent.height);
viewport.maxDepth = 1.0;
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
VkRect2D scissor = { {}, m_swapchainExtent };
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
// Draw
vkCmdDraw(commandBuffer, 3, 1, 0, 0);
@ -487,14 +499,46 @@ namespace nf::client::render {
presentInfo.pSwapchains = &m_swapchain;
presentInfo.pImageIndices = &nextImageIndex;
vkQueuePresentKHR(m_queuePresent, &presentInfo);
result = vkQueuePresentKHR(m_queuePresent, &presentInfo);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR)
recreateSwapchain();
else if (result != VK_SUCCESS)
NFError("Could not present image.");
// Advance to next frame executor
m_currentFrameExecutor = (m_currentFrameExecutor + 1) % m_numFramesInFlight;
}
RenderEngine::~RenderEngine() {
void RenderEngine::setDisplay(DisplayConfig config) {
m_display = config;
m_window->setDisplay(m_display);
}
void RenderEngine::recreateSwapchain() {
waitIdle();
destroySwapchain();
createSwapchain();
createSwapchainFramebuffers();
}
void RenderEngine::destroySwapchain() {
for (int i = 0; i < m_swapchainFramebuffers.size(); i++)
vkDestroyFramebuffer(m_device, m_swapchainFramebuffers[i], nullptr);
for (int i = 0; i < m_swapchainImageViews.size(); i++)
vkDestroyImageView(m_device, m_swapchainImageViews[i], nullptr);
vkDestroySwapchainKHR(m_device, m_swapchain, nullptr);
}
void RenderEngine::waitIdle() {
vkDeviceWaitIdle(m_device);
}
RenderEngine::~RenderEngine() {
waitIdle();
for (int i = 0; i < m_frameExecutors.size(); i++) {
vkDestroyFence(m_device, m_frameExecutors[i].fenceFrameInFlight, nullptr);
@ -502,19 +546,11 @@ namespace nf::client::render {
vkDestroySemaphore(m_device, m_frameExecutors[i].semaphoreImageAvailable, nullptr);
}
destroySwapchain();
vkDestroyCommandPool(m_device, m_commandPool, nullptr);
for (int i = 0; i < m_swapchainFramebuffers.size(); i++)
vkDestroyFramebuffer(m_device, m_swapchainFramebuffers[i], nullptr);
vkDestroyPipeline(m_device, m_pipelineOutput, nullptr);
vkDestroyPipelineLayout(m_device, m_pipelineLayoutOutput, nullptr);
vkDestroyRenderPass(m_device, m_renderPassOutput, nullptr);
for (int i = 0; i < m_swapchainImageViews.size(); i++)
vkDestroyImageView(m_device, m_swapchainImageViews[i], nullptr);
vkDestroySwapchainKHR(m_device, m_swapchain, nullptr);
vkDestroyDevice(m_device, nullptr);
vkDestroySurfaceKHR(m_instance, m_surface, nullptr);
vkDestroyInstance(m_instance, nullptr);

View File

@ -16,6 +16,7 @@ namespace nf::client::render {
RenderEngine(std::shared_ptr<Window> window, DisplayConfig display);
void doFrame();
void setDisplay(DisplayConfig config);
~RenderEngine();
private:
@ -25,9 +26,15 @@ namespace nf::client::render {
void createDevice();
void createSwapchain();
void createOutputRenderPass();
void createSwapchainFramebuffers();
void createOutputPipeline();
void createFrameExecutors();
void recreateSwapchain();
void destroySwapchain();
void waitIdle();
std::shared_ptr<Window> m_window;
DisplayConfig m_display;
@ -43,6 +50,7 @@ namespace nf::client::render {
// Swapchain
VkSwapchainKHR m_swapchain;
VkFormat m_swapchainImageFormat;
VkExtent2D m_swapchainExtent;
std::vector<VkImage> m_swapchainImages;
std::vector<VkImageView> m_swapchainImageViews;
std::vector<VkFramebuffer> m_swapchainFramebuffers;