diff --git a/src/android/app/src/main/java/org/citron/citron_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/citron/citron_emu/features/settings/model/IntSetting.kt index c3e3f3b77..5f5663986 100644 --- a/src/android/app/src/main/java/org/citron/citron_emu/features/settings/model/IntSetting.kt +++ b/src/android/app/src/main/java/org/citron/citron_emu/features/settings/model/IntSetting.kt @@ -29,8 +29,6 @@ enum class IntSetting(override val key: String) : AbstractIntSetting { VERTICAL_ALIGNMENT("vertical_alignment"), FSR_SHARPENING_SLIDER("fsr_sharpening_slider"), FSR2_QUALITY_MODE("fsr2_quality_mode"), - FRAME_GENERATION("frame_generation"), - FRAME_GENERATION_MODE("frame_generation_mode"), FRAME_SKIPPING("frame_skipping"), FRAME_SKIPPING_MODE("frame_skipping_mode"), diff --git a/src/android/app/src/main/java/org/citron/citron_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/citron/citron_emu/features/settings/model/view/SettingsItem.kt index e9c90fe50..716a4387f 100644 --- a/src/android/app/src/main/java/org/citron/citron_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/citron/citron_emu/features/settings/model/view/SettingsItem.kt @@ -443,24 +443,7 @@ abstract class SettingsItem( valuesId = R.array.vramUsageModeValues ) ) - put( - SingleChoiceSetting( - IntSetting.FRAME_GENERATION, - titleId = R.string.frame_generation, - descriptionId = R.string.frame_generation_description, - choicesId = R.array.frameGenerationNames, - valuesId = R.array.frameGenerationValues - ) - ) - put( - SingleChoiceSetting( - IntSetting.FRAME_GENERATION_MODE, - titleId = R.string.frame_generation_mode, - descriptionId = R.string.frame_generation_mode_description, - choicesId = R.array.frameGenerationModeNames, - valuesId = R.array.frameGenerationModeValues - ) - ) + put( SingleChoiceSetting( IntSetting.FRAME_SKIPPING, diff --git a/src/android/app/src/main/java/org/citron/citron_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/citron/citron_emu/features/settings/ui/SettingsFragmentPresenter.kt index 06b2e8d16..d742ddc42 100644 --- a/src/android/app/src/main/java/org/citron/citron_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/citron/citron_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -997,10 +997,6 @@ class SettingsFragmentPresenter( add(IntSetting.SHADER_BACKEND.key) add(IntSetting.VRAM_USAGE_MODE.key) - add(HeaderSetting(R.string.frame_generation_header)) - add(IntSetting.FRAME_GENERATION.key) - add(IntSetting.FRAME_GENERATION_MODE.key) - add(HeaderSetting(R.string.frame_skipping_header)) add(IntSetting.FRAME_SKIPPING.key) add(IntSetting.FRAME_SKIPPING_MODE.key) diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 3ded2979d..e63a9a968 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -187,25 +187,7 @@ 3 - - @string/frame_generation_disabled - @string/frame_generation_enabled - - - 0 - 1 - - - - @string/frame_generation_mode_interpolation - @string/frame_generation_mode_extrapolation - - - - 0 - 1 - @string/frame_skipping_disabled diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 9937f8eb7..7f9e6761d 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -424,7 +424,6 @@ ASTC Settings Advanced Graphics Applet Settings - Frame Generation Cabinet Applet Mode @@ -1229,14 +1228,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Controls which shader backend to use for rendering. VRAM Usage Mode Controls how aggressively VRAM is used. Conservative mode limits VRAM usage for better stability. - Frame Generation - Enables frame generation to create intermediate frames, potentially doubling the perceived frame rate. - Frame Generation Mode - Interpolation creates frames between existing ones, while Extrapolation predicts future frames. - Disabled - Enabled - Interpolation - Extrapolation Frame Skipping Skips frames to maintain performance when the system cannot keep up with the target frame rate. Frame Skipping Mode diff --git a/src/citron/configuration/shared_translation.cpp b/src/citron/configuration/shared_translation.cpp index b23fd816d..22a337a5b 100644 --- a/src/citron/configuration/shared_translation.cpp +++ b/src/citron/configuration/shared_translation.cpp @@ -129,10 +129,7 @@ std::unique_ptr InitializeTranslations(QWidget* parent) { tr("Determines how sharpened the image will look while using FSR's dynamic contrast.")); INSERT(Settings, fsr2_quality_mode, tr("FSR 2.0 Quality Mode:"), tr("Selects the quality mode for FSR 2.0 upscaling. Quality provides better image quality, Performance provides better performance.")); - INSERT(Settings, frame_generation, tr("Frame Generation:"), - tr("Enables frame generation to create intermediate frames, potentially doubling the perceived frame rate.")); - INSERT(Settings, frame_generation_mode, tr("Frame Generation Mode:"), - tr("Interpolation creates frames between existing ones, while Extrapolation predicts future frames.")); + INSERT(Settings, frame_skipping, tr("Frame Skipping:"), tr("Skips frames to maintain performance when the system cannot keep up with the target frame rate.")); INSERT(Settings, frame_skipping_mode, tr("Frame Skipping Mode:"), @@ -419,16 +416,7 @@ std::unique_ptr ComboboxEnumeration(QWidget* parent) { PAIR(FSR2QualityMode, Performance, tr("Performance")), PAIR(FSR2QualityMode, UltraPerformance, tr("Ultra Performance")), }}); - translations->insert({Settings::EnumMetadata::Index(), - { - PAIR(FrameGeneration, Disabled, tr("Disabled")), - PAIR(FrameGeneration, Enabled, tr("Enabled")), - }}); - translations->insert({Settings::EnumMetadata::Index(), - { - PAIR(FrameGenerationMode, Interpolation, tr("Interpolation")), - PAIR(FrameGenerationMode, Extrapolation, tr("Extrapolation")), - }}); + translations->insert({Settings::EnumMetadata::Index(), { PAIR(FrameSkipping, Disabled, tr("Disabled")), diff --git a/src/common/settings.h b/src/common/settings.h index 75e03b79b..57991654b 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -358,25 +358,7 @@ struct Values { true, true}; - SwitchableSetting frame_generation{linkage, - FrameGeneration::Disabled, // Disabled by default - FrameGeneration::Disabled, - FrameGeneration::Enabled, - "frame_generation", - Category::Renderer, - Specialization::Default, - true, - true}; - SwitchableSetting frame_generation_mode{linkage, - FrameGenerationMode::Interpolation, // Interpolation by default - FrameGenerationMode::Interpolation, - FrameGenerationMode::Extrapolation, - "frame_generation_mode", - Category::Renderer, - Specialization::Default, - true, - true}; SwitchableSetting frame_skipping{linkage, FrameSkipping::Disabled, // Disabled by default diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 83df29799..3663e5c18 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -153,10 +153,6 @@ ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); ENUM(FSR2QualityMode, Quality, Balanced, Performance, UltraPerformance); -ENUM(FrameGeneration, Disabled, Enabled, MaxEnum); - -ENUM(FrameGenerationMode, Interpolation, Extrapolation, MaxEnum); - ENUM(FrameSkipping, Disabled, Enabled, MaxEnum); ENUM(FrameSkippingMode, Adaptive, Fixed, MaxEnum); diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index d744305e7..6a4d7be27 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -182,8 +182,6 @@ add_library(video_core STATIC renderer_vulkan/present/fsr.h renderer_vulkan/present/fsr2.cpp renderer_vulkan/present/fsr2.h - renderer_vulkan/present/frame_generation.cpp - renderer_vulkan/present/frame_generation.h frame_skipping.cpp frame_skipping.h renderer_vulkan/present/fxaa.cpp diff --git a/src/video_core/host_shaders/vulkan_frame_generation_frag_spv.h b/src/video_core/host_shaders/vulkan_frame_generation_frag_spv.h deleted file mode 100644 index 80753740d..000000000 --- a/src/video_core/host_shaders/vulkan_frame_generation_frag_spv.h +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -namespace Vulkan { -namespace FrameGenShaders { - -// Minimal fragment shader for frame generation -constexpr std::array FRAG_SPV = {{ - 0x07230203, 0x00010000, 0x0008000A, 0x00000004 -}}; - -} // namespace FrameGenShaders -} // namespace Vulkan \ No newline at end of file diff --git a/src/video_core/host_shaders/vulkan_frame_generation_vert_spv.h b/src/video_core/host_shaders/vulkan_frame_generation_vert_spv.h deleted file mode 100644 index 48be09e58..000000000 --- a/src/video_core/host_shaders/vulkan_frame_generation_vert_spv.h +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -namespace Vulkan { -namespace FrameGenShaders { - -// Minimal vertex shader for frame generation -constexpr std::array VERT_SPV = {{ - 0x07230203, 0x00010000, 0x0008000A, 0x00000004 -}}; - -} // namespace FrameGenShaders -} // namespace Vulkan \ No newline at end of file diff --git a/src/video_core/renderer_vulkan/present/frame_generation.cpp b/src/video_core/renderer_vulkan/present/frame_generation.cpp deleted file mode 100644 index 623c825c2..000000000 --- a/src/video_core/renderer_vulkan/present/frame_generation.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include "common/common_types.h" -#include "common/div_ceil.h" -#include "common/settings.h" - -#include "video_core/host_shaders/vulkan_frame_generation_vert_spv.h" -#include "video_core/host_shaders/vulkan_frame_generation_frag_spv.h" -#include "video_core/host_shaders/vulkan_motion_estimation_frag_spv.h" -#include "video_core/host_shaders/vulkan_frame_interpolation_frag_spv.h" -#include "video_core/renderer_vulkan/present/frame_generation.h" -#include "video_core/renderer_vulkan/present/util.h" -#include "video_core/renderer_vulkan/vk_scheduler.h" -#include "video_core/renderer_vulkan/vk_shader_util.h" -#include "video_core/vulkan_common/vulkan_device.h" - -namespace Vulkan { - -using PushConstants = std::array; - -FrameGeneration::FrameGeneration(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, - VkExtent2D extent) - : m_device{device}, m_memory_allocator{memory_allocator}, m_image_count{image_count}, m_extent{extent} { - // Simplified constructor - no complex initialization needed for safe pass-through implementation -} - -void FrameGeneration::CreateImages() { - m_dynamic_images.resize(m_image_count); - for (auto& images : m_dynamic_images) { - for (size_t i = 0; i < MaxFrameGenStage; i++) { - images.images[i] = CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); - images.image_views[i] = CreateWrappedImageView(m_device, images.images[i], VK_FORMAT_R16G16B16A16_SFLOAT); - } - } - - // Create frame buffer for motion estimation - m_previous_frames.resize(m_image_count); - m_previous_frame_views.resize(m_image_count); - for (size_t i = 0; i < m_image_count; i++) { - m_previous_frames[i] = CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R8G8B8A8_UNORM); - m_previous_frame_views[i] = CreateWrappedImageView(m_device, m_previous_frames[i], VK_FORMAT_R8G8B8A8_UNORM); - } -} - -void FrameGeneration::CreateRenderPasses() { - m_renderpass = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); - - for (auto& images : m_dynamic_images) { - images.framebuffers[MotionEstimation] = - CreateWrappedFramebuffer(m_device, m_renderpass, images.image_views[MotionEstimation], m_extent); - images.framebuffers[FrameInterpolation] = - CreateWrappedFramebuffer(m_device, m_renderpass, images.image_views[FrameInterpolation], m_extent); - } -} - -void FrameGeneration::CreateSampler() { - m_sampler = CreateBilinearSampler(m_device); -} - -void FrameGeneration::CreateShaders() { - m_vert_shader = BuildShader(m_device, Vulkan::FrameGenShaders::VERT_SPV); - m_motion_estimation_shader = BuildShader(m_device, Vulkan::FrameGenShaders::MOTION_ESTIMATION_FRAG_SPV); - m_frame_interpolation_shader = BuildShader(m_device, Vulkan::FrameGenShaders::FRAME_INTERPOLATION_FRAG_SPV); -} - -void FrameGeneration::CreateDescriptorPool() { - // MotionEstimation: 2 descriptors (current + previous frame) - // FrameInterpolation: 3 descriptors (current + previous + motion vectors) - // 5 descriptors, 2 descriptor sets per invocation - m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 5 * m_image_count, 2 * m_image_count); -} - -void FrameGeneration::CreateDescriptorSetLayout() { - m_descriptor_set_layout = - CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); -} - -void FrameGeneration::CreateDescriptorSets() { - std::vector layouts(MaxFrameGenStage, *m_descriptor_set_layout); - - for (auto& images : m_dynamic_images) { - images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts); - } -} - -void FrameGeneration::CreatePipelineLayouts() { - const VkPushConstantRange range{ - .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, - .offset = 0, - .size = sizeof(PushConstants), - }; - VkPipelineLayoutCreateInfo ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .setLayoutCount = 1, - .pSetLayouts = m_descriptor_set_layout.address(), - .pushConstantRangeCount = 1, - .pPushConstantRanges = &range, - }; - - m_pipeline_layout = m_device.GetLogical().CreatePipelineLayout(ci); -} - -void FrameGeneration::CreatePipelines() { - m_motion_estimation_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout, - std::tie(m_vert_shader, m_motion_estimation_shader)); - m_frame_interpolation_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout, - std::tie(m_vert_shader, m_frame_interpolation_shader)); -} - -void FrameGeneration::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { - Images& images = m_dynamic_images[image_index]; - std::vector image_infos; - std::vector updates; - image_infos.reserve(5); - - // Motion estimation: current frame + previous frame - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, - images.descriptor_sets[MotionEstimation], 0)); - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *m_previous_frame_views[image_index], - images.descriptor_sets[MotionEstimation], 1)); - - // Frame interpolation: current frame + previous frame + motion vectors - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, - images.descriptor_sets[FrameInterpolation], 0)); - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *m_previous_frame_views[image_index], - images.descriptor_sets[FrameInterpolation], 1)); - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[MotionEstimation], - images.descriptor_sets[FrameInterpolation], 2)); - - m_device.GetLogical().UpdateDescriptorSets(updates, {}); -} - -void FrameGeneration::UploadImages(Scheduler& scheduler) { - if (m_images_ready) { - return; - } - - scheduler.Record([&](vk::CommandBuffer cmdbuf) { - for (auto& image : m_dynamic_images) { - ClearColorImage(cmdbuf, *image.images[MotionEstimation]); - ClearColorImage(cmdbuf, *image.images[FrameInterpolation]); - } - for (auto& frame : m_previous_frames) { - ClearColorImage(cmdbuf, *frame); - } - }); - scheduler.Finish(); - - m_images_ready = true; -} - -VkImageView FrameGeneration::Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, - VkImageView source_image_view, VkExtent2D input_image_extent, - const Common::Rectangle& crop_rect) { - // TODO(zephyron): Implement a better frame generation method - return source_image_view; -} - -} // namespace Vulkan \ No newline at end of file diff --git a/src/video_core/renderer_vulkan/present/frame_generation.h b/src/video_core/renderer_vulkan/present/frame_generation.h deleted file mode 100644 index a73a08464..000000000 --- a/src/video_core/renderer_vulkan/present/frame_generation.h +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/math_util.h" -#include "video_core/vulkan_common/vulkan_memory_allocator.h" -#include "video_core/vulkan_common/vulkan_wrapper.h" - -namespace Vulkan { - -class Device; -class Scheduler; - -class FrameGeneration { -public: - explicit FrameGeneration(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, - VkExtent2D extent); - VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, - VkImageView source_image_view, VkExtent2D input_image_extent, - const Common::Rectangle& crop_rect); - -private: - void CreateImages(); - void CreateRenderPasses(); - void CreateSampler(); - void CreateShaders(); - void CreateDescriptorPool(); - void CreateDescriptorSetLayout(); - void CreateDescriptorSets(); - void CreatePipelineLayouts(); - void CreatePipelines(); - - void UploadImages(Scheduler& scheduler); - void UpdateDescriptorSets(VkImageView image_view, size_t image_index); - - const Device& m_device; - MemoryAllocator& m_memory_allocator; - const size_t m_image_count; - const VkExtent2D m_extent; - - enum FrameGenStage { - MotionEstimation, - FrameInterpolation, - MaxFrameGenStage, - }; - - vk::DescriptorPool m_descriptor_pool; - vk::DescriptorSetLayout m_descriptor_set_layout; - vk::PipelineLayout m_pipeline_layout; - vk::ShaderModule m_vert_shader; - vk::ShaderModule m_motion_estimation_shader; - vk::ShaderModule m_frame_interpolation_shader; - vk::Pipeline m_motion_estimation_pipeline; - vk::Pipeline m_frame_interpolation_pipeline; - vk::RenderPass m_renderpass; - vk::Sampler m_sampler; - - struct Images { - vk::DescriptorSets descriptor_sets; - std::array images; - std::array image_views; - std::array framebuffers; - }; - std::vector m_dynamic_images; - bool m_images_ready{}; - - // Frame buffering for motion estimation - std::vector m_previous_frames; - std::vector m_previous_frame_views; - size_t m_current_frame_index{}; -}; - -} // namespace Vulkan \ No newline at end of file diff --git a/src/video_core/renderer_vulkan/present/layer.cpp b/src/video_core/renderer_vulkan/present/layer.cpp index 61556cbd1..425eba566 100644 --- a/src/video_core/renderer_vulkan/present/layer.cpp +++ b/src/video_core/renderer_vulkan/present/layer.cpp @@ -9,7 +9,6 @@ #include "video_core/framebuffer_config.h" #include "video_core/renderer_vulkan/present/fsr.h" #include "video_core/renderer_vulkan/present/fsr2.h" -#include "video_core/renderer_vulkan/present/frame_generation.h" #include "video_core/renderer_vulkan/present/fxaa.h" #include "video_core/renderer_vulkan/present/layer.h" #include "video_core/renderer_vulkan/present/present_push_constants.h" @@ -63,10 +62,6 @@ Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Schedule if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr2) { CreateFSR2(output_size); } - - if (Settings::values.frame_generation.GetValue() == Settings::FrameGeneration::Enabled) { - CreateFrameGeneration(output_size); - } } Layer::~Layer() { @@ -122,13 +117,6 @@ void Layer::ConfigureDraw(PresentPushConstants* out_push_constants, render_extent, crop_rect); crop_rect = {0, 0, 1, 1}; } - - if (frame_generation) { - source_image_view = frame_generation->Draw(scheduler, image_index, source_image, source_image_view, - render_extent, crop_rect); - crop_rect = {0, 0, 1, 1}; - } - SetMatrixData(*out_push_constants, layout); SetVertexData(*out_push_constants, layout, crop_rect); @@ -182,10 +170,6 @@ void Layer::CreateFSR2(VkExtent2D output_size) { fsr2 = std::make_unique(device, memory_allocator, image_count, output_size); } -void Layer::CreateFrameGeneration(VkExtent2D output_size) { - frame_generation = std::make_unique(device, memory_allocator, image_count, output_size); -} - void Layer::RefreshResources(const Tegra::FramebufferConfig& framebuffer) { if (framebuffer.width == raw_width && framebuffer.height == raw_height && framebuffer.pixel_format == pixel_format && !raw_images.empty()) { diff --git a/src/video_core/renderer_vulkan/present/layer.h b/src/video_core/renderer_vulkan/present/layer.h index ff2bb313a..348f1d650 100644 --- a/src/video_core/renderer_vulkan/present/layer.h +++ b/src/video_core/renderer_vulkan/present/layer.h @@ -32,7 +32,6 @@ class AntiAliasPass; class Device; class FSR; class FSR2; -class FrameGeneration; class MemoryAllocator; struct PresentPushConstants; class RasterizerVulkan; @@ -59,7 +58,6 @@ private: void CreateRawImages(const Tegra::FramebufferConfig& framebuffer); void CreateFSR(VkExtent2D output_size); void CreateFSR2(VkExtent2D output_size); - void CreateFrameGeneration(VkExtent2D output_size); void RefreshResources(const Tegra::FramebufferConfig& framebuffer); void SetAntiAliasPass(); @@ -96,7 +94,6 @@ private: std::unique_ptr fsr{}; std::unique_ptr fsr2{}; - std::unique_ptr frame_generation{}; std::vector resource_ticks{}; };