Add swapchain recreation
This commit is contained in:
parent
3088a42a07
commit
798be67cc5
@ -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() {
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user