mirror of
https://github.com/sal063/AC6_recomp
synced 2026-07-12 15:42:23 -04:00
Fix accelerated sink rate on 60 fps
Add mid-asm hook ac6PathDropHook at 0x82304F40 scaling the drop by frame_ms/33.3ms, which cancels the amplification.
This commit is contained in:
@@ -10606,6 +10606,26 @@ address = 0x821EFD74
|
||||
name = "ac6DeltaPrecisionHook"
|
||||
registers = ["r8", "r10", "r29", "r30"]
|
||||
|
||||
# Scale the flight model's per-frame vertical lift-deficit drop by the
|
||||
# frame-step ratio (see ac6_fps_physics_fix).
|
||||
#
|
||||
# In the core force step rex_sub_823046A0, f9 = |lift-deficit| is subtracted
|
||||
# from the vertical velocity AFTER the 1/dt-weighted path smoothing mix, and
|
||||
# the result is re-normalized into the stored flight-path direction
|
||||
# [fm+128..136]. Inside that feedback loop the drop's equilibrium
|
||||
# contribution is amplified by (1 + retention) with retention ~ 1/dt, so the
|
||||
# steady-state angle of attack scales with framerate: a hands-off glide at
|
||||
# 60fps settles on a ~2 degree steeper path than at 30fps with identical
|
||||
# attitude and identical force inputs (measured). This hook fires at
|
||||
# 0x82304F40 (the `fsubs f11,f11,f9` that applies the drop) and scales f9 by
|
||||
# frame_ms/33.3ms, which makes the equilibrium framerate-independent while
|
||||
# changing the drop's small direct velocity share (~1/retention of the
|
||||
# effect) by under 2%. Exact no-op at the native 30fps cadence.
|
||||
[[midasm_hook]]
|
||||
address = 0x82304F40
|
||||
name = "ac6PathDropHook"
|
||||
registers = ["f9"]
|
||||
|
||||
# Record frame timestamps at each D3DDevice_Swap call for timing overlay.
|
||||
# Placed before the device[21516] branch so it fires unconditionally —
|
||||
# when that field is non-zero the function skips VdSwap, which caused
|
||||
|
||||
@@ -45,6 +45,39 @@
|
||||
// Both symbols are weak in the generated code and registered by address in
|
||||
// ac6recomp_init, so these strong overrides capture direct calls and vtable
|
||||
// dispatch alike.
|
||||
//
|
||||
// One term needs a different treatment: the sink-rate / angle-of-attack
|
||||
// equilibrium (the ac6PathDropHook mid-asm hook at the bottom of this file,
|
||||
// registered in ac6recomp_config.toml at 0x82304F40). Inside the core force
|
||||
// step, the stored flight-path direction [this+128..136] - the direction the
|
||||
// aircraft actually travels, distinct from where the nose points - is rebuilt
|
||||
// each call as
|
||||
//
|
||||
// vel = |V| * normalize(unit(V) + w * path_old) - drop * up
|
||||
// path_new = normalize(vel)
|
||||
//
|
||||
// where w = [this+324]*const/dt is the old-path retention (1/dt-weighted, so
|
||||
// the smoothing itself is framerate-correct) and drop = |lift-deficit| is
|
||||
// subtracted from the vertical once per CALL. Because the drop lands inside
|
||||
// that feedback loop, its steady-state contribution is amplified by (1 + w):
|
||||
// the equilibrium angle of attack is (drop/|V|) * (1 + w), and w doubles at
|
||||
// 60fps. Measured on a hands-off glide: every INPUT to the step (force
|
||||
// vector [this+1328..1340], speed, deficit fields) is identical between
|
||||
// framerates, yet the path settles ~2 degrees steeper at 60fps - same speed
|
||||
// down a steeper line means a faster sink and an earlier ground impact
|
||||
// (reported as gravity pulling harder at high fps, worse inverted since the
|
||||
// deficit is larger).
|
||||
//
|
||||
// The pre/post blend above cannot fix that: blending rescales an update's
|
||||
// RATE but shares its fixed points, so it can never move a framerate-
|
||||
// dependent equilibrium (confirmed experimentally - the settle slowed, the
|
||||
// floor did not move). Instead the hook scales the drop itself by the same
|
||||
// frame-time ratio at the exact instruction that applies it, which cancels
|
||||
// the 1/dt amplification: the equilibrium becomes drop*C/33.3ms/|V| at any
|
||||
// framerate. The drop's direct (unamplified) share of the sink velocity is
|
||||
// only ~1/w (~3%) of the effect, so scaling it perturbs the true sink rate
|
||||
// by well under 2%; at the native cadence the multiply is a bit-exact no-op.
|
||||
// The alternate-mode step applies no such in-loop drop and needs no hook.
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
@@ -169,3 +202,16 @@ PPC_FUNC_IMPL(rex_sub_82329B40) {
|
||||
BlendFieldDelta(base, self + kTriggerCommandOffset, pre, ratio, "trigger-command(+1456)",
|
||||
s_logged);
|
||||
}
|
||||
|
||||
// Mid-asm hook (registered in ac6recomp_config.toml at 0x82304F40, the
|
||||
// `fsubs f11,f11,f9` inside rex_sub_823046A0): f9 holds the vertical
|
||||
// lift-deficit drop about to be subtracted from the velocity that becomes the
|
||||
// stored flight-path direction. Scaling it by the frame-step ratio makes the
|
||||
// angle-of-attack equilibrium framerate-independent (see the file header for
|
||||
// the mechanism). StepRatio() returns exactly 1.0 at the native cadence or
|
||||
// with the fix disabled, and x *= 1.0 is bit-exact, so this is a strict no-op
|
||||
// there. Runs on the guest sim thread inside the wrapped force step, where
|
||||
// the per-frame ratio is already cached.
|
||||
void ac6PathDropHook(PPCRegister& f9) {
|
||||
f9.f64 *= StepRatio();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user