mirror of
https://git.eden-emu.dev/archive/citron
synced 2026-03-23 09:59:39 -04:00
fix/feat: Introduce CAS & Add Dynamic Slider Viewing for Toggled WAF's, and remove Android ASTC visibility on PC
This commit is contained in:
@@ -31,6 +31,12 @@
|
||||
#include <qgridlayout.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "citron/configuration/configuration_shared.h"
|
||||
#include "citron/configuration/configure_graphics.h"
|
||||
#include "citron/configuration/shared_widget.h"
|
||||
#include "citron/qt_common.h"
|
||||
#include "citron/uisettings.h"
|
||||
#include "citron/vk_device_info.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/dynamic_library.h"
|
||||
#include "common/logging/log.h"
|
||||
@@ -38,12 +44,6 @@
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/core.h"
|
||||
#include "ui_configure_graphics.h"
|
||||
#include "citron/configuration/configuration_shared.h"
|
||||
#include "citron/configuration/configure_graphics.h"
|
||||
#include "citron/configuration/shared_widget.h"
|
||||
#include "citron/qt_common.h"
|
||||
#include "citron/uisettings.h"
|
||||
#include "citron/vk_device_info.h"
|
||||
|
||||
static const std::vector<VkPresentModeKHR> default_present_modes{VK_PRESENT_MODE_IMMEDIATE_KHR,
|
||||
VK_PRESENT_MODE_FIFO_KHR};
|
||||
@@ -276,6 +276,14 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) {
|
||||
return builder.BuildWidget(
|
||||
setting, apply_funcs, ConfigurationShared::RequestType::ReverseSlider, true,
|
||||
0.5f, nullptr, tr("%", "FSR sharpening percentage (e.g. 50%)"));
|
||||
} else if (setting->Id() == Settings::values.cas_sharpening_slider.Id()) {
|
||||
// CAS needs a 0.5 multiplier to show 0-100% (actually 0.0-0.5 internally if we
|
||||
// follow FSR) Wait, CAS slider is 0-100 in settings.h. FSR is 0-200 internally?
|
||||
// Actually FSR slider is 0-200 in settings.h.
|
||||
// Let's check settings.h for CAS slider again.
|
||||
return builder.BuildWidget(setting, apply_funcs,
|
||||
ConfigurationShared::RequestType::Slider, true, 1.0f,
|
||||
nullptr, tr("%"));
|
||||
} else {
|
||||
return builder.BuildWidget(setting, apply_funcs);
|
||||
}
|
||||
@@ -289,9 +297,20 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Store reference to the FSR sharpness widget for later use
|
||||
if (setting->Id() == Settings::values.fsr_sharpening_slider.Id()) {
|
||||
fsr_sharpness_widget = widget;
|
||||
} else if (setting->Id() == Settings::values.cas_sharpening_slider.Id()) {
|
||||
cas_sharpness_widget = widget;
|
||||
} else if (setting->Id() == Settings::values.lanczos_quality.Id()) {
|
||||
lq_widget = widget;
|
||||
} else if (setting->Id() == Settings::values.crt_scanline_strength.Id() ||
|
||||
setting->Id() == Settings::values.crt_curvature.Id() ||
|
||||
setting->Id() == Settings::values.crt_gamma.Id() ||
|
||||
setting->Id() == Settings::values.crt_bloom.Id() ||
|
||||
setting->Id() == Settings::values.crt_mask_type.Id() ||
|
||||
setting->Id() == Settings::values.crt_brightness.Id() ||
|
||||
setting->Id() == Settings::values.crt_alpha.Id()) {
|
||||
crt_widgets.push_back(widget);
|
||||
}
|
||||
|
||||
if (setting->Id() == Settings::values.renderer_backend.Id()) {
|
||||
@@ -372,39 +391,61 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) {
|
||||
api_grid_layout->addWidget(widget);
|
||||
}
|
||||
|
||||
// Set up FSR sharpness slider conditional enabling
|
||||
if (fsr_sharpness_widget) {
|
||||
// Create a function to update the enabled state based on scaling filter
|
||||
auto update_fsr_sharpness_enabled = [this]() {
|
||||
if (fsr_sharpness_widget) {
|
||||
const auto scaling_filter = Settings::values.scaling_filter.GetValue();
|
||||
const bool fsr2_selected = (scaling_filter == Settings::ScalingFilter::Fsr2);
|
||||
fsr_sharpness_widget->setEnabled(!fsr2_selected);
|
||||
// Set up Scaling Filter conditional visibility for sliders
|
||||
QComboBox* scaling_filter_combobox = nullptr;
|
||||
for (const auto& [id, widget] : hold_graphics) {
|
||||
if (id == Settings::values.scaling_filter.Id()) {
|
||||
scaling_filter_combobox = static_cast<ConfigurationShared::Widget*>(widget)->combobox;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Grey out the widget when disabled
|
||||
if (scaling_filter_combobox) {
|
||||
// Create a function to update the enabled/visible state based on current UI selection
|
||||
auto update_visibility = [this, scaling_filter_combobox, &builder]() {
|
||||
const auto& translations = builder.ComboboxTranslations().at(
|
||||
Settings::EnumMetadata<Settings::ScalingFilter>::Index());
|
||||
const auto scaling_filter = static_cast<Settings::ScalingFilter>(
|
||||
translations.at(scaling_filter_combobox->currentIndex()).first);
|
||||
|
||||
if (fsr_sharpness_widget) {
|
||||
const bool fsr_selected = (scaling_filter == Settings::ScalingFilter::Fsr);
|
||||
const bool fsr2_selected = (scaling_filter == Settings::ScalingFilter::Fsr2);
|
||||
|
||||
// Visible only if FSR 1 or FSR 2 is selected
|
||||
fsr_sharpness_widget->setVisible(fsr_selected || fsr2_selected);
|
||||
|
||||
// FSR 2.0 doesn't use the FSR 1.0 sharpness slider but we allow it to be visible
|
||||
// but disabled to show it's an FSR-related setting.
|
||||
fsr_sharpness_widget->setEnabled(!fsr2_selected);
|
||||
if (fsr2_selected) {
|
||||
fsr_sharpness_widget->setStyleSheet(QStringLiteral("QWidget { color: gray; }"));
|
||||
} else {
|
||||
fsr_sharpness_widget->setStyleSheet(QStringLiteral(""));
|
||||
}
|
||||
}
|
||||
|
||||
if (cas_sharpness_widget) {
|
||||
cas_sharpness_widget->setVisible(scaling_filter == Settings::ScalingFilter::Cas);
|
||||
}
|
||||
|
||||
if (lq_widget) {
|
||||
lq_widget->setVisible(scaling_filter == Settings::ScalingFilter::Lanczos);
|
||||
}
|
||||
|
||||
const bool crt_selected = (scaling_filter == Settings::ScalingFilter::CRTEasyMode ||
|
||||
scaling_filter == Settings::ScalingFilter::CRTRoyale);
|
||||
for (auto* crt_widget : crt_widgets) {
|
||||
crt_widget->setVisible(crt_selected);
|
||||
}
|
||||
};
|
||||
|
||||
// Initial state
|
||||
update_fsr_sharpness_enabled();
|
||||
update_visibility();
|
||||
|
||||
// Connect to scaling filter changes
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
// Find the scaling filter widget and connect to its changes
|
||||
for (const auto& [id, widget] : hold_graphics) {
|
||||
if (id == Settings::values.scaling_filter.Id()) {
|
||||
auto* config_widget = static_cast<ConfigurationShared::Widget*>(widget);
|
||||
QObject::connect(config_widget->combobox, QOverload<int>::of(&QComboBox::activated),
|
||||
[update_fsr_sharpness_enabled]() { update_fsr_sharpness_enabled(); });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Connect to scaling filter changes (real-time update)
|
||||
QObject::connect(scaling_filter_combobox, QOverload<int>::of(&QComboBox::activated),
|
||||
[update_visibility]() { update_visibility(); });
|
||||
}
|
||||
|
||||
// Background color is too specific to build into the new system, so we manage it here
|
||||
@@ -615,8 +656,10 @@ void ConfigureGraphics::SetTemplateStyleSheet(const QString& sheet) {
|
||||
// Replace all placeholders with the actual color values in #RRGGBB format
|
||||
// Use QStringLiteral() to satisfy Qt 6's explicit string constructor requirements
|
||||
final_style.replace(QStringLiteral("%%ACCENT_COLOR%%"), accent_color.name(QColor::HexRgb));
|
||||
final_style.replace(QStringLiteral("%%ACCENT_COLOR_HOVER%%"), accent_color_hover.name(QColor::HexRgb));
|
||||
final_style.replace(QStringLiteral("%%ACCENT_COLOR_PRESSED%%"), accent_color_pressed.name(QColor::HexRgb));
|
||||
final_style.replace(QStringLiteral("%%ACCENT_COLOR_HOVER%%"),
|
||||
accent_color_hover.name(QColor::HexRgb));
|
||||
final_style.replace(QStringLiteral("%%ACCENT_COLOR_PRESSED%%"),
|
||||
accent_color_pressed.name(QColor::HexRgb));
|
||||
|
||||
// Apply the processed stylesheet to this widget and all its children (like checkboxes)
|
||||
this->setStyleSheet(final_style);
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
#include <QWidget>
|
||||
#include <qobjectdefs.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include "citron/configuration/configuration_shared.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "configuration/shared_translation.h"
|
||||
#include "vk_device_info.h"
|
||||
#include "citron/configuration/configuration_shared.h"
|
||||
|
||||
class QPushButton;
|
||||
class QEvent;
|
||||
@@ -47,7 +47,8 @@ class ConfigureGraphics : public ConfigurationShared::Tab {
|
||||
Q_OBJECT
|
||||
|
||||
// This property allows the main UI file to pass its stylesheet to this widget
|
||||
Q_PROPERTY(QString templateStyleSheet READ GetTemplateStyleSheet WRITE SetTemplateStyleSheet NOTIFY TemplateStyleSheetChanged)
|
||||
Q_PROPERTY(QString templateStyleSheet READ GetTemplateStyleSheet WRITE SetTemplateStyleSheet
|
||||
NOTIFY TemplateStyleSheetChanged)
|
||||
|
||||
public:
|
||||
explicit ConfigureGraphics(
|
||||
@@ -125,6 +126,9 @@ private:
|
||||
QComboBox* aspect_ratio_combobox;
|
||||
QComboBox* resolution_combobox;
|
||||
QWidget* fsr_sharpness_widget;
|
||||
QWidget* cas_sharpness_widget;
|
||||
QWidget* lq_widget;
|
||||
std::vector<QWidget*> crt_widgets;
|
||||
|
||||
// This variable will hold the raw stylesheet string
|
||||
QString m_template_style_sheet;
|
||||
|
||||
@@ -2,21 +2,22 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "citron/configuration/configure_graphics_advanced.h"
|
||||
#include <vector>
|
||||
#include <QLabel>
|
||||
#include <qnamespace.h>
|
||||
#include "citron/configuration/configuration_shared.h"
|
||||
#include "citron/configuration/configure_graphics_advanced.h"
|
||||
#include "citron/configuration/shared_translation.h"
|
||||
#include "citron/configuration/shared_widget.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "ui_configure_graphics_advanced.h"
|
||||
#include "citron/configuration/configuration_shared.h"
|
||||
#include "citron/configuration/shared_translation.h"
|
||||
#include "citron/configuration/shared_widget.h"
|
||||
|
||||
|
||||
ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(
|
||||
const Core::System& system_, std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
|
||||
const ConfigurationShared::Builder& builder, QWidget* parent)
|
||||
: Tab(group_, parent), ui{std::make_unique<Ui::ConfigureGraphicsAdvanced>()}, system{system_} {
|
||||
: Tab(group_, parent), ui{std::make_unique<Ui::ConfigureGraphicsAdvanced>()}, system{system_} {
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
@@ -36,27 +37,32 @@ void ConfigureGraphicsAdvanced::Setup(const ConfigurationShared::Builder& builde
|
||||
std::map<u32, QWidget*> hold{}; // A map will sort the data for us
|
||||
|
||||
for (auto setting :
|
||||
Settings::values.linkage.by_category[Settings::Category::RendererAdvanced]) {
|
||||
Settings::values.linkage.by_category[Settings::Category::RendererAdvanced]) {
|
||||
#ifndef ANDROID
|
||||
if (setting->Id() == Settings::values.android_astc_mode.Id()) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs);
|
||||
|
||||
if (widget == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (!widget->Valid()) {
|
||||
widget->deleteLater();
|
||||
continue;
|
||||
}
|
||||
|
||||
hold.emplace(setting->Id(), widget);
|
||||
|
||||
// Keep track of enable_compute_pipelines so we can display it when needed
|
||||
if (setting->Id() == Settings::values.enable_compute_pipelines.Id()) {
|
||||
checkbox_enable_compute_pipelines = widget;
|
||||
}
|
||||
if (widget == nullptr) {
|
||||
continue;
|
||||
}
|
||||
for (const auto& [id, widget] : hold) {
|
||||
layout.addWidget(widget);
|
||||
if (!widget->Valid()) {
|
||||
widget->deleteLater();
|
||||
continue;
|
||||
}
|
||||
|
||||
hold.emplace(setting->Id(), widget);
|
||||
|
||||
// Keep track of enable_compute_pipelines so we can display it when needed
|
||||
if (setting->Id() == Settings::values.enable_compute_pipelines.Id()) {
|
||||
checkbox_enable_compute_pipelines = widget;
|
||||
}
|
||||
}
|
||||
for (const auto& [id, widget] : hold) {
|
||||
layout.addWidget(widget);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGraphicsAdvanced::ApplyConfiguration() {
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
#include <utility>
|
||||
#include <QCoreApplication>
|
||||
#include <QWidget>
|
||||
#include "citron/uisettings.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "common/settings_setting.h"
|
||||
#include "common/time_zone.h"
|
||||
#include "citron/uisettings.h"
|
||||
|
||||
namespace ConfigurationShared {
|
||||
|
||||
@@ -130,30 +130,42 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
|
||||
"much more VRAM and bandwidth.\n"
|
||||
"Options lower than 1X can cause rendering issues."));
|
||||
INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QStringLiteral());
|
||||
INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"),
|
||||
INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness"),
|
||||
tr("Determines how sharpened the image will look while using FSR's dynamic contrast."));
|
||||
INSERT(Settings, lanczos_quality, tr("Lanczos Quality:"), tr("The quality of the Lanczos filter. Higher is sharper but more expensive."));
|
||||
INSERT(Settings, cas_sharpening_slider, tr("CAS Sharpness"),
|
||||
tr("Determines the level of sharpening applied by the Contrast Adaptive Sharpening "
|
||||
"(CAS) filter."));
|
||||
INSERT(Settings, lanczos_quality, tr("Lanczos Quality:"),
|
||||
tr("The quality of the Lanczos filter. Higher is sharper but more expensive."));
|
||||
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."));
|
||||
tr("Selects the quality mode for FSR 2.0 upscaling. Quality provides better image "
|
||||
"quality, Performance provides better performance."));
|
||||
INSERT(Settings, crt_scanline_strength, tr("CRT Scanline Strength:"),
|
||||
tr("Controls the intensity of scanlines. Higher values create more pronounced horizontal lines."));
|
||||
tr("Controls the intensity of scanlines. Higher values create more pronounced "
|
||||
"horizontal lines."));
|
||||
INSERT(Settings, crt_curvature, tr("CRT Curvature:"),
|
||||
tr("Applies barrel distortion to simulate the curved screen of a CRT monitor."));
|
||||
INSERT(Settings, crt_gamma, tr("CRT Gamma:"),
|
||||
tr("Adjusts the gamma correction curve. Higher values brighten the image, lower values darken it."));
|
||||
tr("Adjusts the gamma correction curve. Higher values brighten the image, lower values "
|
||||
"darken it."));
|
||||
INSERT(Settings, crt_bloom, tr("CRT Bloom:"),
|
||||
tr("Controls the glow effect around bright areas, simulating phosphor persistence."));
|
||||
INSERT(Settings, crt_mask_type, tr("CRT Mask Type:"),
|
||||
tr("Selects the phosphor mask pattern: None, Aperture Grille (vertical stripes), or Shadow Mask (triangular pattern)."));
|
||||
tr("Selects the phosphor mask pattern: None, Aperture Grille (vertical stripes), or "
|
||||
"Shadow Mask (triangular pattern)."));
|
||||
INSERT(Settings, crt_brightness, tr("CRT Brightness:"),
|
||||
tr("Adjusts overall brightness of the CRT effect. Use to compensate for darkening from other effects."));
|
||||
tr("Adjusts overall brightness of the CRT effect. Use to compensate for darkening from "
|
||||
"other effects."));
|
||||
INSERT(Settings, crt_alpha, tr("CRT Alpha:"),
|
||||
tr("Controls transparency of the CRT effect. Lower values make the effect more transparent."));
|
||||
tr("Controls transparency of the CRT effect. Lower values make the effect more "
|
||||
"transparent."));
|
||||
|
||||
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:"),
|
||||
tr("Adaptive mode skips frames based on performance, while Fixed mode skips a specific number of 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:"),
|
||||
tr("Adaptive mode skips frames based on performance, while Fixed mode skips a specific "
|
||||
"number of frames."));
|
||||
INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"),
|
||||
tr("The anti-aliasing method to use.\nSMAA offers the best quality.\nFXAA has a "
|
||||
"lower performance impact and can produce a better and more stable picture under "
|
||||
@@ -202,10 +214,11 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
|
||||
INSERT(Settings, vram_limit_mb, tr("VRAM Limit (MB):"),
|
||||
tr("Sets the maximum VRAM usage limit in megabytes. Set to 0 for auto-detection "
|
||||
"(80% of available VRAM). Recommended: 6144 for 8GB GPUs, 4096 for 6GB GPUs."));
|
||||
INSERT(Settings, gc_aggressiveness, tr("GC Aggressiveness:"),
|
||||
tr("Controls how aggressively the emulator evicts unused textures and buffers from VRAM.\n"
|
||||
"Off: Disable automatic cleanup (not recommended, may cause crashes).\n"
|
||||
"Light: Gentle cleanup, keeps more textures cached (recommended)."));
|
||||
INSERT(
|
||||
Settings, gc_aggressiveness, tr("GC Aggressiveness:"),
|
||||
tr("Controls how aggressively the emulator evicts unused textures and buffers from VRAM.\n"
|
||||
"Off: Disable automatic cleanup (not recommended, may cause crashes).\n"
|
||||
"Light: Gentle cleanup, keeps more textures cached (recommended)."));
|
||||
INSERT(Settings, texture_eviction_frames, tr("Texture Eviction Frames:"),
|
||||
tr("Number of frames a texture must be unused before it can be evicted. "
|
||||
"Lower values free VRAM faster but may cause more texture reloading."));
|
||||
@@ -271,16 +284,18 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
|
||||
"unlocked."));
|
||||
INSERT(Settings, barrier_feedback_loops, tr("Barrier feedback loops"),
|
||||
tr("Improves rendering of transparency effects in specific games."));
|
||||
INSERT(Settings, extended_dynamic_state, tr("Extended Dynamic State:"),
|
||||
tr("Selects the level of Vulkan Extended Dynamic State support.\n"
|
||||
"EDS3: Enables all Extended Dynamic State features (recommended).\n"
|
||||
"EDS2: Enables EDS1 and EDS2 features only.\n"
|
||||
"EDS1: Enables basic Extended Dynamic State features only.\n"
|
||||
"Disabled: Disables all Extended Dynamic State features (may reduce compatibility)."));
|
||||
INSERT(Settings, use_conditional_rendering, tr("Use conditional rendering"),
|
||||
tr("Enables conditional rendering based on query results.\n"
|
||||
"Disabling this can fix flickering objects in some games but may impact performance.\n"
|
||||
"Try disabling if you see objects appearing and disappearing rapidly."));
|
||||
INSERT(
|
||||
Settings, extended_dynamic_state, tr("Extended Dynamic State:"),
|
||||
tr("Selects the level of Vulkan Extended Dynamic State support.\n"
|
||||
"EDS3: Enables all Extended Dynamic State features (recommended).\n"
|
||||
"EDS2: Enables EDS1 and EDS2 features only.\n"
|
||||
"EDS1: Enables basic Extended Dynamic State features only.\n"
|
||||
"Disabled: Disables all Extended Dynamic State features (may reduce compatibility)."));
|
||||
INSERT(
|
||||
Settings, use_conditional_rendering, tr("Use conditional rendering"),
|
||||
tr("Enables conditional rendering based on query results.\n"
|
||||
"Disabling this can fix flickering objects in some games but may impact performance.\n"
|
||||
"Try disabling if you see objects appearing and disappearing rapidly."));
|
||||
|
||||
// Renderer (Debug)
|
||||
|
||||
@@ -477,6 +492,7 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
|
||||
PAIR(ScalingFilter, Fsr2, tr("AMD FidelityFX™️ Super Resolution 2.0")),
|
||||
PAIR(ScalingFilter, CRTEasyMode, tr("CRT EasyMode")),
|
||||
PAIR(ScalingFilter, CRTRoyale, tr("CRT Royale")),
|
||||
PAIR(ScalingFilter, Cas, tr("CAS (Contrast Adaptive Sharpening)")),
|
||||
}});
|
||||
translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(),
|
||||
{
|
||||
@@ -493,16 +509,16 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
|
||||
PAIR(FSR2QualityMode, UltraPerformance, tr("Ultra Performance")),
|
||||
}});
|
||||
|
||||
translations->insert({Settings::EnumMetadata<Settings::FrameSkipping>::Index(),
|
||||
{
|
||||
PAIR(FrameSkipping, Disabled, tr("Disabled")),
|
||||
PAIR(FrameSkipping, Enabled, tr("Enabled")),
|
||||
}});
|
||||
translations->insert({Settings::EnumMetadata<Settings::FrameSkippingMode>::Index(),
|
||||
{
|
||||
PAIR(FrameSkippingMode, Adaptive, tr("Adaptive")),
|
||||
PAIR(FrameSkippingMode, Fixed, tr("Fixed")),
|
||||
}});
|
||||
translations->insert({Settings::EnumMetadata<Settings::FrameSkipping>::Index(),
|
||||
{
|
||||
PAIR(FrameSkipping, Disabled, tr("Disabled")),
|
||||
PAIR(FrameSkipping, Enabled, tr("Enabled")),
|
||||
}});
|
||||
translations->insert({Settings::EnumMetadata<Settings::FrameSkippingMode>::Index(),
|
||||
{
|
||||
PAIR(FrameSkippingMode, Adaptive, tr("Adaptive")),
|
||||
PAIR(FrameSkippingMode, Fixed, tr("Fixed")),
|
||||
}});
|
||||
translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(),
|
||||
{
|
||||
PAIR(AspectRatio, R16_9, tr("Default (16:9)")),
|
||||
|
||||
@@ -39,17 +39,17 @@ static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map
|
||||
{Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))},
|
||||
{Settings::ScalingFilter::Gaussian,
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))},
|
||||
{Settings::ScalingFilter::Lanczos, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Lanczos"))},
|
||||
{Settings::ScalingFilter::Lanczos, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Lanczos"))},
|
||||
{Settings::ScalingFilter::ScaleForce,
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))},
|
||||
{Settings::ScalingFilter::ScaleFx,
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleFX"))},
|
||||
{Settings::ScalingFilter::ScaleFx, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleFX"))},
|
||||
{Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
|
||||
{Settings::ScalingFilter::Fsr2, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR 2.0"))},
|
||||
{Settings::ScalingFilter::CRTEasyMode,
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "CRT EasyMode"))},
|
||||
{Settings::ScalingFilter::CRTRoyale,
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "CRT Royale"))},
|
||||
{Settings::ScalingFilter::Cas, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "CAS"))},
|
||||
};
|
||||
|
||||
static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map = {
|
||||
|
||||
@@ -5710,8 +5710,22 @@ void GMainWindow::UpdateAPIText() {
|
||||
void GMainWindow::UpdateFilterText() {
|
||||
const auto filter = Settings::values.scaling_filter.GetValue();
|
||||
const auto filter_text = ConfigurationShared::scaling_filter_texts_map.find(filter)->second;
|
||||
filter_status_button->setText(filter == Settings::ScalingFilter::Fsr ? tr("FSR")
|
||||
: filter_text.toUpper());
|
||||
QString label;
|
||||
switch (filter) {
|
||||
case Settings::ScalingFilter::Fsr:
|
||||
label = tr("FSR");
|
||||
break;
|
||||
case Settings::ScalingFilter::Fsr2:
|
||||
label = tr("FSR2");
|
||||
break;
|
||||
case Settings::ScalingFilter::Cas:
|
||||
label = tr("CAS");
|
||||
break;
|
||||
default:
|
||||
label = filter_text.toUpper();
|
||||
break;
|
||||
}
|
||||
filter_status_button->setText(label);
|
||||
}
|
||||
|
||||
void GMainWindow::UpdateAAText() {
|
||||
@@ -6512,7 +6526,6 @@ void GMainWindow::CheckForUpdatesAutomatically() {
|
||||
|
||||
void GMainWindow::RegisterAutoloaderContents() {
|
||||
autoloader_provider->ClearAllEntries();
|
||||
const auto& disabled_addons = Settings::values.disabled_addons;
|
||||
|
||||
const auto sdmc_path = Common::FS::GetCitronPath(Common::FS::CitronPath::SDMCDir);
|
||||
const auto autoloader_root = sdmc_path / "autoloader";
|
||||
@@ -6526,17 +6539,13 @@ void GMainWindow::RegisterAutoloaderContents() {
|
||||
if (!title_dir_entry.is_directory())
|
||||
continue;
|
||||
|
||||
u64 title_id_val = 0;
|
||||
try {
|
||||
title_id_val = std::stoull(title_dir_entry.path().filename().string(), nullptr, 16);
|
||||
[[maybe_unused]] auto val =
|
||||
std::stoull(title_dir_entry.path().filename().string(), nullptr, 16);
|
||||
} catch (const std::invalid_argument&) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto it = disabled_addons.find(title_id_val);
|
||||
const auto& disabled_for_game =
|
||||
(it != disabled_addons.end()) ? it->second : std::vector<std::string>{};
|
||||
|
||||
const auto process_content_type = [&](const std::filesystem::path& content_path) {
|
||||
if (!Common::FS::IsDir(content_path))
|
||||
return;
|
||||
@@ -6546,7 +6555,7 @@ void GMainWindow::RegisterAutoloaderContents() {
|
||||
continue;
|
||||
|
||||
const std::string mod_name = mod_dir_entry.path().filename().string();
|
||||
// Citron: We do NOT skip disabled content here.
|
||||
// We do NOT skip disabled content here.
|
||||
// If we skip it here, it doesn't show up in the UI (Properties -> Add-ons),
|
||||
// making it impossible for the user to re-enable it.
|
||||
// The PatchManager (core/file_sys/patch_manager.cpp) handles the actual enforcement
|
||||
|
||||
@@ -353,72 +353,47 @@ struct Values {
|
||||
Specialization::Percentage,
|
||||
true,
|
||||
true};
|
||||
SwitchableSetting<int, true> cas_sharpening_slider{linkage,
|
||||
50,
|
||||
0,
|
||||
100,
|
||||
"cas_sharpening_slider",
|
||||
Category::Renderer,
|
||||
Specialization::Scalar |
|
||||
Specialization::Percentage,
|
||||
true,
|
||||
true};
|
||||
|
||||
// CRT Shader Settings (only active when CRT filter is selected)
|
||||
SwitchableSetting<float, true> crt_scanline_strength{linkage,
|
||||
1.0f, // 100/100 = 1.0 (range 0-200, actual 0.0-2.0)
|
||||
0.0f,
|
||||
2.0f,
|
||||
"crt_scanline_strength",
|
||||
Category::Renderer,
|
||||
Specialization::Scalar,
|
||||
true,
|
||||
true};
|
||||
SwitchableSetting<float, true> crt_curvature{linkage,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
"crt_curvature",
|
||||
Category::Renderer,
|
||||
Specialization::Scalar,
|
||||
true,
|
||||
true};
|
||||
SwitchableSetting<float, true> crt_gamma{linkage,
|
||||
1.0f, // 100 maps to 1.0 (range 1-300, actual 1.0-3.0)
|
||||
1.0f,
|
||||
3.0f,
|
||||
"crt_gamma",
|
||||
Category::Renderer,
|
||||
Specialization::Scalar,
|
||||
true,
|
||||
true};
|
||||
SwitchableSetting<float, true> crt_bloom{linkage,
|
||||
0.33f, // 33/100 = 0.33 (range 0-100, actual 0.0-1.0)
|
||||
0.0f,
|
||||
1.0f,
|
||||
"crt_bloom",
|
||||
Category::Renderer,
|
||||
Specialization::Scalar,
|
||||
true,
|
||||
true};
|
||||
SwitchableSetting<int, true> crt_mask_type{linkage,
|
||||
1, // Already correct
|
||||
0,
|
||||
2,
|
||||
"crt_mask_type",
|
||||
Category::Renderer,
|
||||
Specialization::Scalar,
|
||||
true,
|
||||
true}; // 0=none, 1=aperture, 2=shadow
|
||||
SwitchableSetting<float, true> crt_brightness{linkage,
|
||||
1.0f, // Default brightness (1.0 = no change)
|
||||
0.0f,
|
||||
2.0f,
|
||||
"crt_brightness",
|
||||
Category::Renderer,
|
||||
Specialization::Scalar,
|
||||
true,
|
||||
true};
|
||||
SwitchableSetting<float, true> crt_alpha{linkage,
|
||||
1.0f, // Default alpha (1.0 = fully opaque)
|
||||
0.0f,
|
||||
1.0f,
|
||||
"crt_alpha",
|
||||
Category::Renderer,
|
||||
Specialization::Scalar,
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<float, true> crt_scanline_strength{
|
||||
linkage,
|
||||
1.0f, // 100/100 = 1.0 (range 0-200, actual 0.0-2.0)
|
||||
0.0f, 2.0f, "crt_scanline_strength", Category::Renderer, Specialization::Scalar,
|
||||
true, true};
|
||||
SwitchableSetting<float, true> crt_curvature{
|
||||
linkage, 0.0f, 0.0f, 1.0f, "crt_curvature", Category::Renderer, Specialization::Scalar,
|
||||
true, true};
|
||||
SwitchableSetting<float, true> crt_gamma{
|
||||
linkage,
|
||||
1.0f, // 100 maps to 1.0 (range 1-300, actual 1.0-3.0)
|
||||
1.0f, 3.0f, "crt_gamma", Category::Renderer, Specialization::Scalar, true, true};
|
||||
SwitchableSetting<float, true> crt_bloom{
|
||||
linkage,
|
||||
0.33f, // 33/100 = 0.33 (range 0-100, actual 0.0-1.0)
|
||||
0.0f, 1.0f, "crt_bloom", Category::Renderer, Specialization::Scalar, true, true};
|
||||
SwitchableSetting<int, true> crt_mask_type{
|
||||
linkage,
|
||||
1, // Already correct
|
||||
0, 2, "crt_mask_type", Category::Renderer, Specialization::Scalar,
|
||||
true, true}; // 0=none, 1=aperture, 2=shadow
|
||||
SwitchableSetting<float, true> crt_brightness{
|
||||
linkage,
|
||||
1.0f, // Default brightness (1.0 = no change)
|
||||
0.0f, 2.0f, "crt_brightness", Category::Renderer, Specialization::Scalar, true, true};
|
||||
SwitchableSetting<float, true> crt_alpha{
|
||||
linkage,
|
||||
1.0f, // Default alpha (1.0 = fully opaque)
|
||||
0.0f, 1.0f, "crt_alpha", Category::Renderer, Specialization::Scalar, true, true};
|
||||
|
||||
SwitchableSetting<int, true> lanczos_quality{linkage,
|
||||
3, // Default value
|
||||
@@ -430,37 +405,38 @@ struct Values {
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<FSR2QualityMode, true> fsr2_quality_mode{linkage,
|
||||
FSR2QualityMode::Quality, // Quality by default
|
||||
FSR2QualityMode::Quality, // Min value
|
||||
FSR2QualityMode::UltraPerformance, // Max value
|
||||
"fsr2_quality_mode",
|
||||
Category::Renderer,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
SwitchableSetting<FSR2QualityMode, true> fsr2_quality_mode{
|
||||
linkage,
|
||||
FSR2QualityMode::Quality, // Quality by default
|
||||
FSR2QualityMode::Quality, // Min value
|
||||
FSR2QualityMode::UltraPerformance, // Max value
|
||||
"fsr2_quality_mode",
|
||||
Category::Renderer,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<FrameSkipping, true> frame_skipping{
|
||||
linkage,
|
||||
FrameSkipping::Disabled, // Disabled by default
|
||||
FrameSkipping::Disabled,
|
||||
FrameSkipping::Enabled,
|
||||
"frame_skipping",
|
||||
Category::Renderer,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
|
||||
SwitchableSetting<FrameSkipping, true> frame_skipping{linkage,
|
||||
FrameSkipping::Disabled, // Disabled by default
|
||||
FrameSkipping::Disabled,
|
||||
FrameSkipping::Enabled,
|
||||
"frame_skipping",
|
||||
Category::Renderer,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<FrameSkippingMode, true> frame_skipping_mode{linkage,
|
||||
FrameSkippingMode::Adaptive, // Adaptive by default
|
||||
FrameSkippingMode::Adaptive,
|
||||
FrameSkippingMode::Fixed,
|
||||
"frame_skipping_mode",
|
||||
Category::Renderer,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
SwitchableSetting<FrameSkippingMode, true> frame_skipping_mode{
|
||||
linkage,
|
||||
FrameSkippingMode::Adaptive, // Adaptive by default
|
||||
FrameSkippingMode::Adaptive,
|
||||
FrameSkippingMode::Fixed,
|
||||
"frame_skipping_mode",
|
||||
Category::Renderer,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<u8, false> bg_red{
|
||||
linkage, 0, "bg_red", Category::Renderer, Specialization::Default, true, true};
|
||||
@@ -520,55 +496,54 @@ struct Values {
|
||||
|
||||
// GC aggressiveness level for texture/buffer cache eviction
|
||||
SwitchableSetting<GCAggressiveness, true> gc_aggressiveness{linkage,
|
||||
GCAggressiveness::Light,
|
||||
GCAggressiveness::Off,
|
||||
GCAggressiveness::Light,
|
||||
"gc_aggressiveness",
|
||||
Category::RendererAdvanced,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
GCAggressiveness::Light,
|
||||
GCAggressiveness::Off,
|
||||
GCAggressiveness::Light,
|
||||
"gc_aggressiveness",
|
||||
Category::RendererAdvanced,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
// Number of frames before unused textures are evicted (default 2)
|
||||
SwitchableSetting<u32, true> texture_eviction_frames{linkage,
|
||||
2, // default: 2 frames
|
||||
1, // min: 1 frame
|
||||
60, // max: 60 frames (1 second at 60fps)
|
||||
"texture_eviction_frames",
|
||||
Category::RendererAdvanced,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
// Number of frames before unused buffers are evicted (default 5)
|
||||
SwitchableSetting<u32, true> buffer_eviction_frames{linkage,
|
||||
5, // default: 5 frames
|
||||
1, // min: 1 frame
|
||||
120, // max: 120 frames (2 seconds at 60fps)
|
||||
"buffer_eviction_frames",
|
||||
2, // default: 2 frames
|
||||
1, // min: 1 frame
|
||||
60, // max: 60 frames (1 second at 60fps)
|
||||
"texture_eviction_frames",
|
||||
Category::RendererAdvanced,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
// Number of frames before unused buffers are evicted (default 5)
|
||||
SwitchableSetting<u32, true> buffer_eviction_frames{linkage,
|
||||
5, // default: 5 frames
|
||||
1, // min: 1 frame
|
||||
120, // max: 120 frames (2 seconds at 60fps)
|
||||
"buffer_eviction_frames",
|
||||
Category::RendererAdvanced,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
// Enable sparse texture priority eviction (evict large unmapped pages first)
|
||||
SwitchableSetting<bool> sparse_texture_priority_eviction{linkage, false,
|
||||
"sparse_texture_priority_eviction",
|
||||
Category::RendererAdvanced};
|
||||
SwitchableSetting<bool> sparse_texture_priority_eviction{
|
||||
linkage, false, "sparse_texture_priority_eviction", Category::RendererAdvanced};
|
||||
|
||||
// Enable VRAM usage logging for debugging
|
||||
SwitchableSetting<bool> log_vram_usage{linkage, false, "log_vram_usage",
|
||||
Category::RendererAdvanced};
|
||||
Category::RendererAdvanced};
|
||||
|
||||
// FIXED: Android Adreno 740 native ASTC eviction
|
||||
// Controls texture cache eviction strategy on Android devices with native ASTC support
|
||||
// Auto = detect based on GPU, Native = use compressed size, Decompress = use decompressed size
|
||||
SwitchableSetting<AndroidAstcMode, true> android_astc_mode{linkage,
|
||||
AndroidAstcMode::Auto,
|
||||
AndroidAstcMode::Auto,
|
||||
AndroidAstcMode::Decompress,
|
||||
"android_astc_mode",
|
||||
Category::RendererAdvanced};
|
||||
AndroidAstcMode::Auto,
|
||||
AndroidAstcMode::Auto,
|
||||
AndroidAstcMode::Decompress,
|
||||
"android_astc_mode",
|
||||
Category::RendererAdvanced};
|
||||
|
||||
SwitchableSetting<bool> async_presentation{linkage,
|
||||
#ifdef ANDROID
|
||||
@@ -605,12 +580,13 @@ struct Values {
|
||||
Category::RendererAdvanced};
|
||||
SwitchableSetting<bool> barrier_feedback_loops{linkage, true, "barrier_feedback_loops",
|
||||
Category::RendererAdvanced};
|
||||
SwitchableSetting<ExtendedDynamicState, true> extended_dynamic_state{linkage,
|
||||
ExtendedDynamicState::EDS3,
|
||||
ExtendedDynamicState::Disabled,
|
||||
ExtendedDynamicState::EDS3,
|
||||
"extended_dynamic_state",
|
||||
Category::RendererAdvanced};
|
||||
SwitchableSetting<ExtendedDynamicState, true> extended_dynamic_state{
|
||||
linkage,
|
||||
ExtendedDynamicState::EDS3,
|
||||
ExtendedDynamicState::Disabled,
|
||||
ExtendedDynamicState::EDS3,
|
||||
"extended_dynamic_state",
|
||||
Category::RendererAdvanced};
|
||||
SwitchableSetting<bool> use_conditional_rendering{linkage, true, "use_conditional_rendering",
|
||||
Category::RendererAdvanced};
|
||||
|
||||
@@ -676,7 +652,9 @@ struct Values {
|
||||
true};
|
||||
|
||||
// Linux
|
||||
Setting<bool, false> is_wayland_platform{linkage, false, "is_wayland_platform", Category::Miscellaneous, Specialization::Default, false};
|
||||
Setting<bool, false> is_wayland_platform{
|
||||
linkage, false, "is_wayland_platform", Category::Miscellaneous, Specialization::Default,
|
||||
false};
|
||||
SwitchableSetting<bool> enable_gamemode{linkage, true, "enable_gamemode", Category::Linux};
|
||||
|
||||
// Controls
|
||||
@@ -805,11 +783,12 @@ struct Values {
|
||||
Setting<std::string> web_api_url{linkage, "api.ynet-fun.xyz", "web_api_url",
|
||||
Category::WebService};
|
||||
Setting<std::string> citron_username{linkage, std::string(), "citron_username",
|
||||
Category::WebService};
|
||||
Category::WebService};
|
||||
Setting<std::string> citron_token{linkage, std::string(), "citron_token", Category::WebService};
|
||||
|
||||
// Updater
|
||||
Setting<bool> enable_auto_update_check{linkage, true, "enable_auto_update_check", Category::WebService};
|
||||
Setting<bool> enable_auto_update_check{linkage, true, "enable_auto_update_check",
|
||||
Category::WebService};
|
||||
|
||||
// Add-Ons
|
||||
std::map<u64, std::vector<std::string>> disabled_addons;
|
||||
@@ -823,9 +802,12 @@ struct Values {
|
||||
// This stores the external path used for Intelligent Mirroring sync
|
||||
std::map<u64, std::string> mirrored_save_paths;
|
||||
|
||||
Setting<bool> global_custom_save_path_enabled{linkage, false, "global_custom_save_path_enabled", Category::DataStorage};
|
||||
Setting<std::string> global_custom_save_path{linkage, std::string(), "global_custom_save_path", Category::DataStorage};
|
||||
Setting<bool> backup_saves_to_nand{linkage, false, "backup_saves_to_nand", Category::DataStorage};
|
||||
Setting<bool> global_custom_save_path_enabled{linkage, false, "global_custom_save_path_enabled",
|
||||
Category::DataStorage};
|
||||
Setting<std::string> global_custom_save_path{linkage, std::string(), "global_custom_save_path",
|
||||
Category::DataStorage};
|
||||
Setting<bool> backup_saves_to_nand{linkage, false, "backup_saves_to_nand",
|
||||
Category::DataStorage};
|
||||
};
|
||||
|
||||
extern Values values;
|
||||
|
||||
@@ -91,8 +91,8 @@ template <>
|
||||
inline std::vector<std::pair<std::string, AudioEngine>>
|
||||
EnumMetadata<AudioEngine>::Canonicalizations() {
|
||||
return {
|
||||
{"auto", AudioEngine::Auto}, {"cubeb", AudioEngine::Cubeb}, {"sdl2", AudioEngine::Sdl2},
|
||||
{"openal", AudioEngine::OpenAL}, {"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe},
|
||||
{"auto", AudioEngine::Auto}, {"cubeb", AudioEngine::Cubeb}, {"sdl2", AudioEngine::Sdl2},
|
||||
{"openal", AudioEngine::OpenAL}, {"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -110,8 +110,7 @@ enum class AudioMode : u32 {
|
||||
};
|
||||
|
||||
template <>
|
||||
inline std::vector<std::pair<std::string, AudioMode>>
|
||||
EnumMetadata<AudioMode>::Canonicalizations() {
|
||||
inline std::vector<std::pair<std::string, AudioMode>> EnumMetadata<AudioMode>::Canonicalizations() {
|
||||
return {
|
||||
{"Mono", AudioMode::Mono},
|
||||
{"Stereo", AudioMode::Stereo},
|
||||
@@ -146,8 +145,7 @@ enum class Language : u32 {
|
||||
};
|
||||
|
||||
template <>
|
||||
inline std::vector<std::pair<std::string, Language>>
|
||||
EnumMetadata<Language>::Canonicalizations() {
|
||||
inline std::vector<std::pair<std::string, Language>> EnumMetadata<Language>::Canonicalizations() {
|
||||
return {
|
||||
{"Japanese", Language::Japanese},
|
||||
{"EnglishAmerican", Language::EnglishAmerican},
|
||||
@@ -186,15 +184,10 @@ enum class Region : u32 {
|
||||
};
|
||||
|
||||
template <>
|
||||
inline std::vector<std::pair<std::string, Region>>
|
||||
EnumMetadata<Region>::Canonicalizations() {
|
||||
inline std::vector<std::pair<std::string, Region>> EnumMetadata<Region>::Canonicalizations() {
|
||||
return {
|
||||
{"Japan", Region::Japan},
|
||||
{"Usa", Region::Usa},
|
||||
{"Europe", Region::Europe},
|
||||
{"Australia", Region::Australia},
|
||||
{"China", Region::China},
|
||||
{"Korea", Region::Korea},
|
||||
{"Japan", Region::Japan}, {"Usa", Region::Usa}, {"Europe", Region::Europe},
|
||||
{"Australia", Region::Australia}, {"China", Region::China}, {"Korea", Region::Korea},
|
||||
{"Taiwan", Region::Taiwan},
|
||||
};
|
||||
}
|
||||
@@ -254,8 +247,7 @@ enum class TimeZone : u32 {
|
||||
};
|
||||
|
||||
template <>
|
||||
inline std::vector<std::pair<std::string, TimeZone>>
|
||||
EnumMetadata<TimeZone>::Canonicalizations() {
|
||||
inline std::vector<std::pair<std::string, TimeZone>> EnumMetadata<TimeZone>::Canonicalizations() {
|
||||
return {
|
||||
{"Auto", TimeZone::Auto},
|
||||
{"Default", TimeZone::Default},
|
||||
@@ -388,8 +380,7 @@ enum class VSyncMode : u32 {
|
||||
};
|
||||
|
||||
template <>
|
||||
inline std::vector<std::pair<std::string, VSyncMode>>
|
||||
EnumMetadata<VSyncMode>::Canonicalizations() {
|
||||
inline std::vector<std::pair<std::string, VSyncMode>> EnumMetadata<VSyncMode>::Canonicalizations() {
|
||||
return {
|
||||
{"Immediate", VSyncMode::Immediate},
|
||||
{"Mailbox", VSyncMode::Mailbox},
|
||||
@@ -543,12 +534,9 @@ template <>
|
||||
inline std::vector<std::pair<std::string, MemoryLayout>>
|
||||
EnumMetadata<MemoryLayout>::Canonicalizations() {
|
||||
return {
|
||||
{"Memory_4Gb", MemoryLayout::Memory_4Gb},
|
||||
{"Memory_6Gb", MemoryLayout::Memory_6Gb},
|
||||
{"Memory_8Gb", MemoryLayout::Memory_8Gb},
|
||||
{"Memory_10Gb", MemoryLayout::Memory_10Gb},
|
||||
{"Memory_12Gb", MemoryLayout::Memory_12Gb},
|
||||
{"Memory_14Gb", MemoryLayout::Memory_14Gb},
|
||||
{"Memory_4Gb", MemoryLayout::Memory_4Gb}, {"Memory_6Gb", MemoryLayout::Memory_6Gb},
|
||||
{"Memory_8Gb", MemoryLayout::Memory_8Gb}, {"Memory_10Gb", MemoryLayout::Memory_10Gb},
|
||||
{"Memory_12Gb", MemoryLayout::Memory_12Gb}, {"Memory_14Gb", MemoryLayout::Memory_14Gb},
|
||||
{"Memory_16Gb", MemoryLayout::Memory_16Gb},
|
||||
};
|
||||
}
|
||||
@@ -625,9 +613,9 @@ enum class ResolutionSetup : s32 {
|
||||
Res1_2X = 0,
|
||||
Res3_4X = 1,
|
||||
Res1X = 2,
|
||||
Res5_4X = 11, // 1.25X
|
||||
Res5_4X = 11, // 1.25X
|
||||
Res3_2X = 3,
|
||||
Res7_4X = 12, // 1.75X
|
||||
Res7_4X = 12, // 1.75X
|
||||
Res2X = 4,
|
||||
Res3X = 5,
|
||||
Res4X = 6,
|
||||
@@ -641,20 +629,13 @@ template <>
|
||||
inline std::vector<std::pair<std::string, ResolutionSetup>>
|
||||
EnumMetadata<ResolutionSetup>::Canonicalizations() {
|
||||
return {
|
||||
{"Res1_4X", ResolutionSetup::Res1_4X},
|
||||
{"Res1_2X", ResolutionSetup::Res1_2X},
|
||||
{"Res3_4X", ResolutionSetup::Res3_4X},
|
||||
{"Res1X", ResolutionSetup::Res1X},
|
||||
{"Res5_4X", ResolutionSetup::Res5_4X},
|
||||
{"Res3_2X", ResolutionSetup::Res3_2X},
|
||||
{"Res7_4X", ResolutionSetup::Res7_4X},
|
||||
{"Res2X", ResolutionSetup::Res2X},
|
||||
{"Res3X", ResolutionSetup::Res3X},
|
||||
{"Res4X", ResolutionSetup::Res4X},
|
||||
{"Res5X", ResolutionSetup::Res5X},
|
||||
{"Res6X", ResolutionSetup::Res6X},
|
||||
{"Res7X", ResolutionSetup::Res7X},
|
||||
{"Res8X", ResolutionSetup::Res8X},
|
||||
{"Res1_4X", ResolutionSetup::Res1_4X}, {"Res1_2X", ResolutionSetup::Res1_2X},
|
||||
{"Res3_4X", ResolutionSetup::Res3_4X}, {"Res1X", ResolutionSetup::Res1X},
|
||||
{"Res5_4X", ResolutionSetup::Res5_4X}, {"Res3_2X", ResolutionSetup::Res3_2X},
|
||||
{"Res7_4X", ResolutionSetup::Res7_4X}, {"Res2X", ResolutionSetup::Res2X},
|
||||
{"Res3X", ResolutionSetup::Res3X}, {"Res4X", ResolutionSetup::Res4X},
|
||||
{"Res5X", ResolutionSetup::Res5X}, {"Res6X", ResolutionSetup::Res6X},
|
||||
{"Res7X", ResolutionSetup::Res7X}, {"Res8X", ResolutionSetup::Res8X},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -675,7 +656,8 @@ enum class ScalingFilter : u32 {
|
||||
Fsr2 = 8,
|
||||
CRTEasyMode = 9,
|
||||
CRTRoyale = 10,
|
||||
MaxEnum = 11,
|
||||
Cas = 11,
|
||||
MaxEnum = 12,
|
||||
};
|
||||
|
||||
template <>
|
||||
@@ -693,6 +675,7 @@ EnumMetadata<ScalingFilter>::Canonicalizations() {
|
||||
{"Fsr2", ScalingFilter::Fsr2},
|
||||
{"CRTEasyMode", ScalingFilter::CRTEasyMode},
|
||||
{"CRTRoyale", ScalingFilter::CRTRoyale},
|
||||
{"Cas", ScalingFilter::Cas},
|
||||
{"MaxEnum", ScalingFilter::MaxEnum},
|
||||
};
|
||||
}
|
||||
@@ -714,10 +697,8 @@ template <>
|
||||
inline std::vector<std::pair<std::string, AntiAliasing>>
|
||||
EnumMetadata<AntiAliasing>::Canonicalizations() {
|
||||
return {
|
||||
{"None", AntiAliasing::None},
|
||||
{"Fxaa", AntiAliasing::Fxaa},
|
||||
{"Smaa", AntiAliasing::Smaa},
|
||||
{"Taa", AntiAliasing::Taa},
|
||||
{"None", AntiAliasing::None}, {"Fxaa", AntiAliasing::Fxaa},
|
||||
{"Smaa", AntiAliasing::Smaa}, {"Taa", AntiAliasing::Taa},
|
||||
{"MaxEnum", AntiAliasing::MaxEnum},
|
||||
};
|
||||
}
|
||||
@@ -805,12 +786,9 @@ template <>
|
||||
inline std::vector<std::pair<std::string, AspectRatio>>
|
||||
EnumMetadata<AspectRatio>::Canonicalizations() {
|
||||
return {
|
||||
{"R16_9", AspectRatio::R16_9},
|
||||
{"R4_3", AspectRatio::R4_3},
|
||||
{"R21_9", AspectRatio::R21_9},
|
||||
{"R16_10", AspectRatio::R16_10},
|
||||
{"R32_9", AspectRatio::R32_9},
|
||||
{"Stretch", AspectRatio::Stretch},
|
||||
{"R16_9", AspectRatio::R16_9}, {"R4_3", AspectRatio::R4_3},
|
||||
{"R21_9", AspectRatio::R21_9}, {"R16_10", AspectRatio::R16_10},
|
||||
{"R32_9", AspectRatio::R32_9}, {"Stretch", AspectRatio::Stretch},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -882,8 +860,8 @@ inline u32 EnumMetadata<ExtendedDynamicState>::Index() {
|
||||
|
||||
// FIXED: VRAM leak prevention - GC aggressiveness levels
|
||||
enum class GCAggressiveness : u32 {
|
||||
Off = 0, // Disable automatic GC (not recommended)
|
||||
Light = 1, // Light GC - gentle eviction of old textures/buffers
|
||||
Off = 0, // Disable automatic GC (not recommended)
|
||||
Light = 1, // Light GC - gentle eviction of old textures/buffers
|
||||
};
|
||||
|
||||
template <>
|
||||
|
||||
@@ -95,6 +95,7 @@ void BlitScreen::CreateWindowAdapt() {
|
||||
break;
|
||||
case Settings::ScalingFilter::Fsr:
|
||||
case Settings::ScalingFilter::Fsr2:
|
||||
case Settings::ScalingFilter::Cas:
|
||||
case Settings::ScalingFilter::Bilinear:
|
||||
default:
|
||||
window_adapt = MakeBilinear(device);
|
||||
|
||||
@@ -49,11 +49,12 @@ FSR::FSR(u32 output_width_, u32 output_height_) : width(output_width_), height(o
|
||||
FSR::~FSR() = default;
|
||||
|
||||
GLuint FSR::Draw(ProgramManager& program_manager, GLuint texture, u32 input_image_width,
|
||||
u32 input_image_height, const Common::Rectangle<f32>& crop_rect) {
|
||||
const f32 input_width = static_cast<f32>(input_image_width);
|
||||
const f32 input_height = static_cast<f32>(input_image_height);
|
||||
const f32 output_width = static_cast<f32>(width);
|
||||
const f32 output_height = static_cast<f32>(height);
|
||||
u32 input_image_height, const Common::Rectangle<float>& crop_rect,
|
||||
float sharpening) {
|
||||
const float input_width = static_cast<float>(input_image_width);
|
||||
const float input_height = static_cast<float>(input_image_height);
|
||||
const float output_width = static_cast<float>(width);
|
||||
const float output_height = static_cast<float>(height);
|
||||
const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_width;
|
||||
const f32 viewport_x = crop_rect.left * input_width;
|
||||
const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_height;
|
||||
@@ -66,9 +67,6 @@ GLuint FSR::Draw(ProgramManager& program_manager, GLuint texture, u32 input_imag
|
||||
easu_con.data() + 12, viewport_width, viewport_height, input_width,
|
||||
input_height, output_width, output_height, viewport_x, viewport_y);
|
||||
|
||||
const float sharpening =
|
||||
static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
|
||||
|
||||
FsrRcasCon(rcas_con.data(), sharpening);
|
||||
|
||||
glProgramUniform4uiv(easu_frag.handle, 0, sizeof(easu_con), easu_con.data());
|
||||
|
||||
@@ -20,7 +20,8 @@ public:
|
||||
~FSR();
|
||||
|
||||
GLuint Draw(ProgramManager& program_manager, GLuint texture, u32 input_image_width,
|
||||
u32 input_image_height, const Common::Rectangle<f32>& crop_rect);
|
||||
u32 input_image_height, const Common::Rectangle<float>& crop_rect,
|
||||
float sharpening);
|
||||
|
||||
bool NeedsRecreation(const Common::Rectangle<u32>& screen);
|
||||
|
||||
|
||||
@@ -65,8 +65,8 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
|
||||
break;
|
||||
case Settings::AntiAliasing::Taa:
|
||||
CreateTAA();
|
||||
texture = taa->Draw(program_manager, info.display_texture,
|
||||
GL_NONE, GL_NONE, GL_NONE, 0); // TODO: Add proper motion vectors
|
||||
texture = taa->Draw(program_manager, info.display_texture, GL_NONE, GL_NONE, GL_NONE,
|
||||
0); // TODO: Add proper motion vectors
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -75,12 +75,23 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
|
||||
|
||||
glDisablei(GL_SCISSOR_TEST, 0);
|
||||
|
||||
if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr) {
|
||||
if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr ||
|
||||
filters.get_scaling_filter() == Settings::ScalingFilter::Cas) {
|
||||
if (!fsr || fsr->NeedsRecreation(layout.screen)) {
|
||||
fsr = std::make_unique<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight());
|
||||
}
|
||||
|
||||
texture = fsr->Draw(program_manager, texture, info.scaled_width, info.scaled_height, crop);
|
||||
float sharpening = 0.0f;
|
||||
if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr) {
|
||||
sharpening =
|
||||
static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
|
||||
} else if (filters.get_scaling_filter() == Settings::ScalingFilter::Cas) {
|
||||
sharpening =
|
||||
static_cast<float>(Settings::values.cas_sharpening_slider.GetValue()) / 100.0f;
|
||||
}
|
||||
|
||||
texture = fsr->Draw(program_manager, texture, info.scaled_width, info.scaled_height, crop,
|
||||
sharpening);
|
||||
crop = {0, 0, 1, 1};
|
||||
}
|
||||
if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr2) {
|
||||
|
||||
@@ -5,14 +5,16 @@
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "video_core/host_shaders/present_bicubic_frag_spv.h"
|
||||
#include "video_core/host_shaders/present_lanczos_frag_spv.h"
|
||||
#include "video_core/host_shaders/present_gaussian_frag_spv.h"
|
||||
#include "video_core/host_shaders/present_lanczos_frag_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_crt_easymode_frag_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_frag_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32_frag_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_present_frag_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_present_scaleforce_fp16_frag_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_present_scalefx_fp16_frag_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_present_scalefx_fp32_frag_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_crt_easymode_frag_spv.h"
|
||||
#include "video_core/renderer_vulkan/present/filters.h"
|
||||
#include "video_core/renderer_vulkan/present/util.h"
|
||||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||
@@ -72,7 +74,8 @@ std::unique_ptr<WindowAdaptPass> MakeScaleFx(const Device& device, VkFormat fram
|
||||
}
|
||||
|
||||
std::unique_ptr<WindowAdaptPass> MakeLanczos(const Device& device, VkFormat frame_format) {
|
||||
return std::make_unique<WindowAdaptPass>(device, frame_format, CreateNearestNeighborSampler(device),
|
||||
return std::make_unique<WindowAdaptPass>(device, frame_format,
|
||||
CreateNearestNeighborSampler(device),
|
||||
BuildShader(device, PRESENT_LANCZOS_FRAG_SPV));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ using PushConstants = std::array<u32, 4 * 4>;
|
||||
|
||||
FSR::FSR(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} {
|
||||
: m_device{device}, m_memory_allocator{memory_allocator}, m_image_count{image_count},
|
||||
m_extent{extent} {
|
||||
|
||||
CreateImages();
|
||||
CreateRenderPasses();
|
||||
@@ -157,7 +157,7 @@ void FSR::UploadImages(Scheduler& scheduler) {
|
||||
|
||||
VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImage source_image,
|
||||
VkImageView source_image_view, VkExtent2D input_image_extent,
|
||||
const Common::Rectangle<f32>& crop_rect) {
|
||||
const Common::Rectangle<f32>& crop_rect, float sharpening) {
|
||||
Images& images = m_dynamic_images[image_index];
|
||||
|
||||
VkImage easu_image = *images.images[Easu];
|
||||
@@ -188,8 +188,6 @@ VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImage source_i
|
||||
input_image_height, output_image_width, output_image_height, viewport_x,
|
||||
viewport_y);
|
||||
|
||||
const float sharpening =
|
||||
static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
|
||||
FsrRcasCon(rcas_con.data(), sharpening);
|
||||
|
||||
UploadImages(scheduler);
|
||||
|
||||
@@ -18,7 +18,7 @@ public:
|
||||
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);
|
||||
const Common::Rectangle<f32>& crop_rect, float sharpening);
|
||||
|
||||
private:
|
||||
void CreateImages();
|
||||
|
||||
@@ -57,7 +57,8 @@ Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Schedule
|
||||
device_memory(device_memory_), filters(filters_), image_count(image_count_) {
|
||||
CreateDescriptorPool();
|
||||
CreateDescriptorSets(layout);
|
||||
if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr) {
|
||||
if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr ||
|
||||
filters.get_scaling_filter() == Settings::ScalingFilter::Cas) {
|
||||
CreateFSR(output_size);
|
||||
}
|
||||
if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr2) {
|
||||
@@ -109,8 +110,16 @@ void Layer::ConfigureDraw(PresentPushConstants* out_push_constants,
|
||||
};
|
||||
|
||||
if (fsr) {
|
||||
float sharpening = 0.0f;
|
||||
if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr) {
|
||||
sharpening =
|
||||
static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
|
||||
} else if (filters.get_scaling_filter() == Settings::ScalingFilter::Cas) {
|
||||
sharpening =
|
||||
static_cast<float>(Settings::values.cas_sharpening_slider.GetValue()) / 100.0f;
|
||||
}
|
||||
source_image_view = fsr->Draw(scheduler, image_index, source_image, source_image_view,
|
||||
render_extent, crop_rect);
|
||||
render_extent, crop_rect, sharpening);
|
||||
crop_rect = {0, 0, 1, 1};
|
||||
}
|
||||
if (fsr2) {
|
||||
@@ -257,7 +266,8 @@ void Layer::UpdateDescriptorSet(VkImageView image_view, VkSampler sampler, size_
|
||||
const VkDescriptorImageInfo image_info{
|
||||
.sampler = sampler,
|
||||
.imageView = image_view,
|
||||
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, // Correct layout for texture sampling
|
||||
.imageLayout =
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, // Correct layout for texture sampling
|
||||
};
|
||||
|
||||
const VkWriteDescriptorSet sampler_write{
|
||||
@@ -294,7 +304,7 @@ void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t i
|
||||
Tegra::Texture::UnswizzleTexture(
|
||||
mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size),
|
||||
bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
|
||||
buffer.Flush(); // Ensure host writes are visible before the GPU copy.
|
||||
buffer.Flush(); // Ensure host writes are visible before the GPU copy.
|
||||
}
|
||||
|
||||
const VkBufferImageCopy copy{
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
#include "video_core/framebuffer_config.h"
|
||||
#include "video_core/fsr.h"
|
||||
#include "video_core/host_shaders/vulkan_present_vert_spv.h"
|
||||
#include "video_core/renderer_vulkan/present/layer.h"
|
||||
#include "video_core/renderer_vulkan/present/present_push_constants.h"
|
||||
@@ -13,7 +15,6 @@
|
||||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||
#include "video_core/vulkan_common/vulkan_device.h"
|
||||
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
@@ -93,8 +94,9 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s
|
||||
cmdbuf.ClearAttachments({clear_attachment}, {clear_rect});
|
||||
|
||||
const auto current_scaling_filter = Settings::values.scaling_filter.GetValue();
|
||||
const bool is_crt_enabled = current_scaling_filter == Settings::ScalingFilter::CRTEasyMode ||
|
||||
current_scaling_filter == Settings::ScalingFilter::CRTRoyale;
|
||||
const bool is_crt_enabled =
|
||||
current_scaling_filter == Settings::ScalingFilter::CRTEasyMode ||
|
||||
current_scaling_filter == Settings::ScalingFilter::CRTRoyale;
|
||||
|
||||
for (size_t i = 0; i < layer_count; i++) {
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipelines[i]);
|
||||
@@ -139,6 +141,7 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s
|
||||
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0,
|
||||
descriptor_sets[i], {});
|
||||
|
||||
cmdbuf.Draw(4, 1, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ void BlitScreen::SetWindowAdaptPass() {
|
||||
case Settings::ScalingFilter::CRTRoyale:
|
||||
window_adapt = MakeCRT(device, swapchain_view_format);
|
||||
break;
|
||||
case Settings::ScalingFilter::Cas:
|
||||
case Settings::ScalingFilter::Fsr:
|
||||
case Settings::ScalingFilter::Fsr2:
|
||||
case Settings::ScalingFilter::Bilinear:
|
||||
|
||||
Reference in New Issue
Block a user