diff --git a/src/video_core/renderer_vulkan/present/frame_gen.cpp b/src/video_core/renderer_vulkan/present/frame_gen.cpp index 1a22a0fa91..c6ab83e018 100644 --- a/src/video_core/renderer_vulkan/present/frame_gen.cpp +++ b/src/video_core/renderer_vulkan/present/frame_gen.cpp @@ -20,6 +20,7 @@ namespace { constexpr f32 LSFG_FLOW_SCALE = 1.0f; constexpr size_t COLOR_CHANNELS = 4; +constexpr u64 LSFG_REQUIRED_FRAMES = 2; bool IsBlueFirst(VkFormat format) { return format == VK_FORMAT_B8G8R8A8_UNORM || format == VK_FORMAT_B8G8R8A8_SRGB; @@ -90,41 +91,31 @@ void WriteColorPpm(const std::filesystem::path& path, VkExtent2D extent, WritePortablePixmap(path, "P6", extent, rgb); } -void CopyPresentedFrame(vk::CommandBuffer cmdbuf, VkImage source, LsfgImage& destination, - VkExtent2D extent) { - const auto make_barrier = [](VkImage image, VkAccessFlags src_access, VkAccessFlags dst_access, - VkImageLayout old_layout, VkImageLayout new_layout) { - return VkImageMemoryBarrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = src_access, - .dstAccessMask = dst_access, - .oldLayout = old_layout, - .newLayout = new_layout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = image, - .subresourceRange{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; +VkImageMemoryBarrier MakeTransitionBarrier(VkImage image, VkAccessFlags src_access, + VkAccessFlags dst_access, VkImageLayout old_layout, + VkImageLayout new_layout) { + return VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = src_access, + .dstAccessMask = dst_access, + .oldLayout = old_layout, + .newLayout = new_layout, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, }; +} - const std::array before{ - make_barrier(source, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, - VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL), - make_barrier(destination.Handle(), VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, - destination.Layout(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL), - }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, {}, before); - - const VkImageCopy region{ +VkImageCopy MakeCopyRegion(VkExtent2D extent) { + return VkImageCopy{ .srcSubresource{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, @@ -141,8 +132,24 @@ void CopyPresentedFrame(vk::CommandBuffer cmdbuf, VkImage source, LsfgImage& des .dstOffset = {}, .extent = {.width = extent.width, .height = extent.height, .depth = 1}, }; +} + +void CopyPresentedFrame(vk::CommandBuffer cmdbuf, VkImage source, LsfgImage& destination, + VkExtent2D extent) { + const auto make_barrier = MakeTransitionBarrier; + + const std::array before{ + make_barrier(source, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, + VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL), + make_barrier(destination.Handle(), VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + destination.Layout(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL), + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, {}, before); + cmdbuf.CopyImage(source, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, destination.Handle(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region); + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, MakeCopyRegion(extent)); const std::array after{ make_barrier(source, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, @@ -201,6 +208,50 @@ void FrameGen::Process(const Device& device, Frame* frame, VkFormat format) { } } +bool FrameGen::HasGeneratedFrame() const { + return chain.has_value() && frame_count >= LSFG_REQUIRED_FRAMES && + Settings::values.frame_gen.GetValue(); +} + +void FrameGen::CopyToFrame(Frame* destination) { + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([this, target = *destination->image](vk::CommandBuffer cmdbuf) { + LsfgImage& source = chain->Output(); + const VkExtent2D extent = source.Extent(); + + const std::array before{ + MakeTransitionBarrier(source.Handle(), VK_ACCESS_SHADER_WRITE_BIT, + VK_ACCESS_TRANSFER_READ_BIT, source.Layout(), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL), + MakeTransitionBarrier(target, 0, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL), + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, {}, before); + + cmdbuf.CopyImage(source.Handle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, target, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, MakeCopyRegion(extent)); + + const std::array after{ + MakeTransitionBarrier(source.Handle(), VK_ACCESS_TRANSFER_READ_BIT, + VK_ACCESS_SHADER_WRITE_BIT, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_IMAGE_LAYOUT_GENERAL), + MakeTransitionBarrier(target, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_GENERAL), + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + 0, {}, {}, after); + + source.SetLayout(VK_IMAGE_LAYOUT_GENERAL); + }); +} + void FrameGen::Rebuild(const Device& device, VkExtent2D extent, VkFormat format) { scheduler.Finish(); chain.reset(); diff --git a/src/video_core/renderer_vulkan/present/frame_gen.h b/src/video_core/renderer_vulkan/present/frame_gen.h index 5d43e6ae75..35bb5bb5b0 100644 --- a/src/video_core/renderer_vulkan/present/frame_gen.h +++ b/src/video_core/renderer_vulkan/present/frame_gen.h @@ -23,6 +23,10 @@ public: void Process(const Device& device, Frame* frame, VkFormat format); + [[nodiscard]] bool HasGeneratedFrame() const; + + void CopyToFrame(Frame* destination); + private: void Rebuild(const Device& device, VkExtent2D extent, VkFormat format); void DumpDebugImages(u64 count); diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index a0c1f017dc..01b4269a6d 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -195,6 +195,14 @@ void RendererVulkan::Composite(std::span framebu frame_gen.Process(device, frame, swapchain.GetImageFormat()); + if (frame_gen.HasGeneratedFrame() && present_manager.CanQueueExtraFrame()) { + Frame* generated = present_manager.GetRenderFrame(); + blit_swapchain.PrepareFrame(device, generated, render_window.GetFramebufferLayout()); + frame_gen.CopyToFrame(generated); + scheduler.Flush(*generated->render_ready); + present_manager.Present(generated); + } + scheduler.Flush(*frame->render_ready); present_manager.Present(frame); diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 05314fea27..96eeeb8045 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -85,6 +85,17 @@ void BlitScreen::SetWindowAdaptPass(const Device& device) { } } +void BlitScreen::PrepareFrame(const Device& device, Frame* frame, + const Layout::FramebufferLayout& layout) { + if (!window_adapt || (frame->width == layout.width && frame->height == layout.height)) { + return; + } + + WaitIdle(device); + present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format, + window_adapt->GetRenderPass()); +} + void BlitScreen::DrawToFrame(const Device& device, RasterizerVulkan& rasterizer, Frame* frame, std::span framebuffers, const Layout::FramebufferLayout& layout, diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h index e50f0bcf33..56b429ae53 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.h +++ b/src/video_core/renderer_vulkan/vk_blit_screen.h @@ -60,6 +60,8 @@ public: const Layout::FramebufferLayout& layout, size_t current_swapchain_image_count, VkFormat current_swapchain_view_format); + void PrepareFrame(const Device& device, Frame* frame, const Layout::FramebufferLayout& layout); + [[nodiscard]] vk::Framebuffer CreateFramebuffer(const Device& device, const Layout::FramebufferLayout& layout, VkImageView image_view, VkFormat current_view_format); diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp index 1c803368d6..98ba048783 100644 --- a/src/video_core/renderer_vulkan/vk_present_manager.cpp +++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp @@ -179,6 +179,15 @@ void PresentManager::Present(Frame* frame) { } } +bool PresentManager::CanQueueExtraFrame() { + if (!use_present_thread) { + return false; + } + + std::scoped_lock lock{queue_mutex, free_mutex}; + return present_queue.empty() && !free_queue.empty(); +} + void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat image_view_format, VkRenderPass rd) { auto& dld = device.GetLogical(); @@ -202,8 +211,8 @@ void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat .arrayLayers = 1, .samples = VK_SAMPLE_COUNT_1_BIT, .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | - VK_IMAGE_USAGE_SAMPLED_BIT, + .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, diff --git a/src/video_core/renderer_vulkan/vk_present_manager.h b/src/video_core/renderer_vulkan/vk_present_manager.h index c51f8ed77f..3518f99a9b 100644 --- a/src/video_core/renderer_vulkan/vk_present_manager.h +++ b/src/video_core/renderer_vulkan/vk_present_manager.h @@ -60,6 +60,9 @@ public: /// Waits for the present thread to finish presenting all queued frames. void WaitPresent(); + /// Whether an additional frame can be queued without stalling the render thread + bool CanQueueExtraFrame(); + private: void PresentThread(std::stop_token token);