[Config/UI] Consolidate render_target_path* cvars

Change from fbo/rtv and fsi/rov to performance/accuracy and rework
cvar aliasing to accommodate more entries
This commit is contained in:
Herman S.
2025-11-04 14:39:17 +09:00
parent 06ddb74cd1
commit 6b4aaabf04
7 changed files with 120 additions and 75 deletions
+35
View File
@@ -17,6 +17,7 @@
#include "xenia/base/string.h"
#include "xenia/base/string_buffer.h"
#include "xenia/base/system.h"
#include "xenia/ui/config_helpers.h"
toml::parse_result ParseFile(const std::filesystem::path& filename) {
return toml::parse_file(xe::path_to_utf8(filename));
@@ -88,6 +89,38 @@ void PrintConfigToLog(const std::filesystem::path& file_path) {
file.close();
}
void MigrateLegacyCvars(const toml::table& config) {
if (!cvar::ConfigVars) {
return;
}
for (const auto& [category_name, category_table] : config) {
if (!category_table.is_table()) continue;
for (const auto& [key, value] : *category_table.as_table()) {
std::string var_name = std::string(key);
std::string var_value = value.value_or("");
if (var_value.length() >= 2 && var_value.front() == '"' &&
var_value.back() == '"') {
var_value = var_value.substr(1, var_value.length() - 2);
}
for (const auto& alias : xe::ui::GetCvarAliases()) {
if (var_name == alias.old_name && var_value == alias.old_value) {
auto new_cvar = (*cvar::ConfigVars).find(alias.new_name);
if (new_cvar != (*cvar::ConfigVars).end()) {
auto config_var = static_cast<cvar::IConfigVar*>(new_cvar->second);
toml::value new_value(alias.new_value);
config_var->LoadConfigValue(&new_value);
}
break;
}
}
}
}
}
void ReadConfig(const std::filesystem::path& file_path,
bool update_if_no_version_stored) {
if (!cvar::ConfigVars) {
@@ -115,6 +148,8 @@ void ReadConfig(const std::filesystem::path& file_path,
config_var->LoadConfigValue(config_key_node.node());
}
}
MigrateLegacyCvars(config);
uint32_t config_defaults_date = defaults_date_cvar->GetTypedConfigValue();
if (update_if_no_version_stored || config_defaults_date) {
cvar::IConfigVarUpdate::ApplyUpdates(config_defaults_date);
@@ -38,33 +38,6 @@ DEFINE_bool(
"working on UHD Graphics 630 as of March 2021 (driver 27.20.0100.8336).",
"GPU");
DEFINE_bool(no_discard_stencil_in_transfer_pipelines, false, "bleh", "GPU");
// TODO(Triang3l): Make ROV the default when it's optimized better (for
// instance, using static shader modifications to pass render target
// parameters).
DEFINE_string(
render_target_path_d3d12, "",
"Render target emulation path to use on Direct3D 12.\n"
"Use: [any, rtv, rov]\n"
" rtv:\n"
" Host render targets and fixed-function blending and depth / stencil "
"testing, copying between render targets when needed.\n"
" Lower accuracy (limited pixel format support).\n"
" Performance limited primarily by render target layout changes requiring "
"copying, but generally higher.\n"
" rov:\n"
" Manual pixel packing, blending and depth / stencil testing, with free "
"render target layout changes.\n"
" Requires a GPU supporting rasterizer-ordered views.\n"
" Highest accuracy (all pixel formats handled in software).\n"
" Performance limited primarily by overdraw.\n"
" On AMD drivers, currently causes shader compiler crashes in many "
"cases.\n"
" Any other value:\n"
" Choose what is considered the most optimal for the system (currently "
"always RTV because the ROV path is much slower now, except for Intel "
"GPUs, which have a bug in stencil testing that causes Xbox 360 Direct3D 9 "
"clears not to work).",
"GPU");
namespace xe {
namespace gpu {
@@ -214,9 +187,9 @@ bool D3D12RenderTargetCache::Initialize() {
command_processor_.GetD3D12Provider();
ID3D12Device* device = provider.GetDevice();
if (cvars::render_target_path_d3d12 == "rtv") {
if (cvars::render_target_path == "performance") {
path_ = Path::kHostRenderTargets;
} else if (cvars::render_target_path_d3d12 == "rov") {
} else if (cvars::render_target_path == "accuracy") {
path_ = Path::kPixelShaderInterlock;
} else {
// As of April 2021 (driver version 27.20.0100.9316), on Intel (tested on
+24
View File
@@ -66,3 +66,27 @@ DEFINE_int32(
"Set to higher number than query_occlusion_sample_lower_threshold. This "
"value is ignored if query_occlusion_sample_lower_threshold is set to -1.",
"GPU");
// TODO(Triang3l): Make accuracy (ROV/FSI) the default when it's optimized
// better (for instance, using static shader modifications to pass render
// target parameters).
DEFINE_string(
render_target_path, "performance",
"Render target emulation path to use across all GPU backends.\n"
"Use: [performance, accuracy]\n"
" performance:\n"
" Host render targets and fixed-function blending and depth/stencil "
"testing, copying between render targets when needed.\n"
" Lower accuracy (limited pixel format support).\n"
" Performance limited primarily by render target layout changes requiring "
"copying, but generally higher.\n"
" Maps to 'fbo' on Vulkan and 'rtv' on D3D12.\n"
" accuracy:\n"
" Manual pixel packing, blending and depth/stencil testing, with free "
"render target layout changes.\n"
" Requires GPU supporting fragment shader interlock (Vulkan) or "
"rasterizer-ordered views (D3D12).\n"
" Highest accuracy (all pixel formats handled in software).\n"
" Performance limited primarily by overdraw.\n"
" Maps to 'fsi' on Vulkan and 'rov' on D3D12.",
"GPU");
+2
View File
@@ -32,6 +32,8 @@ DECLARE_int32(query_occlusion_sample_upper_threshold);
DECLARE_bool(disassemble_pm4);
DECLARE_string(render_target_path);
#define XE_GPU_FINE_GRAINED_DRAW_SCOPES 1
#endif // XENIA_GPU_GPU_FLAGS_H_
@@ -19,6 +19,7 @@
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/gpu/draw_util.h"
#include "xenia/gpu/gpu_flags.h"
#include "xenia/gpu/registers.h"
#include "xenia/gpu/spirv_builder.h"
#include "xenia/gpu/spirv_compatibility.h"
@@ -29,27 +30,6 @@
#include "xenia/gpu/xenos.h"
#include "xenia/ui/vulkan/vulkan_util.h"
DEFINE_string(
render_target_path_vulkan, "",
"Render target emulation path to use on Vulkan.\n"
"Use: [any, fbo, fsi]\n"
" fbo:\n"
" Host framebuffers and fixed-function blending and depth / stencil "
"testing, copying between render targets when needed.\n"
" Lower accuracy (limited pixel format support).\n"
" Performance limited primarily by render target layout changes requiring "
"copying, but generally higher.\n"
" fsi:\n"
" Manual pixel packing, blending and depth / stencil testing, with free "
"render target layout changes.\n"
" Requires a GPU supporting fragment shader interlock.\n"
" Highest accuracy (all pixel formats handled in software).\n"
" Performance limited primarily by overdraw.\n"
" Any other value:\n"
" Choose what is considered the most optimal for the system (currently "
"always FB because the FSI path is much slower now).",
"GPU");
namespace xe {
namespace gpu {
namespace vulkan {
@@ -216,7 +196,7 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) {
const ui::vulkan::VulkanDevice::Properties& device_properties =
vulkan_device->properties();
if (cvars::render_target_path_vulkan == "fsi") {
if (cvars::render_target_path == "accuracy") {
path_ = Path::kPixelShaderInterlock;
} else {
path_ = Path::kHostRenderTargets;
+22 -2
View File
@@ -19,6 +19,27 @@
namespace xe {
namespace ui {
struct CvarAlias {
std::string old_name;
std::string old_value;
std::string new_name;
std::string new_value;
};
inline const std::vector<CvarAlias>& GetCvarAliases() {
static const std::vector<CvarAlias> aliases = {
{"use_new_decoder", "true", "xma_decoder", "new"},
{"use_old_decoder", "true", "xma_decoder", "old"},
{"readback_resolve", "true", "readback_resolve", "fast"},
{"readback_resolve", "false", "readback_resolve", "fast"},
{"render_target_path_d3d12", "rtv", "render_target_path", "performance"},
{"render_target_path_d3d12", "rov", "render_target_path", "accuracy"},
{"render_target_path_vulkan", "fbo", "render_target_path", "performance"},
{"render_target_path_vulkan", "fsi", "render_target_path", "accuracy"},
};
return aliases;
}
// Known enum-like cvars with their valid options
inline const std::map<std::string, std::vector<std::string>>&
GetKnownEnumOptions() {
@@ -39,8 +60,7 @@ GetKnownEnumOptions() {
{"d3d12_readback_resolve",
{"kCopy", "kComputeLuminance", "kComputeRGBA16"}},
{"readback_resolve", {"fast", "full", "none"}},
{"render_target_path_d3d12", {"any", "rtv", "rov"}},
{"render_target_path_vulkan", {"any", "fbo", "fsi"}},
{"render_target_path", {"performance", "accuracy"}},
{"postprocess_antialiasing", {"off", "fxaa", "fxaa_extreme"}},
{"postprocess_scaling_and_sharpening",
{"bilinear", "cas", "fsr", "nearest"}},
+33 -22
View File
@@ -63,32 +63,16 @@ std::string JsonValueToString(const rapidjson::Value& value) {
return result;
}
// CanaryCvarCompat: Translate legacy Xenia Canary cvar names/values to current
// ones Returns true if translation occurred, filling out_var_name and out_value
bool CanaryCvarCompat(const std::string& var_name, const std::string& value,
std::string& out_var_name, std::string& out_value) {
// use_new_decoder: true -> xma_decoder: new
if (var_name == "use_new_decoder" && value == "true") {
out_var_name = "xma_decoder";
out_value = "new";
return true;
for (const auto& alias : xe::ui::GetCvarAliases()) {
if (var_name == alias.old_name && value == alias.old_value) {
out_var_name = alias.new_name;
out_value = alias.new_value;
return true;
}
}
// use_old_decoder: true -> xma_decoder: old
if (var_name == "use_old_decoder" && value == "true") {
out_var_name = "xma_decoder";
out_value = "old";
return true;
}
// readback_resolve: true/false -> readback_resolve: fast (default)
if (var_name == "readback_resolve" && (value == "true" || value == "false")) {
out_var_name = "readback_resolve";
out_value = "fast";
return true;
}
// No translation needed
out_var_name = var_name;
out_value = value;
return false;
@@ -255,6 +239,33 @@ void GameConfigDialogQt::LoadConfigOverrides() {
CreateRow(var_name, value);
}
}
// Check for legacy cvar names in the TOML that need migration
for (const auto& [category_name, category_table] : config) {
if (!category_table.is_table()) continue;
for (const auto& [key, value] : *category_table.as_table()) {
std::string var_name = std::string(key);
std::string var_value = value.value_or("");
if (var_value.length() >= 2 && var_value.front() == '"' &&
var_value.back() == '"') {
var_value = var_value.substr(1, var_value.length() - 2);
}
std::string translated_name;
std::string translated_value;
if (CanaryCvarCompat(var_name, var_value, translated_name,
translated_value)) {
if (translated_name != var_name &&
!config_overrides_.count(translated_name)) {
config_overrides_[translated_name] = translated_value;
CreateRow(translated_name, translated_value);
has_unsaved_changes_ = true;
}
}
}
}
} catch (const std::exception& e) {
XELOGE("Failed to load game config {}: {}", xe::path_to_utf8(config_path),
e.what());