diff --git a/docs/building.md b/docs/building.md index a3952b4d..e59f85c3 100644 --- a/docs/building.md +++ b/docs/building.md @@ -561,6 +561,12 @@ leaves the mode's own exit code and one `exit:` line. The second is what a libra process does when it faults in its detach: an injector's Vulkan layer did so at the end of every shader preparation on a player's PC, and the launcher took each finished preparation for a failed one. No game files, no GPU. +A change to the movie timestamp sites of `recomp/patches-v111.txt` (set `omovie`) passes +`python tests\movie_timeline_check.py --cc ` (`.venv`, Unicorn): it lifts the twelve replacement words on +their own and compares them with Unicorn over repeated, skipped and rounded timestamps, registers, flags and memory. +It does not cover the sites as `gen.py` emits them inside their two functions, nor the add and store after them: +a regenerate of the game code and a compile of the chunk that holds main+0x3b5c7c4 and main+0x3b5c908 does. + A change to the memory gate of the background pipeline builds (`runtime/vk_build_gate.h`, `runtime/vk_pipeline.c` `gate_enter`) passes `python tests\build_gate_check.py []`, which builds the header alone and checks what it admits and refuses and that builds under way count through their reservations. With the executable it also runs @@ -672,7 +678,6 @@ change passes these, all run with `.venv`: | `tests\diff\func.py` | whole functions as the generator emits them, loops, jump tables and calls included (calls go to a stub that checks the registers at the call) | yes | | `tests\flags_check.py` | the function-local flag helpers of `runtime\cpu.h` against its packed ones | no | | `tests\ds3_check.py` | the DualShock 3 report decoding and output report of `runtime\input_ds3.c` | no | -| `tests\movie_timeline_check.py --cc PATH` | both 1.1.1 movie timestamp patches against Unicorn and lifted C, with repeated/skipped pictures, rounded timestamps and preserved registers/flags | no | | `tests\gpa_check.py` | the device address table of `runtime\vk_gpa.c` as a shader reads it: mapping, mirrors and twins, overlapping imports, and blocks given back and taken again while pool addresses climb (no GPU; any Python) | no | | `tests\excl_mt.py` | the exclusive monitor helpers under contention and ABA, on several threads | no | | `tests\jumptables_check.py` | `recomp/jumptables.py` against a Unicorn oracle; must report 0 disagreements, on 1.4.3 too | yes | diff --git a/nx2navi.ini.example b/nx2navi.ini.example index 937dbb7a..a9beb1a8 100644 --- a/nx2navi.ini.example +++ b/nx2navi.ini.example @@ -48,6 +48,11 @@ # rounded that step down to nothing: the window stayed until the area was left and its counting sound went on. 1 lets # it count at least one a frame; 0 is the game's own count. # pickup_count = 1 +# A movie's narration and fades follow the picture on screen: 1 counts their time from the shown picture's +# timestamp, so they stay with it at any frame rate (with movie_fps = 0 too) and wait when the picture stalls; 0 counts +# the game's redraws, as before, which keeps them with the music when the picture stalls. Read at start; needs game +# code generated with this release. +# movie_pts = 1 # The step the game is given each frame: 1 keeps it steady (the same value from frame to frame while the frame rate # holds, exactly one 60 Hz frame at a 60 fps limit) and makes up for a late frame over the next second; 0 is the mean # of the last five frame times, which changes with every frame. diff --git a/recomp/patches-v111.txt b/recomp/patches-v111.txt index 8414acdf..79fea458 100644 --- a/recomp/patches-v111.txt +++ b/recomp/patches-v111.txt @@ -22,6 +22,7 @@ set zstd NX2NAVI_ZSTD_NATIVE 1 set osolver NX2NAVI_PHYSICS_STEPS 1 set opickup NX2NAVI_PICKUP_COUNT 1 set freecam NX2NAVI_PATCH_FREECAM 0 +set omovie NX2NAVI_MOVIE_PTS 1 # Real-time step: the framework's per-frame update (main+0x6e6be0) gives the time manager **[main+0x4c7e890] its # nominal frame through main+0x3b4e300 (+0x10 frames per update, +0x14 the inverse, +0x18 seconds, +0x1c the rate), @@ -86,8 +87,14 @@ site osolver main 38afd20 1e380001 1e280001 # x8 = layer->controller->player->surface->timestamp in ns. Clamp negative timestamps to zero and convert to # the nearest 60 Hz timeline tick (WebM timestamps can be rounded to milliseconds). The original add of one # counts the first picture as frame one. x9 is scratch on both paths, and the replacement preserves flags. -site ostep main 3b5c7c4 b942fa88 f9400a88 f9400108 f940f508 f9406108 8aa8fd08 d2800789 9b097d08 528ca009 72a3b9a9 8b090108 d37ff929 9ac90908 -site ostep main 3b5c908 b942fa88 f9400a88 f9400108 f940f508 f9406108 8aa8fd08 d2800789 9b097d08 528ca009 72a3b9a9 8b090108 d37ff929 9ac90908 +# At the movie cap (runtime/frame_rate.c movie_cap_hz, 60 fps) the two counts are the same while the decoder keeps up. +# They differ when no new picture is ready for a redraw: the old count went on, so cues kept pace with the soundtrack, +# which plays on its own; this count waits with the picture and goes on with it, a step of 2 or more when the decoder +# drops a picture. main+0x3b3ec10 hands the counter out as a float frame (scvtf) through a virtual call, and its +# readers were not traced, so whether they compare frames as ranges or exact values is not known: if exactly, a cue +# on a dropped picture's tick would be missed. NX2NAVI_MOVIE_PTS=0 (movie_pts in nx2navi.ini) gives the old count. +site omovie main 3b5c7c4 b942fa88 f9400a88 f9400108 f940f508 f9406108 8aa8fd08 d2800789 9b097d08 528ca009 72a3b9a9 8b090108 d37ff929 9ac90908 +site omovie main 3b5c908 b942fa88 f9400a88 f9400108 f940f508 f9406108 8aa8fd08 d2800789 9b097d08 528ca009 72a3b9a9 8b090108 d37ff929 9ac90908 # Pick-up window count: the window that shows a pick-up above the character (its update is main+0xa67e50) moves the # number it shows (+0x360) to the new total (+0x35c) by trunc(frames per update * max(1, difference / 50)) a frame, and diff --git a/runtime/cfg.c b/runtime/cfg.c index fdb29d38..f476a23b 100644 --- a/runtime/cfg.c +++ b/runtime/cfg.c @@ -34,7 +34,7 @@ static int save_ok = 1; // the last cfg_save_to wrote (o // Every name the runtime reads (and the patch sets in generated/), without the prefix. A file key outside this list is // logged as unknown. A new variable goes here too, or its ini key logs a harmless "unknown key" line. static const char *const known[] = { - "CORES", "TM_DISPATCH", "NVN2_CS_TABLE", "SKIP_SHADERS", "MOVIE_DEBAND", "MOVIE_FPS", "MOVIE_SKIP", "MOVIE_OVERLAY", "MOVIES", "NPLN", "NVN2_PROGRAMS", "NVN_TRAP", "OCARINA_REALTIME", "ONESHOT_BUDGET", "ONESHOT_WAIT_MS", "OCARINA_STEP", "PHYSICS_STEPS", "PICKUP_COUNT", "STEP_STEADY", "VI_HZ", "VI_VRR", "TIME_DUMP", "SHADER_DUMP", "SHOT_AFTER", "SHOT_DIR", "SHOT_EVERY", // Switch 2 port: movie skip (romfs.c), NVN2 proc traps (nvn_procs.c) + "CORES", "TM_DISPATCH", "NVN2_CS_TABLE", "SKIP_SHADERS", "MOVIE_DEBAND", "MOVIE_FPS", "MOVIE_SKIP", "MOVIE_OVERLAY", "MOVIES", "NPLN", "NVN2_PROGRAMS", "NVN_TRAP", "OCARINA_REALTIME", "ONESHOT_BUDGET", "ONESHOT_WAIT_MS", "OCARINA_STEP", "PHYSICS_STEPS", "PICKUP_COUNT", "MOVIE_PTS", "STEP_STEADY", "VI_HZ", "VI_VRR", "TIME_DUMP", "SHADER_DUMP", "SHOT_AFTER", "SHOT_DIR", "SHOT_EVERY", // Switch 2 port: movie skip (romfs.c), NVN2 proc traps (nvn_procs.c) "AA", "AA_LOG", "AB", "AMIIBO_DIR", "ANISO", "APP_POOL", "ASPECT", "ASPECT_PATCH", "ASSUME_FOCUS", "ASTC", "ASTC_BC", "ASTC_TIME", "ASYNC_PREEMPT", "AUDIO", "AUDIO_CATCHUP", "AUDIO_CHANNELS", "AUDIO_DEVICE", "AUDIO_GAME_PRIO", "AUDIO_OFF", "AUDIO_PREFILL_MS", "AUDIO_RAMP", "AUDIO_STATS", "BARRIER_GAME", "MESH_AHEAD", "MESH_FALLBACK", "MESH_FALLBACK_MB", "MESH_FALLBACK_WORKERS", "PROBE_MESHFB_DIR", "BACKGROUND_WINDOW", "BIND_A", "BIND_B", "BIND_DOWN", "BIND_L", "BIND_LCLICK", "BIND_LEFT", "BIND_LSTICK_DOWN", "BIND_LSTICK_LEFT", @@ -102,7 +102,7 @@ static const struct { const char *key, *section; } sections[] = { {"GUEST_OPT", "performance"}, {"PASS_TIMES", "diagnostics"}, {"DUMP_KEY", "diagnostics"}, {"FPS_LIMIT", "frame rate"}, {"FPS60", "frame rate"}, {"RTSS", "frame rate"}, {"REALTIME", "frame rate"}, {"CLOTH_REALTIME", "frame rate"}, {"HIGHFPS_FIX", "frame rate"}, - {"PHYSICS_STEPS", "frame rate"}, {"PICKUP_COUNT", "frame rate"}, {"STEP_STEADY", "frame rate"}, + {"PHYSICS_STEPS", "frame rate"}, {"PICKUP_COUNT", "frame rate"}, {"MOVIE_PTS", "frame rate"}, {"STEP_STEADY", "frame rate"}, {"FULLSCREEN", "display"}, {"WINDOW_SIZE", "display"}, {"PERF_OVERLAY", "display"}, {"VK_PRESENT", "display"}, {"GPU_INDEX", "display"}, {"MONITOR", "display"}, {"OUTPUT_RES", "display"}, {"GAME_ASPECT", "display"}, {"GAME_RES", "display"}, {"RENDER_SCALE", "display"}, {"UPSCALER", "display"}, {"SHARPNESS", "display"}, {"STANDIN_FILTER", "display"}, {"ASPECT", "display"}, {"FOV", "display"}, {"ANISO", "display"}, diff --git a/runtime/frame_rate.c b/runtime/frame_rate.c index bdd3a205..40b607a1 100644 --- a/runtime/frame_rate.c +++ b/runtime/frame_rate.c @@ -193,11 +193,16 @@ static void frame_limit_live(void) { else rt_log("frame rate: limit at the monitor's refresh rate (60 until the window measures it)"); } -// NX2NAVI_MOVIE_FPS (default 60; 0 = off): the game's frame rate while a movie plays (ov_movie2.c ov_movie_live). -// Movie-synchronised events use the selected picture's timestamp with the 1.1.1 ostep patches enabled; redrawing -// a picture no longer advances that timeline. Keep the 60 Hz pacing policy for the 60 fps movies and for builds -// without those patches. It applies in both directions: a lower gameplay limit must not slow the movie's updates -// relative to its audio. A player can turn this pacing override off independently of the timestamp patches. +// NX2NAVI_MOVIE_FPS (default 60; 0 = off): the game's frame rate while a movie plays (ov_movie2.c ov_movie_live). A +// movie's event counts the game's updates as 60 Hz frames whatever the step: at 144 fps the intro's narration lines +// started 2.4 times too early, each over the one before, and its closing fade to black came at 65 s of the movie's +// 154, hiding the whole dream. The movies are 60 fps, so the cap shows the same pictures. It is the rate in both +// directions: under a lower limit (40 fps, or 60 shown with frame generation, which is 30 of the game's) the movie ran +// at that fraction of its speed and ended many seconds behind its sound, so the game is paced at the movie's rate +// while one plays, above the limit too. +// With set omovie of recomp/patches-v111.txt (NX2NAVI_MOVIE_PTS, on by default) the event count follows the shown +// picture's timestamp instead of the redraws, so with the cap off the cues no longer run ahead; the cap stays for the +// pacing in both directions and for builds without those sites. static int movie_cap_hz(void) { static int hz = -1; if (hz < 0) {