diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 0161c53a44..81a6c69774 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -171,14 +171,141 @@ RendererVulkan::~RendererVulkan() { void(device.GetLogical().WaitIdle()); } +bool RendererVulkan::InterpolateFrames(Frame* prev_frame, Frame* interpolated_frame) { + if (!prev_frame || !interpolated_frame || prev_frame == interpolated_frame || + !prev_frame->image || !interpolated_frame->image || prev_frame->width == 0 || + prev_frame->height == 0 || interpolated_frame->width == 0 || + interpolated_frame->height == 0) { + return false; + } + + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([prev_frame, interpolated_frame](vk::CommandBuffer cmdbuf) { + constexpr VkImageSubresourceRange color_range{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }; + const std::array pre_barriers{ + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = *prev_frame->image, + .subresourceRange = color_range, + }, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = *interpolated_frame->image, + .subresourceRange = color_range, + }, + }; + const std::array post_barriers{ + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = *prev_frame->image, + .subresourceRange = color_range, + }, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = *interpolated_frame->image, + .subresourceRange = color_range, + }, + }; + const VkImageBlit blit{ + .srcSubresource{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .srcOffsets{ + {0, 0, 0}, + {static_cast(prev_frame->width), static_cast(prev_frame->height), 1}, + }, + .dstSubresource{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .dstOffsets{ + {0, 0, 0}, + {static_cast(interpolated_frame->width), + static_cast(interpolated_frame->height), 1}, + }, + }; + + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, + VK_PIPELINE_STAGE_TRANSFER_BIT, {}, {}, {}, pre_barriers); + cmdbuf.BlitImage(*prev_frame->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + *interpolated_frame->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + blit, VK_FILTER_LINEAR); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, {}, {}, {}, + post_barriers); + }); + return true; +} + void RendererVulkan::Composite(std::span framebuffers) { + if (framebuffers.empty()) { + return; + } + #ifdef __ANDROID__ static u64 frame_counter = 0; - if (Settings::values.enable_frame_skipping.GetValue()) { + const bool enable_frame_skipping = Settings::values.enable_frame_skipping.GetValue(); + const bool enable_frame_interpolation = Settings::values.enable_frame_interpolation.GetValue(); + + if (enable_frame_skipping) { ++frame_counter; if ((frame_counter % 2) != 0) { + if (enable_frame_interpolation && previous_frame) { + Frame* interpolated_frame = present_manager.GetRenderFrame(); + if (!InterpolateFrames(previous_frame, interpolated_frame)) { + scheduler.RequestOutsideRenderPassOperationContext(); + blit_swapchain.DrawToFrame(rasterizer, interpolated_frame, framebuffers, + render_window.GetFramebufferLayout(), + swapchain.GetImageCount(), + swapchain.GetImageViewFormat()); + } + scheduler.Flush(*interpolated_frame->render_ready); + present_manager.Present(interpolated_frame); + } return; } + } else { + frame_counter = 0; } #endif @@ -205,6 +332,8 @@ void RendererVulkan::Composite(std::span framebu gpu.RendererFrameEndNotify(); rasterizer.TickFrame(); + + previous_frame = frame; } void RendererVulkan::Report() const { diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 4fb88b29de..6837eaaf4f 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -60,7 +60,7 @@ public: void InitializePlatformSpecific(); private: - void InterpolateFrames(Frame* prev_frame, Frame* curr_frame); + bool InterpolateFrames(Frame* prev_frame, Frame* curr_frame); Frame* previous_frame = nullptr; // Store the previous frame for interpolation VkCommandBuffer BeginSingleTimeCommands(); void EndSingleTimeCommands(VkCommandBuffer command_buffer); diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp index 53c3f6759f..35a7e0901c 100644 --- a/src/video_core/renderer_vulkan/vk_present_manager.cpp +++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp @@ -202,7 +202,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, + .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr,