diff --git a/src/xenia/config.cc b/src/xenia/config.cc index 80051782c..b5bc10122 100644 --- a/src/xenia/config.cc +++ b/src/xenia/config.cc @@ -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(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); diff --git a/src/xenia/gpu/d3d12/d3d12_render_target_cache.cc b/src/xenia/gpu/d3d12/d3d12_render_target_cache.cc index fb4934924..02f982f96 100644 --- a/src/xenia/gpu/d3d12/d3d12_render_target_cache.cc +++ b/src/xenia/gpu/d3d12/d3d12_render_target_cache.cc @@ -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 diff --git a/src/xenia/gpu/gpu_flags.cc b/src/xenia/gpu/gpu_flags.cc index 311715fad..76ec2ed8e 100644 --- a/src/xenia/gpu/gpu_flags.cc +++ b/src/xenia/gpu/gpu_flags.cc @@ -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"); diff --git a/src/xenia/gpu/gpu_flags.h b/src/xenia/gpu/gpu_flags.h index 77559f3c2..f969a5c32 100644 --- a/src/xenia/gpu/gpu_flags.h +++ b/src/xenia/gpu/gpu_flags.h @@ -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_ diff --git a/src/xenia/gpu/vulkan/vulkan_render_target_cache.cc b/src/xenia/gpu/vulkan/vulkan_render_target_cache.cc index 33618b187..928ad499a 100644 --- a/src/xenia/gpu/vulkan/vulkan_render_target_cache.cc +++ b/src/xenia/gpu/vulkan/vulkan_render_target_cache.cc @@ -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; diff --git a/src/xenia/ui/config_helpers.h b/src/xenia/ui/config_helpers.h index 065973093..0fd9a4943 100644 --- a/src/xenia/ui/config_helpers.h +++ b/src/xenia/ui/config_helpers.h @@ -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& GetCvarAliases() { + static const std::vector 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>& 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"}}, diff --git a/src/xenia/ui/game_config_dialog_qt.cc b/src/xenia/ui/game_config_dialog_qt.cc index e746bf115..ba4968c47 100644 --- a/src/xenia/ui/game_config_dialog_qt.cc +++ b/src/xenia/ui/game_config_dialog_qt.cc @@ -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());