diff --git a/implementations/Crafter.Graphics-Window.cpp b/implementations/Crafter.Graphics-Window.cpp index 1388a03..9eccdca 100644 --- a/implementations/Crafter.Graphics-Window.cpp +++ b/implementations/Crafter.Graphics-Window.cpp @@ -430,6 +430,58 @@ Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height submitInfo.pSignalSemaphores = &semaphores.renderComplete; submitInfo.pNext = VK_NULL_HANDLE; + // Per-frame info structs: everything that never varies between frames is + // set here once. Render() only patches the barriers' image/oldLayout. + cmdBufInfo = {}; + cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + + const VkImageSubresourceRange range { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }; + + // Transition into VK_IMAGE_LAYOUT_GENERAL before recording passes. image + // and oldLayout are patched per frame (oldLayout depends on first use). + acquireBarrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = VK_NULL_HANDLE, + .subresourceRange = range, + }; + + // Transition back to PRESENT_SRC_KHR after the passes. Only image varies. + presentBarrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = 0, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = VK_NULL_HANDLE, + .subresourceRange = range, + }; + + // pImageIndices/pSwapchains point at members, so they follow value changes + // (currentBuffer each frame, swapChain across recreation) on their own. + // The render-complete wait semaphore is fixed for the window's lifetime. + presentInfo = {}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presentInfo.pNext = NULL; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = &swapChain; + presentInfo.pImageIndices = ¤tBuffer; + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = &semaphores.renderComplete; + lastMousePos = {0,0}; mouseDelta = {0,0}; currentMousePos = {0,0}; @@ -688,18 +740,8 @@ void Window::Render() { submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; - VkCommandBufferBeginInfo cmdBufInfo {}; - cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - Device::CheckVkResult(vkBeginCommandBuffer(drawCmdBuffers[currentBuffer], &cmdBufInfo)); - VkImageSubresourceRange range{}; - range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - range.baseMipLevel = 0; - range.levelCount = VK_REMAINING_MIP_LEVELS; - range.baseArrayLayer = 0; - range.layerCount = VK_REMAINING_ARRAY_LAYERS; - // On an image's first use after (re)creating the swapchain it is still in // VK_IMAGE_LAYOUT_UNDEFINED; every subsequent frame leaves it in // PRESENT_SRC_KHR. Transitioning from the wrong oldLayout is a validation @@ -708,20 +750,11 @@ void Window::Render() { const bool firstUse = !imageInitialised[currentBuffer]; imageInitialised[currentBuffer] = true; - VkImageMemoryBarrier image_memory_barrier { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, - .oldLayout = firstUse ? VK_IMAGE_LAYOUT_UNDEFINED - : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = images[currentBuffer], - .subresourceRange = range - }; + acquireBarrier.oldLayout = firstUse ? VK_IMAGE_LAYOUT_UNDEFINED + : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + acquireBarrier.image = images[currentBuffer]; - vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &acquireBarrier); // Synthesise key-repeat events before listeners run, so the focused // widget's OnTextInput / OnKeyDown sees them in the same frame. @@ -786,35 +819,13 @@ void Window::Render() { } } - VkImageMemoryBarrier image_memory_barrier2 { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .dstAccessMask = 0, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, - .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = images[currentBuffer], - .subresourceRange = range - }; + presentBarrier.image = images[currentBuffer]; - vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier2); + vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &presentBarrier); Device::CheckVkResult(vkEndCommandBuffer(drawCmdBuffers[currentBuffer])); Device::CheckVkResult(vkQueueSubmit(Device::queue, 1, &submitInfo, VK_NULL_HANDLE)); - VkPresentInfoKHR presentInfo = {}; - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - presentInfo.pNext = NULL; - presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = &swapChain; - presentInfo.pImageIndices = ¤tBuffer; - // Check if a wait semaphore has been specified to wait for before presenting the image - if (semaphores.renderComplete != VK_NULL_HANDLE) - { - presentInfo.pWaitSemaphores = &semaphores.renderComplete; - presentInfo.waitSemaphoreCount = 1; - } VkResult result = vkQueuePresentKHR(Device::queue, &presentInfo); if (result == VK_SUBOPTIMAL_KHR || result == VK_ERROR_OUT_OF_DATE_KHR) { diff --git a/interfaces/Crafter.Graphics-Window.cppm b/interfaces/Crafter.Graphics-Window.cppm index b957882..4bd121a 100644 --- a/interfaces/Crafter.Graphics-Window.cppm +++ b/interfaces/Crafter.Graphics-Window.cppm @@ -259,6 +259,16 @@ export namespace Crafter { std::thread thread; VkCommandBuffer drawCmdBuffers[numFrames]; VkSubmitInfo submitInfo; + // Per-frame Vulkan info structs whose contents are almost entirely + // invariant. Initialised once in the constructor; Render() patches only + // the few fields that change each frame (the two barriers' image and + // the acquire barrier's oldLayout). presentInfo.pImageIndices and + // pSwapchains point at the currentBuffer/swapChain members, so they + // track value changes (including swapchain recreation) automatically. + VkCommandBufferBeginInfo cmdBufInfo; + VkImageMemoryBarrier acquireBarrier; + VkImageMemoryBarrier presentBarrier; + VkPresentInfoKHR presentInfo; Semaphores semaphores; std::uint32_t currentBuffer = 0; VkPipelineStageFlags submitPipelineStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;