feat: remove frame generation options from all platforms

Frame generation was marked as WIP and not ready for production use.
This commit removes all frame generation related code and UI elements
to prevent users from enabling an incomplete feature.

Changes:
- Remove frame generation settings from Android UI (Kotlin/XML)
- Remove frame generation settings from Desktop UI (Qt/C++)
- Remove frame generation enums and settings from core (C++)
- Remove frame generation Vulkan renderer implementation
- Remove frame generation shader files
- Update CMake build configuration

Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
Zephyron
2025-08-17 16:09:05 +10:00
parent 06f13f3cb1
commit e27fc65d11
15 changed files with 3 additions and 381 deletions

View File

@@ -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"),

View File

@@ -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,

View File

@@ -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)

View File

@@ -187,25 +187,7 @@
<item>3</item>
</integer-array>
<string-array name="frameGenerationNames">
<item>@string/frame_generation_disabled</item>
<item>@string/frame_generation_enabled</item>
</string-array>
<integer-array name="frameGenerationValues">
<item>0</item>
<item>1</item>
</integer-array>
<string-array name="frameGenerationModeNames">
<item>@string/frame_generation_mode_interpolation</item>
<item>@string/frame_generation_mode_extrapolation</item>
</string-array>
<integer-array name="frameGenerationModeValues">
<item>0</item>
<item>1</item>
</integer-array>
<string-array name="frameSkippingNames">
<item>@string/frame_skipping_disabled</item>

View File

@@ -424,7 +424,6 @@
<string name="astc_settings_header">ASTC Settings</string>
<string name="advanced_graphics_header">Advanced Graphics</string>
<string name="applet_settings_header">Applet Settings</string>
<string name="frame_generation_header">Frame Generation</string>
<!-- Applet Mode Settings -->
<string name="cabinet_applet_mode">Cabinet Applet Mode</string>
@@ -1229,14 +1228,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<string name="shader_backend_description">Controls which shader backend to use for rendering.</string>
<string name="vram_usage_mode">VRAM Usage Mode</string>
<string name="vram_usage_mode_description">Controls how aggressively VRAM is used. Conservative mode limits VRAM usage for better stability.</string>
<string name="frame_generation">Frame Generation</string>
<string name="frame_generation_description">Enables frame generation to create intermediate frames, potentially doubling the perceived frame rate.</string>
<string name="frame_generation_mode">Frame Generation Mode</string>
<string name="frame_generation_mode_description">Interpolation creates frames between existing ones, while Extrapolation predicts future frames.</string>
<string name="frame_generation_disabled">Disabled</string>
<string name="frame_generation_enabled">Enabled</string>
<string name="frame_generation_mode_interpolation">Interpolation</string>
<string name="frame_generation_mode_extrapolation">Extrapolation</string>
<string name="frame_skipping">Frame Skipping</string>
<string name="frame_skipping_description">Skips frames to maintain performance when the system cannot keep up with the target frame rate.</string>
<string name="frame_skipping_mode">Frame Skipping Mode</string>

View File

@@ -129,10 +129,7 @@ std::unique_ptr<TranslationMap> 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<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
PAIR(FSR2QualityMode, Performance, tr("Performance")),
PAIR(FSR2QualityMode, UltraPerformance, tr("Ultra Performance")),
}});
translations->insert({Settings::EnumMetadata<Settings::FrameGeneration>::Index(),
{
PAIR(FrameGeneration, Disabled, tr("Disabled")),
PAIR(FrameGeneration, Enabled, tr("Enabled")),
}});
translations->insert({Settings::EnumMetadata<Settings::FrameGenerationMode>::Index(),
{
PAIR(FrameGenerationMode, Interpolation, tr("Interpolation")),
PAIR(FrameGenerationMode, Extrapolation, tr("Extrapolation")),
}});
translations->insert({Settings::EnumMetadata<Settings::FrameSkipping>::Index(),
{
PAIR(FrameSkipping, Disabled, tr("Disabled")),

View File

@@ -358,25 +358,7 @@ struct Values {
true,
true};
SwitchableSetting<FrameGeneration, true> frame_generation{linkage,
FrameGeneration::Disabled, // Disabled by default
FrameGeneration::Disabled,
FrameGeneration::Enabled,
"frame_generation",
Category::Renderer,
Specialization::Default,
true,
true};
SwitchableSetting<FrameGenerationMode, true> frame_generation_mode{linkage,
FrameGenerationMode::Interpolation, // Interpolation by default
FrameGenerationMode::Interpolation,
FrameGenerationMode::Extrapolation,
"frame_generation_mode",
Category::Renderer,
Specialization::Default,
true,
true};
SwitchableSetting<FrameSkipping, true> frame_skipping{linkage,
FrameSkipping::Disabled, // Disabled by default

View File

@@ -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);

View File

@@ -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

View File

@@ -1,18 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <cstdint>
namespace Vulkan {
namespace FrameGenShaders {
// Minimal fragment shader for frame generation
constexpr std::array<std::uint32_t, 4> FRAG_SPV = {{
0x07230203, 0x00010000, 0x0008000A, 0x00000004
}};
} // namespace FrameGenShaders
} // namespace Vulkan

View File

@@ -1,18 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <cstdint>
namespace Vulkan {
namespace FrameGenShaders {
// Minimal vertex shader for frame generation
constexpr std::array<std::uint32_t, 4> VERT_SPV = {{
0x07230203, 0x00010000, 0x0008000A, 0x00000004
}};
} // namespace FrameGenShaders
} // namespace Vulkan

View File

@@ -1,163 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <bit>
#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<u32, 4 * 4>;
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<VkDescriptorSetLayout> 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<VkDescriptorImageInfo> image_infos;
std::vector<VkWriteDescriptorSet> 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<f32>& crop_rect) {
// TODO(zephyron): Implement a better frame generation method
return source_image_view;
}
} // namespace Vulkan

View File

@@ -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<f32>& 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<vk::Image, MaxFrameGenStage> images;
std::array<vk::ImageView, MaxFrameGenStage> image_views;
std::array<vk::Framebuffer, MaxFrameGenStage> framebuffers;
};
std::vector<Images> m_dynamic_images;
bool m_images_ready{};
// Frame buffering for motion estimation
std::vector<vk::Image> m_previous_frames;
std::vector<vk::ImageView> m_previous_frame_views;
size_t m_current_frame_index{};
};
} // namespace Vulkan

View File

@@ -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<FSR2>(device, memory_allocator, image_count, output_size);
}
void Layer::CreateFrameGeneration(VkExtent2D output_size) {
frame_generation = std::make_unique<FrameGeneration>(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()) {

View File

@@ -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> fsr{};
std::unique_ptr<FSR2> fsr2{};
std::unique_ptr<FrameGeneration> frame_generation{};
std::vector<u64> resource_ticks{};
};