Repository navigation
A GPU whose access macOS revoked stops the game instead of leaving it running with nothing drawn - #1332
A GPU whose access macOS revoked stops the game instead of leaving it running with nothing drawn#1332PucaVaz wants to merge 1 commit into
Conversation
8313cb9 to
f45dd67
Compare
f45dd67 to
97b308a
Compare
|
clang-tidy: the two findings in this PR's Drafted with Claude; reviewed by me. |
97b308a to
750bda2
Compare
|
Update after building and playing, as promised: checked in Ocarina of Time on an M4 (24 GB), macOS 27.2. Setup: lunks's 574a38f (the pool-table fix branch) with this PR's change on top, windowed,
One fix from the test, now in the PR (750bda24): when the app was opened in the background the alert stayed behind other apps, and with On the previous head the build check, host tests, sanitizers and iOS app passed (coverage and tool tests were still running); the one clang-tidy finding left is in Drafted with Claude; reviewed by me. |
750bda2 to
7f5f2fc
Compare
…rings upstream main to 7249ee9) into main The translator binaries, its lock and dmsh_chain.h are kept from main here and rebuilt from the merged source after the Ocarina merges.
…pstream's VOTEU, the growing raw pool table): chain c7de80c08992 The merges of seedds#1332 and the pool table commit kept main's translator binaries; this publishes Ryujinx.Graphics.Shader, Ryujinx.Common, Spv.Generator and dmsh_xlate (osx-arm64, framework-dependent, the universal launcher kept) from the merged source, with the lock and dmsh_chain.h written again. RawGlobal's test takes its own storage slot setup instead of AtomgCas's, a class of a Star Fox commit not in this tree. Every test_dmsh_*.py passes against it (DMSH_TEST_XLATE), and t_gmem_raw, t_pool_chunks, t_mesh_indirect, t_deferred_release, t_image_usage, t_image_alias, t_gpu_lost, t_sbw, t_ubp_running and t_unwritten_store pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the game instead of leaving it running with nothing drawn Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g # Conflicts: # dm/gfx/nvn/nvn_mtl.m
…d of freezing the Mac A session of Ocarina of Time ended in a GPU out-of-memory and a stuck game, and seedds#1326 reports a Mac frozen until the power button: GPU memory is wired, IOGPUScheduler's freeToWireMemory fails at recommendedMaxWorkingSetSize, and the submit thread then waits in the kernel where the process cannot be killed. Nothing in the host looked at the working set. res_commit now reads the larger of the set's allocatedSize and the device's currentAllocatedSize before it commits; the set's size leaves out what was never committed (measured: a new 64 MB texture adds 0 to it until the commit), the device's counts it from its creation. From 80% of the working set the submit thread lets go of what can go: idle sharing entries nothing holds, finished deferred releases, spares and the upload slots' staging. From 95%, if that was not enough, the game stops with a [RED] report of what the set holds, by kind. A commit that adds to the set is not made at the working set itself. A 100 ms timer reads the sizes too, as a stuck submit thread never comes back to look. The stop reaches the launcher as a normal exit: the Mac app closes the graphics gate, leaves full screen, shows an alert and quits as Cmd-Q does, the path PucaVaz's PR seedds#1332 takes for a revoked GPU (front_stop_alert is written so front_gpu_lost can call it); without a window the host takes its graceful exit. DM_NVN_RES_LIMIT_MB and the report's distinct pages and texture formats come from JioChoi's PR seedds#1319, moved from after the commit to before it. iOS only evicts unless the switch sets a limit: no iPhone has tried the stop. t_res_guard covers the levels, the commit refused and removals passing, what stays (a live object, the parent of a live sRGB view, a recent lookup), the second look at the stop level, and the timer with the lock held.
…cy commit stops the game instead of freezing the Mac front_mac.m: seedds#1332's front_gpu_lost now goes through this pull request's front_stop_alert; alias_let_go keeps the submission's finalized serials (g_ghost_active); the memory line keeps NXForge's GPU allocation by kind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
Revised brief: keep only PR seedds#1434 (9eeacbb), seedds#1332 (7f5f2fc), seedds#1417 (1c1d453), and seedds#1428 (f537147). Remove all other prior ports and their follow-ups while preserving published history. Other glass and Mac agents own those items. Update the scoped report and references; exclude disabled SDK hooks from hit diagnostics. Python tools match the baseline failure set; 12 docs/environment checks pass. Portable SDK gate and diagnostic checks pass GCC ASan/UBSan. Mac and Metal checks remain open.
… layers, seedds#1345 and seedds#1373 landed) Upstream's c83c48f makes tex_fit's views from layer counts, which covers the cube read as a 2D array, so NXForge's own version of that (f17b3e7) is reverted before this merge. hle_audren.c and its notes take upstream's landed seedds#1345. The failed command buffer report keeps both seedds#1382's segment names (cbn_close) and upstream's shader keys (cbs_feedback), and seedds#1332's gpu_lost_check. The present keeps NXForge's sidebar inset on upstream's present_fit. The translator binaries, lock and chain header are upstream's until the translator is rebuilt on the Mac. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
) replaces the pull request heads Upstream landed Ocarina of Time in main (chain adec7d81d970: "holds 60 fps in Kokiri Forest, 4 GB less") and closed the pull requests it folded in, with reworded and partly different code. Mixing a pull request head with its landed version hunk by hunk silently broke things (two GlobalToStorage tables, a duplicate AF2ip, two mtk_pso definitions), so the rule here is one version per feature: - Pull requests upstream closed (seedds#1299, seedds#1300, seedds#1312, seedds#1343, seedds#1345, seedds#1353, seedds#1354, seedds#1359, seedds#1360, seedds#1373, seedds#1428, seedds#1430, seedds#1434): their hunks are taken out of our side before the three-way merge, so upstream's landed version stands alone. Gone with them: bindless pool table patching (reg_bump), the ghost table's per-submission hold, the growing raw pool table and its write-log sizing, seedds#1428's pool_buf spares (upstream's pool_table_take is the same idea), the ixc-based indirect mesh draw (upstream uses mdx), and their tests (t_pool_table, t_mesh_indirect, t_gmem_raw, finalized_bindless_pool, test_bnsh_nvn2, test_ndve). - Open pull requests that upstream's landing supersedes: seedds#1394 (upstream's mtl_pool_addressed) and seedds#1393 pool_skip_retired (upstream's budgeted pool walk; the profile does not use it) are dropped with their tests and docs. seedds#1368's mesh pipeline prewarm is dropped too: upstream kept the background build in nvn_geometry.inc, and the prewarm's mtk_build / mtk_claim / g_warm_mps went with it. - Kept, as upstream does not have them: NXForge's own features (GPU watchdog, present filters and anti-aliasing, aniso floor, quick settings, pipeline archive compaction, upload breakdown, memory report lines) and the still open pull requests seedds#1304, seedds#1305, seedds#1308, seedds#1320, seedds#1331, seedds#1332, seedds#1355, seedds#1356, seedds#1369, seedds#1376, seedds#1377, seedds#1379, seedds#1382, seedds#1390, seedds#1391, seedds#1392, seedds#1414, seedds#1420, seedds#1436, seedds#1439, seedds#1446, seedds#1453. - Combined where both sides changed the same lines: GW_N 1 << 16 with seedds#1320's wide-write intervals (GW_PAGES = SBW_WPAGES, 128 slots), the CPU upload log with upstream's linear upload route, arch_open_end before the compaction limit, cbn_prog in geo_gs_one (seedds#1382). - Translator: upstream's sources and binaries, plus seedds#1414's InstEmitAmpereMem diff and the LDSM SubgroupBarrier in InstEmitAmpereTensor.cs. The binaries and dmsh_chain.lock are upstream's until the Mac rebuilds them (dotnet publish, tool_lock --update-lock, dmsh_chain --header); dmsh_chain.h is regenerated for now. - RED_ONCE keys: upstream took 153 and 154, so the present anti-aliasing lines move to 156 and 157 and seedds#1420's packaged-entry line to 158 (array 159). - Host: hle_net.c and t_net_eventfd.c are upstream's (its eventfd poll waits out the whole timeout, which our 5d7920c fixed another way). - Kept ours: dm/tools/romfs_image.py and its test, so the 26 GB RomFS image is not packed again. dm/tools tests on Linux: same failures as on 875449e (Linux-only quirks), plus test_nlg4 and test_dmsh_pool_arrays, which fail on upstream main too. Not yet built on the Mac. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
…er and shader chain, the branch's host services, movie clock, packing and Ocarina of Time additions Upstream reached about 60 fps in Kokiri Forest with its own versions of most of the branch's merged PRs, so where both changed the same code upstream's is kept: - The shader chain is upstream's (translator, lock, dmsh tools, Ryujinx sources), plus the branch's bracket-depth flattening (dmsh_depth.py, test_dmsh_depth.py); chain c732699d4fdb. Not carried: the LDSM subgroup fence (seedds#1376, needs a translator rebuild; upstream's HMMA/LDSM rework reports clean movies) and the shared raw-memory lookups (seedds#1405, superseded by upstream's last-entry lookups). - The renderer (dm/gfx/nvn, dm/gfx/msl) and its tests are upstream's. Not carried: seedds#1390-seedds#1394, seedds#1404, seedds#1408, seedds#1409, seedds#1417, seedds#1418, seedds#1420, seedds#1427, seedds#1320, seedds#1331, seedds#1332, seedds#1368, seedds#1285 renderer parts, pool_skip_retired, and the branch's warning fixes and prewarm progress there. They stay on oot-integration. - Ocarina of Time's profile is upstream's, with the branch's 12 movie compute rows in pso_manifest.tsv, PLAN.md and PR-AUDIT.md. - shader_split.py is upstream's plus the BAHS record check (test_split_bahs_records.py). - Host: upstream's EventFd, PlayReport send, audio lock and vibration answers; the branch's service sessions (hle_sf, hle_prepo), sync waits, AV1 movie clock and parallel decode, diag_abort frame fix and focus control are kept. - Scripts: shader.noindex, tmp.noindex, notes outside the shader key, bench and regress changes. Checked: the Mac host builds (GAME=0400486024DEC000); host unit tests pass; the Python tool tests pass on Python 3.13 except test_dmsh_pool_arrays, test_nro_pack, test_nsp_dump and test_nsp_incremental, which fail on upstream main as well. Not run: the full shader build on this chain, gameplay.
… running with nothing drawn In issue seedds#1326 (Ocarina of Time on a Mac), a GPU hang in full screen and, in a second run, GPU page faults in a window were followed by every upload, segment, submission and display command buffer coming back MTL4CommandQueueErrorAccessRevoked ("Ignored (for causing prior/excessive GPU errors)", kIOGPUCommandBufferCallbackErrorSubmissionsIgnored). The host logged them and went on. In the windowed run nothing was drawn while the game's sound and logic kept running for about 70 s, until the game aborted itself. In full screen, WindowServer froze for 40 s and its watchdog killed it. macOS revokes a process's GPU access after GPU errors it caused, and from then on runs none of its command buffers. Their feedback still comes, at once, but the GPU no longer signals the shared event a game's SyncWait waits on, and every present takes a drawable that nothing draws. Metal 4 reports a commit's error as MTL4CommandQueueErrorTimeout with no text, with the command buffer's own error under NSUnderlyingErrorKey and NSMultipleUnderlyingErrorsKey; every logged error in both seedds#1326 logs has that shape. gpu_lost_check looks for the revoked access in each command buffer's error and the errors under it, in MTL4CommandQueueErrorDomain only (an innocent victim's IOGPU error has code 5 too). The first one: - stops presents; - raises the shared event from the CPU to the last fence whose feedback has come back, and again at each later submission's feedback, so g_done still follows Metal's feedback and no command allocator is reset before Metal hands its command buffer back; - calls front_gpu_lost (front.h) once. The Mac app leaves full screen, says why in an alert that points to the log and scripts/bug_report.py, and quits as ⌘Q does. Without a window, and on the iPhone (the weak default in nvn_mtl.m), the run ends with a FATAL line and an abort. A hang or a fault alone does not stop the game. The second run recovered from several innocent-victim timeouts, then page-faulted at fences 110102 and 110108, and its command buffers were ignored from fence 110114, two frames later. DM_TEST_GPU_LOST=<n> imitates the revoked access from the nth submission on. Checked on an M4 (24 GB), macOS 27.2, Xcode 27.2: - t_gpu_lost, a new test: the error's shape, one call to the front end, SyncWaits on revoked fences returning at their feedback, and presents stopped. It fails with the event release taken out, and with only the top-level code compared. - make -C dm/gfx/tests GAME=010015100B514000 run. - The docs and switch-table tests. No real revoked access was reproduced, and regress.py was not run: the change reaches only error paths, and one early return in present_now once the access is revoked.
7f5f2fc to
f30f804
Compare
|
Rebased onto main Checked on an M4 (24 GB), macOS 27.2: The reports since show the state this PR handles:
This PR ends the game at the first of those ignored command buffers. It does not fix the hangs and faults that lead there, and the reports don't show whether stopping earlier avoids every later Mac freeze or panic. #1391's |
… running with nothing drawn (PR #1332) In issue #1326 (Ocarina of Time on a Mac), a GPU hang in full screen and, in a second run, GPU page faults in a window were followed by every upload, segment, submission and display command buffer coming back MTL4CommandQueueErrorAccessRevoked ("Ignored (for causing prior/excessive GPU errors)", kIOGPUCommandBufferCallbackErrorSubmissionsIgnored). The host logged them and went on. In the windowed run nothing was drawn while the game's sound and logic kept running for about 70 s, until the game aborted itself. In full screen, WindowServer froze for 40 s and its watchdog killed it. Reports in #1265 and #1326 show the same state ending in a frozen Mac or a kernel panic. macOS revokes a process's GPU access after GPU errors it caused, and from then on runs none of its command buffers. Their feedback still comes, at once, but the GPU no longer signals the shared event a game's SyncWait waits on, and every present takes a drawable that nothing draws. Metal 4 reports a commit's error as MTL4CommandQueueErrorTimeout with no text, with the command buffer's own error under NSUnderlyingErrorKey and NSMultipleUnderlyingErrorsKey. gpu_lost_check looks for the revoked access in each command buffer's error and the errors under it, in MTL4CommandQueueErrorDomain only (an innocent victim's IOGPU error has code 5 too). The first one: - stops presents; - raises the shared event from the CPU to the last fence whose feedback has come back, and again at each later submission's feedback, so g_done still follows Metal's feedback and no command allocator is reset before Metal hands its command buffer back; - calls front_gpu_lost (front.h) once. The Mac app leaves full screen, says why in an alert that points to the log and scripts/bug_report.py, and quits with exit status 70, so a launcher or script sees a failed run (⌘Q's is 0). Without a window, and on the iPhone (the weak default in nvn_mtl.m), the run ends with a FATAL line and an abort. A hang or a fault alone does not stop the game: the GPU recovers from those. DM_TEST_GPU_LOST=<n> imitates the revoked access from the nth submission on; t_gpu_lost checks the error's shape, the single call, the released waits and the stopped presents. The hangs and faults that lead to the revoked access stay open. Checked on an M4 Pro: t_gpu_lost 14 checks, 0 failed (also with DM_NVN_ENC_THREAD=1); the Mac host builds; the docs and switch-table tests pass. No real revoked access was reproduced here; a reporter's run with the PR (#1326) showed the alert and a clean quit. regress.py was not run: the new code runs only on command buffer errors, plus one flag test per present. Refs #1326 Co-authored-by: Puca Vaz <65235669+PucaVaz@users.noreply.github.com>
|
Landed as 5636e7a with you as co-author. Thank you, this is good work: the error shape under the top-level timeout, raising the event at feedback rather than at commit, and the test. One change: after the alert the Mac app now quits with exit status 70 instead of 0 ( Checked on an M4 Pro: |
…ir storage by GPU-written range, PR seedds#1604; a revoked GPU stops the game, PR seedds#1332) into switch2 The fork's own GPU-written range tracking (DM_NVN_GPUW_RANGE) gives way to upstream's gws_* spans, which do the same for Ocarina's terrain; the NDVE scale marks its target through mtl_tex_gpu_written. front_gpu_lost uses the fork's front_stop_alert beside front_gpu_memory_stop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013SzykpcKm2eZPvPjGPKx1C
… running with nothing drawn (PR seedds#1332) In issue seedds#1326 (Ocarina of Time on a Mac), a GPU hang in full screen and, in a second run, GPU page faults in a window were followed by every upload, segment, submission and display command buffer coming back MTL4CommandQueueErrorAccessRevoked ("Ignored (for causing prior/excessive GPU errors)", kIOGPUCommandBufferCallbackErrorSubmissionsIgnored). The host logged them and went on. In the windowed run nothing was drawn while the game's sound and logic kept running for about 70 s, until the game aborted itself. In full screen, WindowServer froze for 40 s and its watchdog killed it. Reports in seedds#1265 and seedds#1326 show the same state ending in a frozen Mac or a kernel panic. macOS revokes a process's GPU access after GPU errors it caused, and from then on runs none of its command buffers. Their feedback still comes, at once, but the GPU no longer signals the shared event a game's SyncWait waits on, and every present takes a drawable that nothing draws. Metal 4 reports a commit's error as MTL4CommandQueueErrorTimeout with no text, with the command buffer's own error under NSUnderlyingErrorKey and NSMultipleUnderlyingErrorsKey. gpu_lost_check looks for the revoked access in each command buffer's error and the errors under it, in MTL4CommandQueueErrorDomain only (an innocent victim's IOGPU error has code 5 too). The first one: - stops presents; - raises the shared event from the CPU to the last fence whose feedback has come back, and again at each later submission's feedback, so g_done still follows Metal's feedback and no command allocator is reset before Metal hands its command buffer back; - calls front_gpu_lost (front.h) once. The Mac app leaves full screen, says why in an alert that points to the log and scripts/bug_report.py, and quits with exit status 70, so a launcher or script sees a failed run (⌘Q's is 0). Without a window, and on the iPhone (the weak default in nvn_mtl.m), the run ends with a FATAL line and an abort. A hang or a fault alone does not stop the game: the GPU recovers from those. DM_TEST_GPU_LOST=<n> imitates the revoked access from the nth submission on; t_gpu_lost checks the error's shape, the single call, the released waits and the stopped presents. The hangs and faults that lead to the revoked access stay open. Checked on an M4 Pro: t_gpu_lost 14 checks, 0 failed (also with DM_NVN_ENC_THREAD=1); the Mac host builds; the docs and switch-table tests pass. No real revoked access was reproduced here; a reporter's run with the PR (seedds#1326) showed the alert and a clean quit. regress.py was not run: the new code runs only on command buffer errors, plus one flag test per present. Refs seedds#1326 Co-authored-by: Puca Vaz <65235669+PucaVaz@users.noreply.github.com>
…d of freezing the Mac A session of Ocarina of Time ended in a GPU out-of-memory and a stuck game, and seedds#1326 reports a Mac frozen until the power button: GPU memory is wired, IOGPUScheduler's freeToWireMemory fails at recommendedMaxWorkingSetSize, and the submit thread then waits in the kernel where the process cannot be killed. Nothing in the host looked at the working set. res_commit now reads the larger of the set's allocatedSize and the device's currentAllocatedSize before it commits; the set's size leaves out what was never committed (measured: a new 64 MB texture adds 0 to it until the commit), the device's counts it from its creation. From 80% of the working set the submit thread lets go of what can go: idle sharing entries nothing holds, finished deferred releases, spares and the upload slots' staging. From 95%, if that was not enough, the game stops with a [RED] report of what the set holds, by kind. A commit that adds to the set is not made at the working set itself. A 100 ms timer reads the sizes too, as a stuck submit thread never comes back to look. The stop reaches the launcher as a normal exit: the Mac app closes the graphics gate, leaves full screen, shows an alert and quits as Cmd-Q does, the path PucaVaz's PR seedds#1332 takes for a revoked GPU (front_stop_alert is written so front_gpu_lost can call it); without a window the host takes its graceful exit. DM_NVN_RES_LIMIT_MB and the report's distinct pages and texture formats come from JioChoi's PR seedds#1319, moved from after the commit to before it. iOS only evicts unless the switch sets a limit: no iPhone has tried the stop. t_res_guard covers the levels, the commit refused and removals passing, what stays (a live object, the parent of a live sRGB view, a recent lookup), the second look at the stop level, and the timer with the lock held.
Checked in the game (Ocarina of Time, M4 24 GB, macOS 27.2; details in the update comment): the revoke line once, the picture stops while the game keeps running, the alert, and Quit exits with 0. Rebased on
865cc35f.Refs #1326
What and why
In #1326 (The Legend of Zelda: Ocarina of Time,
0400486024DEC000v131072, on a Mac), a GPU hang (run 1, full screen) or GPU page faults (run 2, windowed) were followed by every later upload, segment, submission and display command buffer coming backMTL4CommandQueueErrorAccessRevoked("Ignored (for causing prior/excessive GPU errors)",kIOGPUCommandBufferCallbackErrorSubmissionsIgnored). The host logged them and went on:Cause. macOS revokes a process's GPU access after GPU errors it caused, and from then on runs none of its command buffers. They come back at once with that error, so their feedback still arrives. But the GPU no longer signals the shared event that a game's SyncWait waits on (for up to an hour,
mtl_wait_at), and every present takes a drawable that nothing draws.The change (
nvn_mtl.m,front.h,front_mac.m).gpu_lost_checklooks for the revoked access in each command buffer's error. The first one:present_now);gpu_lost_release), so no SyncWait waits for a signal that will not come;front_gpu_lost). The Mac app leaves full screen, says why in an alert that points to the log andscripts/bug_report.py, and quits as ⌘Q does. Without a window, the run ends with a FATAL line and an abort (the weak default innvn_mtl.m).Why the access being revoked, and not the first timeout or page fault. The two #1326 logs show that the GPU recovers from those on its own:
Stopping at the first fault would have ended run 2 at 219 s while it was still playable. (My first comment on #1326 said "timeout, page fault or access-revoked"; this is the narrower trigger.)
Two changes from the first commit of this PR:
MTL4CommandQueueErrorTimeoutwith no text at the top, and the command buffer's own error underNSUnderlyingErrorKey/NSMultipleUnderlyingErrorsKey. Every logged command buffer error in both Ocarina of Time (0400486024DEC000 v131072, Mac): GPU hang about 10 minutes into play takes WindowServer down #1326 logs has that shape. The check walks those, inMTL4CommandQueueErrorDomainonly: an innocent victim's IOGPU error has code 5 too.g_fencewhen a submission was committed. A SyncWait could then mark a fence done (post_earlymovesg_done) before Metal had handed the command buffer back. The next submission could then reset a command allocator Metal still held, becausemtl_sub_beginreuses allocators whose fence is at or belowg_done. Raising it tog_doneat each feedback keepsg_donetied to Metal's feedback.iPhone. The weak default stays (a FATAL line in
log.txtand an abort). The app has no quit of its own to follow, and I cannot try it on a device. Before this, the game ran on with nothing drawn.Not covered. The Vulkan and OpenGL layers keep their own queues and waits. Their presents go through
present_now, though, so a revoked display command buffer still tells the front end.This complements #1275's GPU hang defences rather than replacing them. Its watchdog fires when a fence has not completed after 4 s. Here the ignored command buffers complete at once, with an error, so it never fires.
Rendering reference entry. The game note
dm/games/0400486024DEC000/README.mdis not on main (the profile is in #1299), so the entry links the issue, and the note gets no "Found on this game" line here.Checked
make -C dm/gfx/tests GAME=010015100B514000 t_gpu_lost && MTL_DEBUG_LAYER=1 ./t_gpu_lost: 14 checks, 0 failed, five times, and three more withDM_NVN_ENC_THREAD=1. It checks:front_gpu_lostis called exactly once over four revoked submissions;With the event release taken out, the four SyncWait checks fail (each waits out its 2 s timeout). With the first commit's top-level-only check, 7 of 14 fail.
make -C dm/gfx/tests GAME=010015100B514000 -j4 run: every test passed (exit 0).t_reflskipped 2 checks for sample files that are not on this Mac. This ran before the rebase onto7249ee9f, which changes only tools and docs.t_gpu_lostwas built and run again after it.python3 -m unittest dm/tools/tests/test_docs.py dm/tools/tests/test_env_switches.py: 12 tests OK;python3 dm/tools/env_switches.py --check: OK.front_mac.mcompiled with the host's flags (the strongfront_gpu_lostis in its object). The full host was not built, and clang-tidy ran in CI: the two findings int_gpu_lost.mare fixed; the one left is int_int_float_view.mfrom main (fixed by Tests check the Metal device, textures, encoders and functions they make before using them #1338).0400486024DEC000, v131072), on lunks's 574a38f with this change, windowed, withDM_TEST_GPU_LOST=1500(the imitation; no real revoked access was reproduced). In two runs the[RED] GPU access revokedline came once (at 15.3 s and 8.2 s), the picture stopped while the game kept running at its normal frame times, and the alert came; Quit exited with 0. The alert was hidden behind a background-opened orDM_WINDOW_TOPgame window; 750bda24 activates the app and raises the alert above it (compiled; not yet re-run in the game). Leaving full screen was not exercised: both runs were windowed.scripts/regress.py check --games: not run. My only local game build is busy, and the change's reach is narrow: the new code runs only on a command buffer error, plus one early return inpresent_nowonce the access is revoked. On a run without a revoked access, the new work is a flag test per submission and, on a command buffer error, a walk of the errors under it.Front ends
No
Front-ends:line. After the alert, a Mac app exits with 0, as for ⌘Q. Before, a run like this ended in the game's own abort (the launcher returned 134) or in WindowServer's kill.docs/frontends.mddocuments no exit status for a game run. If a non-zero status would be better for launchers (#1275's watchdog uses 70), that is a small change.Docs
docs/reference/rendering.mdentry, andpython3 dm/tools/docs_symptoms.pywas run (the symptom index)DM_TEST_GPU_LOSTis indocs/env_switches.tsvDrafted with Claude; reviewed by me.
🤖 Generated with Claude Code