mirror of
https://git.eden-emu.dev/archive/citron
synced 2026-03-28 04:19:33 -04:00
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:
@@ -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
|
||||
@@ -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
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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{};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user