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

View File

@ -20,6 +20,7 @@ namespace nf::client::render {
, m_renderPassOutput() , m_renderPassOutput()
, m_swapchain() , m_swapchain()
, m_swapchainImageFormat() , m_swapchainImageFormat()
, m_swapchainExtent()
, m_swapchainImages() , m_swapchainImages()
, m_swapchainImageViews() , m_swapchainImageViews()
, m_swapchainFramebuffers() , m_swapchainFramebuffers()
@ -39,6 +40,7 @@ namespace nf::client::render {
createDevice(); createDevice();
createSwapchain(); createSwapchain();
createOutputRenderPass(); createOutputRenderPass();
createSwapchainFramebuffers();
createOutputPipeline(); createOutputPipeline();
createFrameExecutors(); createFrameExecutors();
@ -209,9 +211,9 @@ namespace nf::client::render {
if (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR) if (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)
chosenPresentMode = presentModes[i]; chosenPresentMode = presentModes[i];
VkExtent2D swapchainExtent = surfaceCapabilities.currentExtent; m_swapchainExtent = surfaceCapabilities.currentExtent;
if (swapchainExtent.width == std::numeric_limits<uint32_t>::max()) if (m_swapchainExtent.width == std::numeric_limits<uint32_t>::max())
swapchainExtent.width = m_display.width, swapchainExtent.height = m_display.height; m_swapchainExtent = { m_display.width, m_display.height };
uint32_t numRequestedImages = surfaceCapabilities.minImageCount + (surfaceCapabilities.minImageCount != surfaceCapabilities.maxImageCount ? 1 : 0); uint32_t numRequestedImages = surfaceCapabilities.minImageCount + (surfaceCapabilities.minImageCount != surfaceCapabilities.maxImageCount ? 1 : 0);
@ -220,7 +222,7 @@ namespace nf::client::render {
swapchainCI.minImageCount = numRequestedImages; swapchainCI.minImageCount = numRequestedImages;
swapchainCI.imageFormat = m_swapchainImageFormat = chosenSurfaceFormat.format; swapchainCI.imageFormat = m_swapchainImageFormat = chosenSurfaceFormat.format;
swapchainCI.imageColorSpace = chosenSurfaceFormat.colorSpace; swapchainCI.imageColorSpace = chosenSurfaceFormat.colorSpace;
swapchainCI.imageExtent = swapchainExtent; swapchainCI.imageExtent = m_swapchainExtent;
swapchainCI.imageArrayLayers = 1; swapchainCI.imageArrayLayers = 1;
swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
@ -297,6 +299,22 @@ namespace nf::client::render {
NFError("Could not create Vulkan output render pass."); 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() { void RenderEngine::createOutputPipeline() {
// First, create shader modules // First, create shader modules
std::string outputShaderVertexBinary, outputShaderFragmentBinary; std::string outputShaderVertexBinary, outputShaderFragmentBinary;
@ -315,10 +333,10 @@ namespace nf::client::render {
outputShaderStages[1].module = outputShaderFragmentModule.getHandle(); outputShaderStages[1].module = outputShaderFragmentModule.getHandle();
// Specify dynamic state // 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 }; VkPipelineDynamicStateCreateInfo dynamicStateCI = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
dynamicStateCI.dynamicStateCount = 1; dynamicStateCI.dynamicStateCount = dynamicStates.size();
dynamicStateCI.pDynamicStates = &dynamicState; dynamicStateCI.pDynamicStates = dynamicStates.data();
// Specify vertex input state // Specify vertex input state
VkPipelineVertexInputStateCreateInfo vertexInputStateCI = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; 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; inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
// Specify viewport state // Specify viewport state
VkRect2D scissor = { {0, 0}, {m_display.width, m_display.height} };
VkPipelineViewportStateCreateInfo viewportStateCI = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; VkPipelineViewportStateCreateInfo viewportStateCI = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
viewportStateCI.viewportCount = 1; viewportStateCI.viewportCount = 1;
viewportStateCI.scissorCount = 1; viewportStateCI.scissorCount = 1;
viewportStateCI.pScissors = &scissor;
// Specify rasterization state // Specify rasterization state
VkPipelineRasterizationStateCreateInfo rasterizationCI = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; 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) if (vkCreateGraphicsPipelines(m_device, nullptr, 1, &pipelineCI, nullptr, &m_pipelineOutput) != VK_SUCCESS)
NFError("Could not create graphics pipeline."); 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() { void RenderEngine::createFrameExecutors() {
@ -424,11 +426,19 @@ namespace nf::client::render {
// First, wait for previous frame to complete // First, wait for previous frame to complete
vkWaitForFences(m_device, 1, &fenceFrameInFlight, VK_TRUE, UINT64_MAX); 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; 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 // Begin recording
VkCommandBufferBeginInfo commandBufferBI = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; 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 }; VkRenderPassBeginInfo renderPassBI = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
renderPassBI.renderPass = m_renderPassOutput; renderPassBI.renderPass = m_renderPassOutput;
renderPassBI.framebuffer = m_swapchainFramebuffers[nextImageIndex]; renderPassBI.framebuffer = m_swapchainFramebuffers[nextImageIndex];
renderPassBI.renderArea.extent = { m_display.width, m_display.height }; renderPassBI.renderArea.extent = m_swapchainExtent;
renderPassBI.clearValueCount = 1; renderPassBI.clearValueCount = 1;
renderPassBI.pClearValues = &black; renderPassBI.pClearValues = &black;
vkCmdBeginRenderPass(commandBuffer, &renderPassBI, VK_SUBPASS_CONTENTS_INLINE); vkCmdBeginRenderPass(commandBuffer, &renderPassBI, VK_SUBPASS_CONTENTS_INLINE);
@ -449,11 +459,13 @@ namespace nf::client::render {
// Bind output pipeline // Bind output pipeline
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineOutput); vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineOutput);
// Set viewport // Set viewport and scissor
VkViewport viewport = {}; 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; viewport.maxDepth = 1.0;
vkCmdSetViewport(commandBuffer, 0, 1, &viewport); vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
VkRect2D scissor = { {}, m_swapchainExtent };
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
// Draw // Draw
vkCmdDraw(commandBuffer, 3, 1, 0, 0); vkCmdDraw(commandBuffer, 3, 1, 0, 0);
@ -487,14 +499,46 @@ namespace nf::client::render {
presentInfo.pSwapchains = &m_swapchain; presentInfo.pSwapchains = &m_swapchain;
presentInfo.pImageIndices = &nextImageIndex; 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 // Advance to next frame executor
m_currentFrameExecutor = (m_currentFrameExecutor + 1) % m_numFramesInFlight; 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); vkDeviceWaitIdle(m_device);
}
RenderEngine::~RenderEngine() {
waitIdle();
for (int i = 0; i < m_frameExecutors.size(); i++) { for (int i = 0; i < m_frameExecutors.size(); i++) {
vkDestroyFence(m_device, m_frameExecutors[i].fenceFrameInFlight, nullptr); vkDestroyFence(m_device, m_frameExecutors[i].fenceFrameInFlight, nullptr);
@ -502,19 +546,11 @@ namespace nf::client::render {
vkDestroySemaphore(m_device, m_frameExecutors[i].semaphoreImageAvailable, nullptr); vkDestroySemaphore(m_device, m_frameExecutors[i].semaphoreImageAvailable, nullptr);
} }
destroySwapchain();
vkDestroyCommandPool(m_device, m_commandPool, nullptr); 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); vkDestroyPipeline(m_device, m_pipelineOutput, nullptr);
vkDestroyPipelineLayout(m_device, m_pipelineLayoutOutput, nullptr); vkDestroyPipelineLayout(m_device, m_pipelineLayoutOutput, nullptr);
vkDestroyRenderPass(m_device, m_renderPassOutput, 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); vkDestroyDevice(m_device, nullptr);
vkDestroySurfaceKHR(m_instance, m_surface, nullptr); vkDestroySurfaceKHR(m_instance, m_surface, nullptr);
vkDestroyInstance(m_instance, nullptr); vkDestroyInstance(m_instance, nullptr);

View File

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