diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index 119517d99b..667771465a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt @@ -68,8 +68,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { USE_LRU_CACHE("use_lru_cache"); external fun isRaiiEnabled(): Boolean -// external fun isFrameSkippingEnabled(): Boolean - external fun isFrameInterpolationEnabled(): Boolean override fun getBoolean(needsGlobal: Boolean): Boolean = NativeConfig.getBoolean(key, needsGlobal) diff --git a/src/common/settings.h b/src/common/settings.h index 41c042bcf1..910e46a447 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -322,12 +322,8 @@ struct Values { SwitchableSetting vulkan_device{linkage, 0, "vulkan_device", Category::Renderer, Specialization::RuntimeList}; SwitchableSetting enable_raii{linkage, false, "enable_raii", Category::Renderer}; -#ifdef __ANDROID__ SwitchableSetting frame_interpolation{linkage, true, "frame_interpolation", Category::Renderer, Specialization::RuntimeList}; - SwitchableSetting frame_skipping{linkage, false, "frame_skipping", Category::Renderer, - Specialization::RuntimeList}; -#endif SwitchableSetting use_disk_shader_cache{linkage, true, "use_disk_shader_cache", Category::Renderer}; SwitchableSetting optimize_spirv_output{linkage, diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index a91fba6725..fd20bd5c1c 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -38,9 +38,7 @@ #include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_surface.h" #include "video_core/vulkan_common/vulkan_wrapper.h" -#ifdef __ANDROID__ -#include -#endif + namespace Vulkan { namespace { @@ -189,149 +187,31 @@ RendererVulkan::~RendererVulkan() { void(device.GetLogical().WaitIdle()); } -#ifdef __ANDROID__ -class BooleanSetting { - public: -// static BooleanSetting FRAME_SKIPPING; - static BooleanSetting FRAME_INTERPOLATION; - explicit BooleanSetting(bool initial_value = false) : value(initial_value) {} - - [[nodiscard]] bool getBoolean() const { - return value; - } - - void setBoolean(bool new_value) { - value = new_value; - } - - private: - bool value; - }; - - // Initialize static members -// BooleanSetting BooleanSetting::FRAME_SKIPPING(false); - BooleanSetting BooleanSetting::FRAME_INTERPOLATION(false); - -// extern "C" JNIEXPORT jboolean JNICALL -// Java_org_yuzu_yuzu_1emu_features_settings_model_BooleanSetting_isFrameSkippingEnabled(JNIEnv* env, jobject /* this */) { -// return static_cast(BooleanSetting::FRAME_SKIPPING.getBoolean()); -// } - - extern "C" JNIEXPORT jboolean JNICALL - Java_org_yuzu_yuzu_1emu_features_settings_model_BooleanSetting_isFrameInterpolationEnabled(JNIEnv* env, jobject /* this */) { - return static_cast(BooleanSetting::FRAME_INTERPOLATION.getBoolean()); - } - - void RendererVulkan::InterpolateFrames(Frame* prev_frame, Frame* interpolated_frame) { - if (!prev_frame || !interpolated_frame || !prev_frame->image || !interpolated_frame->image) { - return; - } - - const auto& framebuffer_layout = render_window.GetFramebufferLayout(); - // Fixed aggressive downscale (50%) - VkExtent2D dst_extent{ - .width = framebuffer_layout.width / 2, - .height = framebuffer_layout.height / 2 - }; - - // Check if we need to recreate the destination frame - bool needs_recreation = false; // Only recreate when necessary - if (!interpolated_frame->image_view) { - needs_recreation = true; // Need to create initially - } else { - // Check if dimensions have changed - if (interpolated_frame->framebuffer) { - needs_recreation = (framebuffer_layout.width / 2 != dst_extent.width) || - (framebuffer_layout.height / 2 != dst_extent.height); - } else { - needs_recreation = true; - } - } - - if (needs_recreation) { - interpolated_frame->image = CreateWrappedImage(memory_allocator, dst_extent, swapchain.GetImageViewFormat()); - interpolated_frame->image_view = CreateWrappedImageView(device, interpolated_frame->image, swapchain.GetImageViewFormat()); - interpolated_frame->framebuffer = blit_swapchain.CreateFramebuffer( - Layout::FramebufferLayout{dst_extent.width, dst_extent.height}, - *interpolated_frame->image_view, - swapchain.GetImageViewFormat()); - } - - scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([&](vk::CommandBuffer cmdbuf) { - // Transition images to transfer layouts - TransitionImageLayout(cmdbuf, *prev_frame->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - TransitionImageLayout(cmdbuf, *interpolated_frame->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - // Perform the downscale blit - VkImageBlit blit_region{}; - blit_region.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; - blit_region.srcOffsets[0] = {0, 0, 0}; - blit_region.srcOffsets[1] = { - static_cast(framebuffer_layout.width), - static_cast(framebuffer_layout.height), - 1 - }; - blit_region.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; - blit_region.dstOffsets[0] = {0, 0, 0}; - blit_region.dstOffsets[1] = { - static_cast(dst_extent.width), - static_cast(dst_extent.height), - 1 - }; - - // Using the wrapper's BlitImage with proper parameters - cmdbuf.BlitImage( - *prev_frame->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - *interpolated_frame->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - blit_region, VK_FILTER_NEAREST - ); - - // Transition back to general layout - TransitionImageLayout(cmdbuf, *prev_frame->image, VK_IMAGE_LAYOUT_GENERAL); - TransitionImageLayout(cmdbuf, *interpolated_frame->image, VK_IMAGE_LAYOUT_GENERAL); - }); - } -#endif - void RendererVulkan::Composite(std::span framebuffers) { - #ifdef __ANDROID__ - static int frame_counter = 0; - static int target_fps = 60; // Target FPS (30 or 60) - int frame_skip_threshold = 1; - - bool frame_skipping = false; //BooleanSetting::FRAME_SKIPPING.getBoolean(); - bool frame_interpolation = BooleanSetting::FRAME_INTERPOLATION.getBoolean(); - #endif - if (framebuffers.empty()) { return; } - #ifdef __ANDROID__ - if (frame_skipping) { - frame_skip_threshold = (target_fps == 30) ? 2 : 2; - } - - frame_counter++; - if (frame_counter % frame_skip_threshold != 0) { - if (frame_interpolation && previous_frame) { - Frame* interpolated_frame = present_manager.GetRenderFrame(); - InterpolateFrames(previous_frame, interpolated_frame); - 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; - } - #endif - SCOPE_EXIT { render_window.OnFrameDisplayed(); }; + // Frameskip logic + if (Settings::values.frame_interpolation.GetValue()) { + skip_next_frame = !skip_next_frame; + // Only skip if we have a valid last frame + if (skip_next_frame && last_presented_frame != nullptr) { + present_manager.Present(last_presented_frame); + gpu.RendererFrameEndNotify(); + rasterizer.TickFrame(); + return; + } + } else { + // If frameskip is off, always reset skip state + skip_next_frame = false; + last_presented_frame = nullptr; + } + RenderAppletCaptureLayer(framebuffers); if (!render_window.IsShown()) { @@ -346,6 +226,11 @@ void RendererVulkan::Composite(std::span framebu scheduler.Flush(*frame->render_ready); present_manager.Present(frame); + // Store the last presented frame for interpolation + if (Settings::values.frame_interpolation.GetValue()) { + last_presented_frame = frame; + } + gpu.RendererFrameEndNotify(); rasterizer.TickFrame(); } diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index c1e6d5db7f..aaa0a1f8b8 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -58,10 +58,6 @@ public: void InitializePlatformSpecific(); private: - void InterpolateFrames(Frame* prev_frame, Frame* curr_frame); - Frame* previous_frame = nullptr; // Store the previous frame for interpolation - VkCommandBuffer BeginSingleTimeCommands(); - void EndSingleTimeCommands(VkCommandBuffer command_buffer); void Report() const; vk::Buffer RenderToBuffer(std::span framebuffers, @@ -102,6 +98,9 @@ private: std::optional turbo_mode; Frame applet_frame; + + bool skip_next_frame = false; + Frame* last_presented_frame = nullptr; }; } // namespace Vulkan