mirror of
https://github.com/sal063/AC6_recomp
synced 2026-08-28 09:18:28 -04:00
Fix cvar preset timing and settings-save leaks; tidy the cvar surface
Config presets now run after the toml loads: the ac6_fix_* switches and their scale gates finally honor user settings. The settings save persists only user-set values, with toml escaping - no leaked presets, and a saved Windows path can no longer void the whole config. Runtime-forced values (performance mode) can no longer overwrite the user's saved choices. Menu: wrapped tooltips, "(set by <cvar>)" on driven entries, a developer toggle; AC6-added cvars grouped under the AC6 node as AC6/Fixes and AC6/Enhancements, with true restart/live lifecycles.
This commit is contained in:
@@ -36,23 +36,13 @@
|
||||
#include <rex/ppc.h>
|
||||
#include <rex/system/kernel_state.h>
|
||||
|
||||
REXCVAR_DEFINE_BOOL(ac6_cutscene_resync, true, "AC6",
|
||||
"Keep in-engine cutscene video locked to its audio. Audio "
|
||||
"is the master clock and is never altered; after a render "
|
||||
"hitch (or under sustained slow rendering) the cutscene "
|
||||
"timeline catches up by running extra sequencer ticks - a "
|
||||
"bounded frame-skip, like every film player's audio-master "
|
||||
"sync. No effect on normal full-speed playback (catch-up "
|
||||
"engages only past 2 ticks of drift; verified extras=0 in "
|
||||
"steady state, injected-hitch recovery in 3 frames, and "
|
||||
"sustained 14fps @ 5x draw scale staying locked).");
|
||||
REXCVAR_DEFINE_INT32(ac6_cutscene_resync_max_ticks, 3, "AC6",
|
||||
"Max extra cutscene sequencer ticks per rendered frame "
|
||||
"while catching up (ac6_cutscene_resync). Also sets the "
|
||||
"slowest sustained render rate that stays in sync: N "
|
||||
"extras holds sync down to 30/(1+N) fps (3 -> 7.5 fps). "
|
||||
"Low = gentle brief fast-forward after a stall; 0 = "
|
||||
"uncapped, one hard jump-cut.");
|
||||
REXCVAR_DEFINE_BOOL(ac6_cutscene_resync, true, "AC6/Fixes",
|
||||
"Keep cutscene video locked to its audio after render hitches "
|
||||
"by letting the timeline catch up (a bounded frame-skip).");
|
||||
REXCVAR_DEFINE_INT32(ac6_cutscene_resync_max_ticks, 3, "AC6/Fixes",
|
||||
"Max catch-up cutscene sequencer ticks per rendered frame "
|
||||
"during resync. 0 = uncapped (one hard jump-cut).")
|
||||
.debug_only();
|
||||
|
||||
PPC_EXTERN_FUNC(__imp__rex_sub_82184460); // CAce6DemoManager::Exec ("DD")
|
||||
PPC_EXTERN_FUNC(__imp__rex_sub_821856F8); // CX360DemoManagerEM::Exec ("EM")
|
||||
|
||||
@@ -67,13 +67,9 @@
|
||||
#include <rex/ppc.h>
|
||||
|
||||
REXCVAR_DEFINE_BOOL(
|
||||
ac6_effect_mode_fix, false, "AC6",
|
||||
"Fix the one-frame effects/clouds flicker under host CPU pressure. The effect update "
|
||||
"and the effect draw are two async worker jobs sharing one vtable slot, told apart only "
|
||||
"by a plain-store mode word with no per-job copy; when a host stall lets the second "
|
||||
"submit's write land first, both jobs draw and the effect + cloud passes are emitted "
|
||||
"twice. This snapshots the mode at submit and dispatches from the snapshot. Off by "
|
||||
"default.");
|
||||
ac6_effect_mode_fix, false, "AC6/Fixes",
|
||||
"Fix the one-frame effects/clouds flicker under heavy CPU load by "
|
||||
"snapshotting the effect job mode at submit. Off by default.");
|
||||
|
||||
// Dispatch targets, called directly. NOT via PPC_CALL_INDIRECT_FUNC: that macro
|
||||
// degrades to __builtin_debugtrap() unless ppc_config.h was included first,
|
||||
|
||||
@@ -67,14 +67,17 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
REXCVAR_DEFINE_BOOL(ac6_kbm_enabled, false, "AC6/KBM",
|
||||
"Enable keyboard+mouse input (default off; set true in ac6recomp.toml)");
|
||||
REXCVAR_DEFINE_BOOL(ac6_kbm_log, false, "AC6/KBM",
|
||||
"Log KBM diagnostics to the game log (info level)");
|
||||
REXCVAR_DEFINE_STRING(ac6_kbm_config, "ac6_input.toml", "AC6/KBM",
|
||||
"Path to the keyboard+mouse binding config");
|
||||
REXCVAR_DEFINE_BOOL(ac6_kbm_padless, true, "AC6/KBM",
|
||||
"Present a synthetic pad on slot 0 when no controller is connected");
|
||||
REXCVAR_DEFINE_BOOL(ac6_kbm_enabled, false, "AC6/Enhancements",
|
||||
"Enable keyboard and mouse controls.");
|
||||
REXCVAR_DEFINE_BOOL(ac6_kbm_log, false, "AC6/Enhancements",
|
||||
"Log keyboard and mouse input diagnostics.")
|
||||
.debug_only();
|
||||
REXCVAR_DEFINE_STRING(ac6_kbm_config, "ac6_input.toml", "AC6/Enhancements",
|
||||
"Path to the keyboard and mouse bindings file. Edits to the "
|
||||
"file are picked up live.");
|
||||
REXCVAR_DEFINE_BOOL(ac6_kbm_padless, true, "AC6/Enhancements",
|
||||
"Provide a virtual controller when none is connected, so "
|
||||
"keyboard and mouse work on their own.");
|
||||
|
||||
// The SDK's own mnk virtual-pad driver (input/mnk) - forced off while our
|
||||
// KB+M is enabled so two keyboard mappers never fight over the same pad.
|
||||
|
||||
@@ -50,29 +50,12 @@
|
||||
#include "../render_hooks.h"
|
||||
#include "ac6_widescreen.h"
|
||||
|
||||
REXCVAR_DEFINE_BOOL(ac6_widescreen, false, "AC6",
|
||||
"Arbitrary aspect ratio (hor+), in-mission only. The target aspect "
|
||||
"is derived from the actual window size (re-checked continuously; "
|
||||
"a live resize adapts). While the game's mode task is the mission "
|
||||
"(gameplay, in-engine cutscenes, pause), cameras are patched from "
|
||||
"16:9 to the window aspect, the presenter fills the window, and "
|
||||
"the 2D layer is pre-squeezed at draw time for a crisp 16:9 HUD "
|
||||
"(the game-placed target-marker shader stays full-width by design; "
|
||||
"its art is squared by narrowing the geometry). Everywhere else "
|
||||
"(menus, hangar, briefing, FMV, attract) and at 16:9-or-narrower "
|
||||
"windows, cameras stay 16:9 and the presentation letterboxes - "
|
||||
"vanilla. While enabled the feature drives the presenter's "
|
||||
"letterbox decision itself; it never writes the present_letterbox "
|
||||
"cvar, so nothing leaks into a saved config.");
|
||||
REXCVAR_DEFINE_BOOL(ac6_widescreen_cinematics, true, "AC6",
|
||||
"With ac6_widescreen: render in-engine cinematics wide too, even "
|
||||
"outside the mission mode task (detected via the demo-manager "
|
||||
"cinematic signal, the same one the cutscene frame-rate clamp "
|
||||
"uses - so ac6_cutscene_clamp=false disables this detection). "
|
||||
"Cinematics are staged and framed for 16:9, so widening can "
|
||||
"expose things outside that frame (set edges, actors popping in "
|
||||
"at the frustum border). Off = cinematics letterbox at 16:9 "
|
||||
"as staged.");
|
||||
REXCVAR_DEFINE_BOOL(ac6_widescreen, false, "AC6/Enhancements",
|
||||
"Ultrawide support (hor+), in missions only. The aspect follows "
|
||||
"the window size; menus and the front end stay vanilla 16:9.");
|
||||
REXCVAR_DEFINE_BOOL(ac6_widescreen_cinematics, true, "AC6/Enhancements",
|
||||
"With ultrawide on, render in-engine cinematics wide too. They "
|
||||
"are staged for 16:9, so widening can expose set edges.");
|
||||
// Read (never written) only to report it in the activation log.
|
||||
REXCVAR_DECLARE(bool, present_letterbox);
|
||||
|
||||
|
||||
@@ -20,6 +20,11 @@
|
||||
|
||||
REXCVAR_DECLARE(std::string, ac6_graphics_backend);
|
||||
|
||||
// Defined in main.cpp: the config preset suite (session defaults + fix
|
||||
// payload expansion) and its post-logging summary lines.
|
||||
void ApplyAc6ConfigPresets();
|
||||
void LogAc6ConfigPresetSummary();
|
||||
|
||||
class Ac6recompApp : public rex::ReXApp {
|
||||
public:
|
||||
using rex::ReXApp::ReXApp;
|
||||
@@ -40,9 +45,15 @@ class Ac6recompApp : public rex::ReXApp {
|
||||
// and rejects wrong/corrupt images with a clear message.
|
||||
uint32_t OnGetExpectedTitleId() const override { return 0x4E4D07D1; }
|
||||
|
||||
// Runs right after the toml is parsed and before anything consumes cvars:
|
||||
// the one place presets can both see the user's real values and still act
|
||||
// before logging/window/graphics read the results.
|
||||
void OnConfigLoaded() override { ApplyAc6ConfigPresets(); }
|
||||
|
||||
void OnPreSetup(rex::RuntimeConfig& config) override {
|
||||
REXLOG_INFO("Ac6recompApp::OnPreSetup");
|
||||
rex::ReXApp::OnPreSetup(config);
|
||||
LogAc6ConfigPresetSummary();
|
||||
|
||||
const std::string requested_backend = REXCVAR_GET(ac6_graphics_backend);
|
||||
#if REX_HAS_VULKAN
|
||||
|
||||
+130
-116
@@ -12,37 +12,20 @@ REXCVAR_DECLARE(bool, ac6_native_graphics_enabled);
|
||||
REXCVAR_DECLARE(bool, ac6_force_safe_draw_resolution_scale);
|
||||
REXCVAR_DECLARE(bool, ac6_force_safe_direct_host_resolve);
|
||||
REXCVAR_DECLARE(std::string, ac6_graphics_mode);
|
||||
REXCVAR_DECLARE(bool, direct_host_resolve);
|
||||
REXCVAR_DECLARE(int32_t, resolution_scale);
|
||||
REXCVAR_DECLARE(int32_t, draw_resolution_scale_x);
|
||||
REXCVAR_DECLARE(int32_t, draw_resolution_scale_y);
|
||||
REXCVAR_DECLARE(bool, param_gen_integer_guest_position);
|
||||
REXCVAR_DECLARE(bool, param_gen_host_subpixel_restore);
|
||||
REXCVAR_DECLARE(std::string, ac6_neutralize_deswizzle_hashes);
|
||||
REXCVAR_DECLARE(std::string, ac6_snap_guest_texel_hashes);
|
||||
REXCVAR_DECLARE(std::string, ac6_densify_x_fetch_hashes);
|
||||
REXCVAR_DECLARE(std::string, ac6_densify_y_fetch_hashes);
|
||||
REXCVAR_DECLARE(std::string, log_file);
|
||||
REXCVAR_DECLARE(bool, param_gen_integer_guest_position);
|
||||
REXCVAR_DECLARE(bool, param_gen_host_subpixel_restore);
|
||||
REXCVAR_DECLARE(std::string, log_level);
|
||||
REXCVAR_DECLARE(bool, ac6_d3d_trace);
|
||||
REXCVAR_DECLARE(bool, ac6_backend_debug_swap);
|
||||
REXCVAR_DECLARE(bool, ac6_backend_log_signatures);
|
||||
REXCVAR_DECLARE(bool, ac6_backend_signature_diagnostics);
|
||||
REXCVAR_DECLARE(bool, ac6_texture_swaps_dump_enabled);
|
||||
REXCVAR_DECLARE(bool, vsync);
|
||||
REXCVAR_DECLARE(bool, guest_vblank_sync_to_refresh);
|
||||
REXCVAR_DECLARE(bool, host_present_from_non_ui_thread);
|
||||
#if REX_HAS_D3D12
|
||||
REXCVAR_DECLARE(bool, d3d12_allow_variable_refresh_rate_and_tearing);
|
||||
REXCVAR_DECLARE(bool, d3d12_low_latency_swap_chain);
|
||||
REXCVAR_DECLARE(int32_t, d3d12_max_frame_latency);
|
||||
#endif
|
||||
REXCVAR_DECLARE(bool, vfetch_index_rounding_bias);
|
||||
REXCVAR_DECLARE(int32_t, video_mode_width);
|
||||
REXCVAR_DECLARE(int32_t, video_mode_height);
|
||||
REXCVAR_DECLARE(std::string, resolution);
|
||||
REXCVAR_DECLARE(int32_t, window_width);
|
||||
REXCVAR_DECLARE(int32_t, window_height);
|
||||
|
||||
#if REX_HAS_VULKAN
|
||||
#define AC6_DEFAULT_GRAPHICS_BACKEND "vulkan"
|
||||
@@ -61,25 +44,21 @@ REXCVAR_DEFINE_STRING(ac6_graphics_backend, AC6_DEFAULT_GRAPHICS_BACKEND,
|
||||
REXCVAR_DEFINE_BOOL(ac6_performance_mode, true, "AC6/Performance",
|
||||
"Disable all diagnostics, logging, and development overlays for maximum runtime performance");
|
||||
|
||||
// Correctness-fix switches. Each expands
|
||||
// to its detailed cvars + AC6-specific shader hashes in ApplyAc6FixDefaults()
|
||||
// unless the user overrode them.
|
||||
REXCVAR_DEFINE_BOOL(ac6_fix_scaling, true, "AC6",
|
||||
"Fix the upscaling mosaic on AC6's deferred EDRAM restore passes by "
|
||||
"flooring the PsParamGen pixel position (+ host sub-pixel restore). "
|
||||
"Auto-applies only at draw resolution scale > 1; inert at 1x. On by default.");
|
||||
REXCVAR_DEFINE_BOOL(ac6_fix_deswizzle, true, "AC6",
|
||||
"Fix the de-swizzle mosaic/streaks: identity-override AC6's manual EDRAM "
|
||||
"sub-tile de-swizzle, which is always a wrong texel permutation once the "
|
||||
"emulator detiles to linear. On by default; effective at all draw scales "
|
||||
"(the swizzle is present at 1x too).");
|
||||
REXCVAR_DEFINE_BOOL(ac6_fix_dof, true, "AC6",
|
||||
"Fix the in-engine cutscene depth-of-field striping/ghosting at draw "
|
||||
"resolution scale > 1: the 6-pass DoF chain is authored for the 640x360 "
|
||||
"grid, so its guest-texel kernels alias against the extra detail of "
|
||||
"scaled sources (combed CoC weights, ghost copies at the sparse gather "
|
||||
"taps). Snaps the CoC pre-blurs to the guest grid and densifies the "
|
||||
"gather taps along each pass's axis. On by default; inert at 1x.");
|
||||
// Correctness-fix switches. Each expands to its detailed cvars + AC6-specific
|
||||
// shader hashes in ApplyAc6FixDefaults() unless the user overrode them. The
|
||||
// payloads apply at shader translation, so a change needs a restart.
|
||||
REXCVAR_DEFINE_BOOL(ac6_fix_scaling, true, "AC6/Fixes",
|
||||
"Fix the upscaling mosaic on deferred rendering passes. Only "
|
||||
"active at resolution scale above 1x.")
|
||||
.lifecycle(rex::cvar::Lifecycle::kRequiresRestart);
|
||||
REXCVAR_DEFINE_BOOL(ac6_fix_deswizzle, true, "AC6/Fixes",
|
||||
"Fix mosaic and streak artifacts from the game's manual texture "
|
||||
"de-swizzle. Effective at all resolution scales.")
|
||||
.lifecycle(rex::cvar::Lifecycle::kRequiresRestart);
|
||||
REXCVAR_DEFINE_BOOL(ac6_fix_dof, true, "AC6/Fixes",
|
||||
"Fix cutscene depth-of-field striping and ghosting at resolution "
|
||||
"scale above 1x.")
|
||||
.lifecycle(rex::cvar::Lifecycle::kRequiresRestart);
|
||||
|
||||
#include "generated/ac6recomp_config.h"
|
||||
#include "generated/ac6recomp_init.h"
|
||||
@@ -105,63 +84,69 @@ void ApplyAc6HybridStartupSafetyOverrides() {
|
||||
}
|
||||
|
||||
if (REXCVAR_GET(ac6_force_safe_draw_resolution_scale)) {
|
||||
REXCVAR_SET(resolution_scale, 1);
|
||||
REXCVAR_SET(draw_resolution_scale_x, 1);
|
||||
REXCVAR_SET(draw_resolution_scale_y, 1);
|
||||
rex::cvar::SetSessionDefault("resolution_scale", "1",
|
||||
"ac6_force_safe_draw_resolution_scale");
|
||||
rex::cvar::SetSessionDefault("draw_resolution_scale_x", "1",
|
||||
"ac6_force_safe_draw_resolution_scale");
|
||||
rex::cvar::SetSessionDefault("draw_resolution_scale_y", "1",
|
||||
"ac6_force_safe_draw_resolution_scale");
|
||||
}
|
||||
// (The >1x param-gen mosaic fix is applied by ApplyAc6FixDefaults via
|
||||
// ac6_fix_scaling.)
|
||||
|
||||
if (REXCVAR_GET(ac6_force_safe_direct_host_resolve)) {
|
||||
REXCVAR_SET(direct_host_resolve, false);
|
||||
rex::cvar::SetSessionDefault("direct_host_resolve", "false",
|
||||
"ac6_force_safe_direct_host_resolve");
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyAc6DefaultSettings() {
|
||||
if (!rex::cvar::HasNonDefaultValue("vsync")) {
|
||||
REXCVAR_SET(vsync, true);
|
||||
}
|
||||
if (!rex::cvar::HasNonDefaultValue("guest_vblank_sync_to_refresh")) {
|
||||
REXCVAR_SET(guest_vblank_sync_to_refresh, true);
|
||||
}
|
||||
if (!rex::cvar::HasNonDefaultValue("host_present_from_non_ui_thread")) {
|
||||
REXCVAR_SET(host_present_from_non_ui_thread, true);
|
||||
}
|
||||
rex::cvar::SetSessionDefault("vsync", "true");
|
||||
rex::cvar::SetSessionDefault("guest_vblank_sync_to_refresh", "true");
|
||||
rex::cvar::SetSessionDefault("host_present_from_non_ui_thread", "true");
|
||||
#if REX_HAS_D3D12
|
||||
if (!rex::cvar::HasNonDefaultValue("d3d12_allow_variable_refresh_rate_and_tearing")) {
|
||||
REXCVAR_SET(d3d12_allow_variable_refresh_rate_and_tearing, true);
|
||||
}
|
||||
if (!rex::cvar::HasNonDefaultValue("d3d12_low_latency_swap_chain")) {
|
||||
REXCVAR_SET(d3d12_low_latency_swap_chain, true);
|
||||
}
|
||||
if (!rex::cvar::HasNonDefaultValue("d3d12_max_frame_latency")) {
|
||||
REXCVAR_SET(d3d12_max_frame_latency, 1);
|
||||
}
|
||||
rex::cvar::SetSessionDefault("d3d12_allow_variable_refresh_rate_and_tearing", "true");
|
||||
rex::cvar::SetSessionDefault("d3d12_low_latency_swap_chain", "true");
|
||||
rex::cvar::SetSessionDefault("d3d12_max_frame_latency", "1");
|
||||
#endif
|
||||
if (!rex::cvar::HasNonDefaultValue("vfetch_index_rounding_bias")) {
|
||||
REXCVAR_SET(vfetch_index_rounding_bias, true);
|
||||
}
|
||||
if (!rex::cvar::HasNonDefaultValue("direct_host_resolve")) {
|
||||
REXCVAR_SET(direct_host_resolve, false);
|
||||
}
|
||||
if (!rex::cvar::HasNonDefaultValue("video_mode_width")) {
|
||||
REXCVAR_SET(video_mode_width, 1920);
|
||||
}
|
||||
if (!rex::cvar::HasNonDefaultValue("video_mode_height")) {
|
||||
REXCVAR_SET(video_mode_height, 1080);
|
||||
}
|
||||
if (!rex::cvar::HasNonDefaultValue("resolution")) {
|
||||
REXCVAR_SET(resolution, "1080p");
|
||||
}
|
||||
if (!rex::cvar::HasNonDefaultValue("window_width")) {
|
||||
REXCVAR_SET(window_width, 1920);
|
||||
}
|
||||
if (!rex::cvar::HasNonDefaultValue("window_height")) {
|
||||
REXCVAR_SET(window_height, 1080);
|
||||
}
|
||||
rex::cvar::SetSessionDefault("vfetch_index_rounding_bias", "true");
|
||||
rex::cvar::SetSessionDefault("direct_host_resolve", "false");
|
||||
rex::cvar::SetSessionDefault("video_mode_width", "1920");
|
||||
rex::cvar::SetSessionDefault("video_mode_height", "1080");
|
||||
rex::cvar::SetSessionDefault("resolution", "1080p");
|
||||
rex::cvar::SetSessionDefault("window_width", "1920");
|
||||
rex::cvar::SetSessionDefault("window_height", "1080");
|
||||
}
|
||||
|
||||
void ApplyAc6PerformanceModeOverrides() {
|
||||
if (REXCVAR_GET(ac6_performance_mode)) {
|
||||
// Session-default RECORDING: gives the settings menu the "(set by
|
||||
// ac6_performance_mode)" annotation and the boot-time value. The
|
||||
// actual enforcement stays the pre-existing per-frame re-assert
|
||||
// (EnforceAc6PerformanceModeOverrides below) - these cvars predate
|
||||
// this rework and keep their upstream semantics: performance mode
|
||||
// forces them off while enabled.
|
||||
rex::cvar::SetSessionDefault("log_level", "error", "ac6_performance_mode");
|
||||
rex::cvar::SetSessionDefault("ac6_d3d_trace", "false", "ac6_performance_mode");
|
||||
rex::cvar::SetSessionDefault("ac6_render_capture", "false", "ac6_performance_mode");
|
||||
rex::cvar::SetSessionDefault("ac6_backend_debug_swap", "false", "ac6_performance_mode");
|
||||
rex::cvar::SetSessionDefault("ac6_backend_log_signatures", "false",
|
||||
"ac6_performance_mode");
|
||||
rex::cvar::SetSessionDefault("ac6_backend_signature_diagnostics", "false",
|
||||
"ac6_performance_mode");
|
||||
rex::cvar::SetSessionDefault("ac6_texture_swaps_dump_enabled", "false",
|
||||
"ac6_performance_mode");
|
||||
} else {
|
||||
rex::cvar::SetSessionDefault("log_level", "debug");
|
||||
}
|
||||
}
|
||||
|
||||
// The pre-existing enforcement, unchanged from before this rework: while
|
||||
// performance mode is on, the diagnostics are re-forced off every frame from
|
||||
// the graphics overlay. These writes go through REXCVAR_SET (raw storage), so
|
||||
// they no longer leak into a settings-menu save - the save writes the user's
|
||||
// own values (FlagEntry::user_value).
|
||||
void EnforceAc6PerformanceModeOverrides() {
|
||||
if (!REXCVAR_GET(ac6_performance_mode)) {
|
||||
return;
|
||||
}
|
||||
@@ -174,45 +159,86 @@ void ApplyAc6PerformanceModeOverrides() {
|
||||
REXCVAR_SET(ac6_texture_swaps_dump_enabled, false);
|
||||
}
|
||||
|
||||
// Each fix switch -> its detailed cvars + AC6-specific shader hashes. The
|
||||
// game-specific constants live here in code, not in the user's toml; each
|
||||
// value is filled in only if the user did NOT set it explicitly, so any toml
|
||||
// override still wins (the dev/debug path).
|
||||
#define AC6_SET_IF_UNSET(cv, value) \
|
||||
do { if (!rex::cvar::HasNonDefaultValue(#cv)) REXCVAR_SET(cv, value); } while (0)
|
||||
|
||||
void ApplyAc6FixDefaults() {
|
||||
// Post-config, these read the USER's draw scale - with the old app-create
|
||||
// timing this gate always saw the default 1x, so the scaling fix payload
|
||||
// never engaged for anyone who set their scale in the toml.
|
||||
const bool scaled = REXCVAR_GET(draw_resolution_scale_x) > 1 ||
|
||||
REXCVAR_GET(draw_resolution_scale_y) > 1;
|
||||
|
||||
// param_gen floor + host sub-pixel restore only bites at >1x --
|
||||
// inert at 1x, so gate it on the draw scale.
|
||||
if (REXCVAR_GET(ac6_fix_scaling) && scaled) {
|
||||
AC6_SET_IF_UNSET(param_gen_integer_guest_position, true);
|
||||
AC6_SET_IF_UNSET(param_gen_host_subpixel_restore, true);
|
||||
rex::cvar::SetSessionDefault("param_gen_integer_guest_position", "true",
|
||||
"ac6_fix_scaling");
|
||||
rex::cvar::SetSessionDefault("param_gen_host_subpixel_restore", "true",
|
||||
"ac6_fix_scaling");
|
||||
}
|
||||
// de-swizzle identity override is a texture-layout mismatch present
|
||||
// at ALL draw scales, so it is NOT gated on scale.
|
||||
if (REXCVAR_GET(ac6_fix_deswizzle)) {
|
||||
AC6_SET_IF_UNSET(ac6_neutralize_deswizzle_hashes,
|
||||
"7d22894002d16018, 17e5e4ac3e713245:4");
|
||||
rex::cvar::SetSessionDefault("ac6_neutralize_deswizzle_hashes",
|
||||
"7d22894002d16018, 17e5e4ac3e713245:4",
|
||||
"ac6_fix_deswizzle");
|
||||
}
|
||||
// Cutscene DoF chain (CoC pre-blur pair snapped to the guest grid; the
|
||||
// two 13-tap gathers densified along their pass axes). The translator
|
||||
// only emits these at draw scale > 1, so no scale gate is needed here.
|
||||
if (REXCVAR_GET(ac6_fix_dof)) {
|
||||
AC6_SET_IF_UNSET(ac6_snap_guest_texel_hashes,
|
||||
"85d733927e3bba9c, d050dfa6f58567f6");
|
||||
AC6_SET_IF_UNSET(ac6_densify_x_fetch_hashes, "6328f9c40913c82c");
|
||||
AC6_SET_IF_UNSET(ac6_densify_y_fetch_hashes, "5bd20f9d0d911687");
|
||||
rex::cvar::SetSessionDefault("ac6_snap_guest_texel_hashes",
|
||||
"85d733927e3bba9c, d050dfa6f58567f6", "ac6_fix_dof");
|
||||
rex::cvar::SetSessionDefault("ac6_densify_x_fetch_hashes", "6328f9c40913c82c",
|
||||
"ac6_fix_dof");
|
||||
rex::cvar::SetSessionDefault("ac6_densify_y_fetch_hashes", "5bd20f9d0d911687",
|
||||
"ac6_fix_dof");
|
||||
}
|
||||
}
|
||||
#undef AC6_SET_IF_UNSET
|
||||
|
||||
} // namespace
|
||||
|
||||
void ApplyAc6PerformanceModeOverridesPublic() {
|
||||
// The whole preset suite. Called from Ac6recompApp::OnConfigLoaded - AFTER
|
||||
// rex::cvar::LoadConfig parsed the user's toml (so the master switches and
|
||||
// gates read the user's real values, fixing the "ac6_fix_* dead from the
|
||||
// toml" bug) and BEFORE anything consumes cvars (logging, window, graphics).
|
||||
// Everything goes through SetSessionDefault: a user-set value always wins,
|
||||
// and none of these values leak into the user's config on a settings save.
|
||||
// The game-specific fix constants stay here in code, not in the user's toml.
|
||||
void ApplyAc6ConfigPresets() {
|
||||
rex::cvar::SetSessionDefault("log_file", "ac6recomp.log");
|
||||
ApplyAc6DefaultSettings();
|
||||
ApplyAc6HybridStartupSafetyOverrides();
|
||||
ApplyAc6FixDefaults();
|
||||
ApplyAc6PerformanceModeOverrides();
|
||||
// Runtime enforcement of performance mode stays the pre-existing
|
||||
// per-frame re-assert from the graphics overlay (see
|
||||
// ApplyAc6PerformanceModeOverridesPublic).
|
||||
}
|
||||
|
||||
void ApplyAc6PerformanceModeOverridesPublic() {
|
||||
EnforceAc6PerformanceModeOverrides();
|
||||
}
|
||||
|
||||
// One greppable summary of what the presets decided, logged from OnPreSetup
|
||||
// (the presets themselves run before logging is initialized). Logged at
|
||||
// error level so it survives performance mode's log_level=error session
|
||||
// default - the same reason the widescreen activation line logs at error.
|
||||
// This is the support line: it shows whether a master switch genuinely
|
||||
// disengaged its payload.
|
||||
void LogAc6ConfigPresetSummary() {
|
||||
REXLOG_ERROR("AC6 config: graphics mode={} capture={}", REXCVAR_GET(ac6_graphics_mode),
|
||||
REXCVAR_GET(ac6_render_capture) ? "true" : "false");
|
||||
REXLOG_ERROR("AC6 fixes: scaling={} deswizzle={} dof={} perf_mode={} unlock_fps={}",
|
||||
REXCVAR_GET(ac6_fix_scaling), REXCVAR_GET(ac6_fix_deswizzle),
|
||||
REXCVAR_GET(ac6_fix_dof), REXCVAR_GET(ac6_performance_mode),
|
||||
REXCVAR_GET(ac6_unlock_fps));
|
||||
REXLOG_ERROR("AC6 fix payloads: neutralize='{}' snap='{}' densify_x='{}' densify_y='{}' "
|
||||
"param_gen={}/{}",
|
||||
REXCVAR_GET(ac6_neutralize_deswizzle_hashes),
|
||||
REXCVAR_GET(ac6_snap_guest_texel_hashes),
|
||||
REXCVAR_GET(ac6_densify_x_fetch_hashes),
|
||||
REXCVAR_GET(ac6_densify_y_fetch_hashes),
|
||||
REXCVAR_GET(param_gen_integer_guest_position),
|
||||
REXCVAR_GET(param_gen_host_subpixel_restore));
|
||||
}
|
||||
|
||||
void InitEarlyLog() {
|
||||
@@ -230,24 +256,12 @@ std::unique_ptr<rex::ui::WindowedApp> Ac6recompAppCreate(rex::ui::WindowedAppCon
|
||||
g_boot_log << "Ac6recompApp::Create called" << std::endl;
|
||||
g_boot_log.flush();
|
||||
}
|
||||
|
||||
// Force SDK logging to a file as well
|
||||
REXCVAR_SET(log_file, "ac6recomp.log");
|
||||
if (!rex::cvar::HasNonDefaultValue("log_level")) {
|
||||
REXCVAR_SET(log_level, "debug");
|
||||
}
|
||||
// Smooth 60fps unlock is on by default now (zero-config for players); the
|
||||
// toml can still set ac6_unlock_fps=false for stock locked behaviour.
|
||||
REXCVAR_SET(ac6_unlock_fps, true);
|
||||
ApplyAc6DefaultSettings();
|
||||
ApplyAc6HybridStartupSafetyOverrides();
|
||||
ApplyAc6FixDefaults();
|
||||
ApplyAc6PerformanceModeOverrides();
|
||||
|
||||
REXLOG_INFO("Ac6recompAppCreate: graphics mode={} capture={}",
|
||||
REXCVAR_GET(ac6_graphics_mode),
|
||||
REXCVAR_GET(ac6_render_capture) ? "true" : "false");
|
||||
|
||||
|
||||
// No cvar presets here: this runs BEFORE rex::cvar::LoadConfig parses
|
||||
// the toml, so anything applied here reads defaults, not user values.
|
||||
// The preset suite lives in ApplyAc6ConfigPresets, called from
|
||||
// Ac6recompApp::OnConfigLoaded.
|
||||
|
||||
return Ac6recompApp::Create(ctx);
|
||||
}
|
||||
|
||||
|
||||
+16
-35
@@ -28,41 +28,22 @@ REXCVAR_DEFINE_BOOL(ac6_unlock_fps, true, "AC6",
|
||||
REXCVAR_DEFINE_BOOL(ac6_cutscene_clamp, true, "AC6",
|
||||
"Suspend the 60fps unlock during in-engine cutscenes so they "
|
||||
"play at native ~30fps instead of double speed");
|
||||
REXCVAR_DEFINE_BOOL(ac6_dynamic_vblank, true, "AC6",
|
||||
"With the FPS unlock active, pace frame-locked content (menus, "
|
||||
"cutscenes, pause) at the native 60Hz guest vblank while gameplay "
|
||||
"free-runs at the configured rate. Gameplay is detected via the "
|
||||
"world-compositor draw heartbeat; cutscenes via the cinematic "
|
||||
"hooks.");
|
||||
REXCVAR_DEFINE_DOUBLE(ac6_fps_target, 60.0, "AC6",
|
||||
"The rate the simulation + presentation run at under the FPS unlock. "
|
||||
">0 = that exact rate (clamped to 30..ac6_max_sim_fps). "
|
||||
"0 = AUTO: the largest rate <= ac6_max_sim_fps that evenly divides your "
|
||||
"monitor's refresh, so frames land on refresh boundaries instead of "
|
||||
"beating against them (240Hz->60, 144Hz->48, 120Hz->60, 60Hz->60). "
|
||||
"Pick a target that divides your refresh; auto does this for you. "
|
||||
"Mirrors PA's native PC engine, which has no separate cap and simply "
|
||||
"runs the (dt-correct) sim at the display rate.");
|
||||
REXCVAR_DEFINE_DOUBLE(ac6_max_sim_fps, 60.0, "AC6",
|
||||
"Ceiling on the simulation/pacing rate. AC6's physics is only validated "
|
||||
"to 60fps; above it, fixed-step assumptions surface (untested regime). "
|
||||
"Both ac6_fps_target and the auto (refresh-matched) target are clamped to "
|
||||
"this. Raise only once the >60 regime has been validated.");
|
||||
REXCVAR_DEFINE_BOOL(ac6_dt_snap, true, "AC6",
|
||||
"With the FPS unlock active, snap the per-frame simulation delta to the "
|
||||
"EXACT pacing target when the real frame time is within ~1ms of it, so "
|
||||
"the fixed-step integrator sees a constant step. Removes the residual "
|
||||
"per-frame delta jitter that a fixed-step sim turns into visible shake; "
|
||||
"genuine slowdowns fall through to the precision path.");
|
||||
REXCVAR_DEFINE_DOUBLE(ac6_min_sim_fps, 20.0, "AC6",
|
||||
"Lowest framerate the simulation runs at true speed before the "
|
||||
"game's frame-delta clamp forces slow motion. The stock game floors "
|
||||
"at 30 (below 30fps it plays in slow motion); this lowers the floor "
|
||||
"(default 20) so a sub-30fps dip runs at correct speed - just "
|
||||
"choppier - instead of slowing down and rubber-banding on recovery. "
|
||||
"Set 30 for stock behavior. Active with the FPS unlock; drives the "
|
||||
"frame-delta clamp and the physics step cap together so dynamics "
|
||||
"stay consistent with the kinematics.");
|
||||
REXCVAR_DEFINE_BOOL(ac6_dynamic_vblank, true, "AC6/Enhancements",
|
||||
"Pace menus and cutscenes at the native 60Hz vblank while "
|
||||
"gameplay runs at the target rate.");
|
||||
REXCVAR_DEFINE_DOUBLE(ac6_fps_target, 60.0, "AC6/Enhancements",
|
||||
"Target framerate under the FPS unlock. 0 = auto: the highest "
|
||||
"rate up to ac6_max_sim_fps that evenly divides your monitor's "
|
||||
"refresh rate.");
|
||||
REXCVAR_DEFINE_DOUBLE(ac6_max_sim_fps, 60.0, "AC6/Enhancements",
|
||||
"Ceiling on the simulation rate. The game's physics is only "
|
||||
"validated to 60fps; raise only for testing.");
|
||||
REXCVAR_DEFINE_BOOL(ac6_dt_snap, true, "AC6/Enhancements",
|
||||
"Snap the simulation step to the pacing target when within ~1ms "
|
||||
"of it, removing residual micro-stutter and shake.");
|
||||
REXCVAR_DEFINE_DOUBLE(ac6_min_sim_fps, 20.0, "AC6/Enhancements",
|
||||
"Lowest framerate the game runs at true speed before slow "
|
||||
"motion kicks in. Stock behaviour is 30.");
|
||||
|
||||
using Clock = std::chrono::steady_clock;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user