Skip to content

Ocarina of Time: GPU time per frame triples after a big scene change (leaving the Deku Tree) and stays up until restart (M2 Pro 16 GB) #2277

Open

Description

@GeorgeKKR

What happened

After a large scene change, the GPU time per frame jumps about 3–4× and stays there until the game is restarted. In the meantime the game runs at 5–10 fps and plays in slow motion (game time 0.88 of real time). Restarting and loading the same place brings it back to normal right away, at the same spot.

Seen twice, in two separate sessions:

  1. Leaving the Great Deku Tree after Queen Gohma (this report's log): the frame before held a steady 30 fps, then went to 7–11 fps.
  2. Walking into the Great Deku Tree (an earlier session on an older build): GPU 23 → 67 ms, submit+present 10 → 160 ms, 4–5 fps, with a 16 s frame. After a restart, the GPU time inside the Deku Tree was 21–22 ms.

From the log, across the transition (300-frame windows):

frames up to frame p50 / p95 GPU p50 / p95 submit+present p50 dropped footprint
31200 (before) 33.3 / 33.5 ms 24.3 / 26.0 ms 7.4 ms 0 3.95 GB
31500 (the scene change) 33.5 / 161.9 ms 25.7 / 60.9 ms 7.1 ms 105 6.14 GB
31800 (after) 143.0 / 175.1 ms 81.2 / 102.6 ms 7.5 ms 298 5.99 GB
~40800 (still after, minutes later) 147.1 / 163.7 ms 92.5 / 106.4 ms — — —

Notes from the same log:

  • The CPU side stays normal: translation CPU 4–7.5 ms a frame, and GPU waits so far 0 in the translation detail before and after. It is the GPU's own time per frame that grows.

  • The extra GPU time does not follow the draw count, which looks like a per-frame cost that grew:

    frames up to draws / frame GPU p50
    31500 (before) 882 25.7 ms
    31800 1,113 81.2 ms
    32100 682 78.0 ms
    32400–33000 (a screen with almost nothing drawn, probably a menu or loading screen) 4–5 12.2 ms
    40500 386 53.8 ms
    40800 1,159 92.5 ms

    For comparison, the boot screens took about 0.6 ms of GPU a frame in an earlier session on this Mac (an older build).

  • In the scene-change window, the residency set added 4,458 allocations / 3,663 MB (commits 1520 / 348.5 ms). The window after it added 1,718 allocations / 545 MB, with 6,842 in the set. GPU allocated stayed at about 7.1–7.4 GB, resident about 6.0–6.1 GB.

  • OptimizeChangeBanc was the busiest guest thread in the scene-change window (59 ms/s). In the window after, it was no longer at the top.

  • Swap was about 3 GB, and the game's footprint about 6 GB. Thermal state stayed normal (thermal -1), on AC power.

  • No GPU hang or [RED] line at the transition.

A similar case was posted in discussion #1789: an M5 (10-core GPU, 16 GB) with DM_GUEST_FILE=1, where the submit thread waited for the GPU about 4× as often per frame after entering Castle Town Market, and stayed at 15–20 fps until a restart.

What you expected

The GPU time per frame to return to about 21–25 ms after the scene change, as it is after a restart in the same place.

Game, title ID and version

The Legend of Zelda: Ocarina of Time, 0400486024DEC000, update v131072, from a .nup. No DLC.

nxnative commit

fd8188c9 (as pinned by switch2mac), built as fd8188c94a53+dirty. The local changes:

  • switch2mac's front end (overlay and settings).
  • A local 40 fps frame step: state page +0x38 holds the step's unit, and DM_FPS_TARGET=40 holds 3 ticks of 1/120 s on a 120 Hz link. It only touches the frame step and present pacing.

The earlier occurrence (2. above) was on f7a54525 with only switch2mac's front end, at DM_FPS_TARGET=60, so it is not the 40 fps change.

Shader chain version

cb2e91c138e3

How it was built and run

switch2mac (azmasm/switch2mac) build --engine nxnative from the .nup, which runs nxnative's build_ipa_from_nsp.py --platform mac-app. All 42,848 shaders built, with a bundled pipeline archive for applegpu_g14s. App and game image on an external USB SSD (10 Gb/s).

Device and OS

MacBook Pro 14" (Mac14,9), Apple M2 Pro, 10-core CPU, 16-core GPU, 16 GB, macOS 27.2, built-in ProMotion screen.

Settings and environment overrides

env.txt:

DM_FPS_TARGET=40
DM_GUEST_FILE=1

Earlier occurrence: DM_FPS_TARGET=60, DM_FPS_HOLD=off, DM_GUEST_FILE=1.

Logs

Excerpts above; the full log can be attached if useful.

No game files

  • Nothing attached here contains game files, keys or data extracted from a game.

Activity

  1. qq3229311585-jpg commented on Nov 11, 2026

    @qq3229311585-jpg

    Another data point from a 16 GB fanless MacBook Air (M5, macOS 27.2), Ocarina of Time v131072, commit 01ec314 (chain b8856126d238, so the allocator-pool fix fd8188c9 is in), DM_FPS_TARGET=30, DM_GUEST_FILE=1. It looks like the same "GPU time per frame jumps and stays up until restart" state, with these differences from the report above: it starts about 40–80 s after the first 3D frame in the Kokiri plain (not after a scene change), the GPU time is about 1.5× (24 → 36 ms at the same spot), and it does not follow the draw count, but it goes away by itself in light scenes. It does not happen on every launch: one launch ran 70 minutes at a flat 33.3 ms (the only slow stretches matched macOS thermal pressure 0→1→2 and recovered when it fell), the next launch of the same app went slow after 40 s and stayed there.

    What powermetrics --samplers gpu_power,cpu_power,thermal shows in the slow launch (2 s samples, AC power, thermal pressure Nominal all the time):

    good (first 35 s of 3D) slow
    GPU HW active frequency ~1510 MHz ~890–1050 MHz
    GPU HW active residency 56–74 % ~100 %
    GPU Power 5.9–7.4 W ~1.7 W
    CPU Power 0.6–2 W 7–10 W (all four P cores at ~4.27 GHz)
    frame time p50 / GPU span p50 33.3 / 18–25 ms 52–65 / 32–41 ms

    So in the slow state the GPU is 100 % busy but is only given about 950 MHz and 1.7 W, while CPU+GPU together stay at about 10 W in both states. With DM_NVN_WAITS=1, the game's main thread blocks about 42–54 ms per frame in SyncWait on the GPU in the slow state (0.12 ms in the good state), and GPU waits so far stays 0, so it is the GPU itself that is slow, not the submission side.

    Things tried that did not fix it, in case they save someone time:

    • A small offscreen Metal compute load (12 % and 25 % duty, 40 s each) to "kick" the GPU clock: no change in frame time, GPU stayed ~900–1300 MHz.
    • DM_RT=0: still slow after ~1.5 min.
    • DM_QOS_MAP=1: the slow state came after about 12 minutes instead of 1–2, then the same pattern (GPU ~750–900 MHz at ~98 % busy, 1.4 W). More frames with a long tail, too.
    • Game Mode (off in Info.plist, no "Game mode enabled" in gamepolicyd), low power mode (off), memory/disk (resident 5.8–6.9 GB, 20 GB free) were ruled out.

    It reads to me like macOS power management on a fanless machine rather than something the game does, but I could not show that. I can attach the logs and the powermetrics files (no game files in them) if useful; my notes and small scripts are in https://github.com/qq3229311585-jpg/oot2-mac-notes.

  2. qq3229311585-jpg commented on Nov 12, 2026

    @qq3229311585-jpg

    Correction and more data for my comment above (M5 Air 16 GB, 01ec314, DM_FPS_TARGET=30).

    1. My "good (first 35 s of 3D)" column was the title and file-select screens (2–30 draws a frame), in fullscreen on the 60 Hz link. In healthy gameplay the GPU also sits at 730–1140 MHz (P4–P8), 75–90 % active, 2–4 W, with 33.3 ms frames. So the low clock is not the fault. Please disregard the power-management reading in my first comment.
    2. The slow launches are slow from the first or second 300-frame window after loading into gameplay (frame p50 57–61 ms), not after a while.
    3. In the slow state a 5 s sample of dmhost has com.Metal4.SubmissionQueue active in 792 of 894 samples, 791 of them in iokit_user_client_trap. That looks like the bad state described in frame-rate.md ("The GPU idles with the next command buffer already committed").
    4. powermetrics shows the GPU "100 % active" in that state, but at the same clock it draws about 0.6× the power per active time of healthy gameplay (e.g. around 900 MHz: ~2.0 W against ~2.8 W). An offscreen compute load that pushed the clock to P12 for 10 s changed nothing (4.4 W at "100 % active", where healthy work at P12 is about 10 W).
    5. At a 30 fps cap kmax is 2 (hle_nvn.c, fstep_tick), so a run that falls into this state has no way out; it plays in slow motion until restart. The two slow-from-the-start launches whose logs I checked were in fullscreen (log: "fullscreen, fixed refresh"). One launch with DM_OPMODE=0 ran ~25 minutes with every 300-frame window at 33.3 ms; that is one run, so too little to conclude.

    I can attach the logs and the powermetrics files (no game files in them) if useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggame: 0400486024DEC000A gamemacThe Mac front end and app (dm/mac, front_mac.m)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions