Repository navigation
Fix Ocarina of Time frame ratio calculation and enable frame buffering (0400486024DEC000) - #1996
Zard-Studios wants to merge 1 commit into
Conversation
…g (0400486024DEC000) - patches.py: In cutscenes, Nintendo sets bit 0 of [x21+0x24f], which selects base rate 30.0f in s1 instead of 60.0f. The previous stub calculated (step * base_rate) / 60, resulting in (2 * 30.0) / 60 = 1.0 at 30 fps (or 0.5 at 60 fps). This caused cutscenes and dialog progression to run in half-speed slow motion while audio played in real time. Replace with fmov s8, s0 to set the ratio directly to (float)step and adjust the CBZ forward jump offsets accordingly. - game.tsv: Add present_snap on (as Breath of the Wild) so that swapchain textures are copied immediately into host present snapshots, decoupling the game engine from display link vsync jitter and preventing micro-stutters under the macOS compositor.
… takes half the step, and the frame time floor is not paid back twice Movies: AV1 at 60 pictures a second with no sound track, and the game's player took one picture a calc at most (its limit, +0x20, from a creation argument of 1). Below 60 fps a movie fell behind the sound the game plays beside it: on an iPhone Duo at 26-31 ms a frame the opening's 9,254 pictures (154.2 s) took 262 s, and at a 2-vsync hold a movie plays at half speed (the half-speed cutscenes of PR seedds#1996). A function-matched cut makes the limit 2, the player's own value for a larger argument. Frame ratio: base 30 is the framework's half rate, two calcs a drawn frame, so each calc takes half the frame's step: step x base / 60 stays (the step alone, PR seedds#1996, would run that mode at twice its speed). The stub counts a half-rate frame's vsyncs once. test_oot_frame_step.py runs both bases and fails on the step alone. Frame time floor: the stub's larger-of-step-and-frame-time was paid back again by the host's debt, 9 to 17% fast with the hold off at frames between one and two vsyncs (t_fstep: 1.132 / 1.166 / 1.088 at 1.2 / 1.5 / 1.8 vsyncs, 1.000 with nvn_fstep_moved). The log's game time counts the floor's share. Co-authored-by: Zard Studios <96824937+Zard-Studios@users.noreply.github.com> Co-authored-by: dsi-hub <260802842+dsi-hub@users.noreply.github.com>
|
Thanks for raising the base-30 case, it sent me into the function. I don't think the ratio is what slowed your cutscenes, so I'd hold off on the The ratio's unit is 1/60 s whatever the base: the time object at What I think you saw is the part of the game that counts calcs instead of time. The movie player takes one picture per calc, and the subtitle and voice timeline advances once per calc too. At a 2-vsync hold the ratio is already 2, but those only move by 1, so cutscenes drag while the music keeps time. On my iPhone Duo the 154 s opening movie took 262 s. The fix I have working is the game's own half rate. #2082 lets the host ask for it while it holds 2 vsyncs, so the game calcs 60 times a second at 30 fps. With that on, the same movie took about 160 s on the Duo. If The fixed |
…e, the stub's branches are computed, and a test runs it at both base rates Base 30 in the frame ratio is the framework's half rate, not a cutscene speed: bit 0 of its +0x24f is the mode and bit 1 flips each pass of its frame function, and with both set the draw and the present return at once while the calc tree runs. The game then calcs twice a drawn frame, the ratio's function among them, so each calc takes half the frame's step: step x base / 60 is right, and the step alone (PR seedds#1996) would run that mode at twice its speed. The ratio's unit is 1/60 s whatever the base (the time object's step in seconds is ratio x 1/60). The stub counted the frame's vsyncs at both calcs, so the host's game time would read twice the real time in that mode. It now counts them at the calc that is drawn. Its branch targets are computed from the words emitted instead of fixed offsets. test_oot_frame_step.py runs the emitted stub under Unicorn (optional, as test_astral_fps.py): both bases at steps 1 to 3, the count, x17 lost after a store, and the game's own division at target 0 and before a step is decided. It fails on main's stub (the count) and on PR seedds#1996's (the ratio). Co-authored-by: Zard Studios <96824937+Zard-Studios@users.noreply.github.com> Co-authored-by: dsi-hub <260802842+dsi-hub@users.noreply.github.com>
… holds 2 vsyncs (DM_FPS_HALF_RATE=1) Some of this game counts calcs, not time: its movie player takes one picture a calc and the timeline that fires its subtitles advances once a calc. The frame ratio moves what reads the time object by the frame's real time, so below 60 fps the two fall apart (issue seedds#1922, the cutscenes of PR seedds#1996). The game's own answer to a frame of two vsyncs is its half rate, two calcs a drawn frame. A stub at its request site (main+0x650ea4 on v131072, placed by the function's fingerprint) ORs the host's ask (state +0x44) into the flag the game reads, and fstep_tick asks while the hold is 2 vsyncs or more. Off by default; refused with a red line when the hold is off, where a half-rate frame of one vsync would calc 120 times a second. On an iPhone Duo (the hold on, the movie network off, a new game): the half rate was asked for when the hold went to 2 at the opening movie, the log's game time read 1.002 of real time, and the movie's 154.2 s took about 160 s. test_oot_frame_step.py runs the request stub under Unicorn.
… takes half the step, and the frame time floor is not paid back twice Movies: AV1 at 60 pictures a second with no sound track, and the game's player took one picture a calc at most (its limit, +0x20, from a creation argument of 1). Below 60 fps a movie fell behind the sound the game plays beside it: on an iPhone Duo at 26-31 ms a frame the opening's 9,254 pictures (154.2 s) took 262 s, and at a 2-vsync hold a movie plays at half speed (the half-speed cutscenes of PR seedds#1996). A function-matched cut makes the limit 2, the player's own value for a larger argument. Frame ratio: base 30 is the framework's half rate, two calcs a drawn frame, so each calc takes half the frame's step: step x base / 60 stays (the step alone, PR seedds#1996, would run that mode at twice its speed). The stub counts a half-rate frame's vsyncs once. test_oot_frame_step.py runs both bases and fails on the step alone. Frame time floor: the stub's larger-of-step-and-frame-time was paid back again by the host's debt, 9 to 17% fast with the hold off at frames between one and two vsyncs (t_fstep: 1.132 / 1.166 / 1.088 at 1.2 / 1.5 / 1.8 vsyncs, 1.000 with nvn_fstep_moved). The log's game time counts the floor's share. Co-authored-by: Zard Studios <96824937+Zard-Studios@users.noreply.github.com> Co-authored-by: dsi-hub <260802842+dsi-hub@users.noreply.github.com> (cherry picked from commit fd62582)
Summary
[x21+0x24f], which selects base rate30.0fins1instead of60.0f. The previous stub calculated(step * base_rate) / 60, resulting in(2 * 30.0) / 60 = 1.0at 30 fps (or0.5at 60 fps). This caused cutscenes and dialog progression to run in half-speed slow motion while audio dialogue played in real time (e.g. 50s awkward silences between narrator phrases). Replacing this withfmov s8, s0sets the ratio directly to(float)stepand adjusts the CBZ forward jump offsets accordingly.present_snap on(matching Breath of the Wild01007EF00011E000) so that swapchain textures are copied immediately into host present snapshots, decoupling the game engine from display link vsync jitter and preventing micro-stutters under the macOS compositor.Verification
0400486024DEC000) on Apple Silicon.test_launcher_settings.pyandtest_patches.py.