Repository navigation
Ocarina of Time: a patch of scenery no longer draws dark for one frame when the game gives it a sharper texture (a pool texture the walk does not make holds a stand-in of its own colours until its first read) - #2075
sleepyeyedjoe wants to merge 3 commits into
Conversation
… 4-5 frames apart, when the game streams new textures into them: a texture ID a shader has read keeps its read mark when it gets another texture (DM_NVN_POOL_SEEN_KEEP) The models' shaders read their textures through the bindless pool table. When a walk writes an ID's entry it makes the ID's Metal texture if it fits the walk's room (pool_room, within pool_budget()), or, whatever the room, if a shader has read the ID (pool_seen, under POOL_REQ_BYTES); otherwise the entry is the dummy (black) and pool_seen_scan makes the texture after the first read. In Ocarina of Time the room is always 0: pool_budget() is a twentieth of Metal's working set (909 MB on a 24 GB M4 Pro) against 15-18 GB of currentAllocatedSize, so only read IDs are made ahead. mtl_reg_tex cleared the read mark whenever the game registered another texture into an ID. The game's streaming replaces the 17 textures of the castle town wall's material set at once (3-7 MB each), finalizing the old texture first, and registers each step twice, 4-5 frames apart. With the marks cleared the replacements counted as unread, and each registration's first read found the dummy: the wall, the castle, rocks and trees drew black for one frame, twice, with the fog in front. Now an ID that held a made texture (retired by the game, or still viewed; the finalize drops the view before the new registration arrives) keeps its mark, so the next walk makes the replacement under the read budget. An ID that held none, or is emptied, is cleared as before; DM_NVN_POOL_SEEN_KEEP=0 restores the old clearing. A texture streamed into an ID no shader has read yet still reads the dummy once, a single dark frame (issue seedds#1969). Replays of a player's route past the wall (M4 Pro, macOS 27.0.1), every flagged frame checked by picture: 4-5 dark frames flagged per run before (2-4 at the wall's streaming steps), 0 in 5 runs with the change and 0 in 5 runs under a stress that makes streaming steps collide; with DM_NVN_POOL_SEEN_KEEP=0 the pairs return. Frame intervals and GPU time unchanged. Another user saw the same pairs on an M5 Max and none with a change of the same kind (issue seedds#1969). t_pool_chunks: its check that an ID given another texture is unread now checks the new rule (kept for a made texture, cleared under DM_NVN_POOL_SEEN_KEEP=0), and that an emptied ID and an ID given its first texture are unread. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Exercise a historically sampled ID with no ordinary pool-walk room, both when replacing a live texture and after Finalize drops its view. Check the first table's replacement GPU pixels, old table entry stability, unread and empty IDs, unwritten targets, and the clearing control. Exhaust the existing budget counter rather than allocating a large working set. The fixture passes 38/38 with the fix in PR2022 and 36/36 with DM_NVN_POOL_SEEN_KEEP=0. It uses synthetic 1 MiB storage and requires no game assets. These direct-backend configuration variants do not independently stress replay-worker concurrency. From akratch's commit 6f251e7, offered on PR 2022. Rerun on an M4 Pro (macOS 27.0.1) on this branch: 38/38, 38/38 and 36/36. Against nvn_mtl.m from before the fix it fails 6 of 38, the six replacement checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e when the game gives it a sharper texture: a pool texture the walk does not make holds a stand-in of its own colours until its first read (DM_NVN_POOL_STANDIN, issue seedds#1969) The surface's shader reads its textures through the bindless pool table. Once pool_budget() is spent a walk makes only the textures a shader has read (pool_seen), so an ID no shader has read held the dummy, which reads 0; the first read sets the ID's bit and pool_seen_scan makes the texture for the next frame. Ocarina of Time keeps a texture set of its own for each piece of distant scenery and resolution (one level each: a BC5 texture, and two three-layer arrays, BC6H and BC7, of half its size) and registers a new set 12 to 969 frames before the material names it, so the first frame of each such step read the dummy: the cliff beside the castle town wall dark for one frame. Which registrations a shader will read is not known ahead: in 58 s of play the game registered 14,455 textures that stayed unread, 9.8 GB, against 10 first reads, and making textures ahead by age and size cost 0.7-1.9 GB for one hit in three. Now a texture the walk leaves out for good holds a stand-in in the tables until its first read (pool_stand): its own smallest levels, from the first of at most 1 KB on, or, when it has no level that small (the sets above have one level), its last level cut down to at most 4 KB a layer, every n-th block of every n-th block row (nvn_deswizzle_pick; the blocks stay as they are, so compressed formats need no decoder). The host reads them from the storage on the CPU into a small shared texture cut from a heap that is resident from the start, so making one takes no upload batch and no residency commit, and a table may name it at once. The first read then samples the texture's own colours, coarse, for the one frame before the texture is there. 2D textures and 2D arrays of up to 8 layers get one, with the type, layers, format and channel swizzle of the view tex_by_id makes for the ID (the swizzle rule moves to treg_swizzle, which both use); other types, textures in sparse pools, depth formats and render targets no pass has written hold the dummy as before. Only once the budget is full: below it a later walk makes the texture, and a table walked then is walked once more when the budget has filled. At most 512 are made a frame, as an area load registers thousands at once (Hyrule Field: 5,152, 82 ms in one frame without the limit); the rest wait and are written into the tables in place, as a newly read ID's texture is. DM_NVN_POOL_STANDIN=0 keeps the dummy. DM_NVN_POOL_FIRST=1 logs each first read of a texture not made yet and what it found; the totals go to the log every 300 frames. Replays of a player's route past the cliff (M4 Pro, macOS 27.0.1), 10 with DM_NVN_POOL_STANDIN=0 and 8 without. After the area load, 31 first reads of a texture not made found the dummy in the 10 runs and none in the 8, where 49 found a stand-in (none made from storage the game wrote afterwards). On a 32x18 luminance grid of every present the cliff's first read is a connected group of 9-12 cells at 0.70-0.79 of the presents before and after in each of the 4 baseline runs in which its set streamed, and at most 2 cells, the grid's noise (a group of 4 or more in 7 of 17,994 presents elsewhere), in the 5 runs with stand-ins; by picture the cliff is dark for one frame in the one and a little soft in the other. At the area load 39 first reads a run found the dummy, and 5-11 with stand-ins (the frame's share). A run makes about 57,000 stand-ins in 355 ms (6 us each) and holds about 6,000 at a time, in 16 MB of heaps. Medians without and with: resident 10,830 and 10,883 MB (runs 10,725-11,536 and 10,726-11,398), GPU allocated 16,376 and 15,965 MB, residency set 6,465 and 6,491 objects, frame time p50 16.67 and 16.66 ms, p95 16.85 and 17.19 ms (runs of both kinds between 16.8 and 19.4; 3 of 10 and 4 of 8 above 17.5), submit+present p50 6.38 and 6.51 ms. No new red lines (the known "texture type mismatch ... texture ID 850" in 6 of the 10 runs and 2 of the 8). Not yet checked in live play, and scripts/regress.py was not run (no other game's build on this Mac). Tests: t_pool_standin (which registrations get a stand-in and what it holds, read back from the GPU: levels as they are, a level cut down, a compressed texture, an sRGB view with a swizzle, a 2D array and a layer of it; the first read; another registration; an emptied ID; a frame's share and the rest written in place; with DM_NVN_POOL_STANDIN=0 the dummy), t_tex P (the picked blocks against the level decoded whole, in both GOB orders). t_pool_notes and t_pool_chunks: where they checked that an unread texture past the budget is the dummy, they now check that it is not made (its stand-in, or the dummy with the switch off). Passing: those, t_pool_seen_replace, t_cpu_tex, t_bc_alias, t_defer_chain, t_image_usage, t_image_type, t_view_swizzle, t_stencil_view, t_pfv, t_int_float_view, t_readback; docs_symptoms.py --check, test_docs.py, test_env_switches.py. Co-Authored-By: Claude <noreply@anthropic.com>
|
I tested this PR (with #2022, which it includes) on a MacBook Air M5 (10-core GPU, 16 GB, macOS 27.2). It was applied to
The check was by eye, and the session after was short; it was not a frame-by-frame comparison. |
seedds#2030, seedds#2077, seedds#2022, seedds#2075, seedds#2085), last merged at 04811a6, and the Castle Town result on a MacBook Air M4 Castle Town on a 16 GB MacBook Air M4 at 30 fps and full resolution: GPU p50 26-27 ms a frame and 6.9 GB of GPU memory allocated with seedds#2085, against 16.2 GB and about 9 fps before it. Tests: dm/tools/tests/test_docs.py passes.
…tch of scenery no longer draws dark for one frame when the game gives it a sharper texture (a pool texture the walk does not make Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…s until its first read: Ocarina of Time's cliff beside the castle town wall, and other streamed scenery, no longer draws dark for one frame when the game gives it a sharper texture (PR #2075) Once pool_budget() is spent a walk makes only the textures a shader has read (pool_seen), and an ID no shader has read held the dummy, which reads 0: the first read set its bit and pool_seen_scan made the texture for the next frame, so that frame was dark. Ocarina of Time keeps a texture set of its own for each piece of distant scenery and resolution (one level each: a BC5 texture and two three-layer BC6H / BC7 arrays) and registers a new set 12 to 969 frames before the material names it. Which registrations will be read cannot be known ahead (14,455 stayed unread in 58 s of play, 9.8 GB, against 10 first reads), and making them all is what lazy making exists to avoid, so the miss frame stays; this changes what it reads. The entry holds a stand-in (pool_stand): the texture's own smallest levels, from the first of at most 1 KB on, or its last level cut down to at most 4 KB a layer, every n-th block of every n-th block row (nvn_deswizzle_pick; blocks as they are, so no decoder). It is read from the storage on the CPU into a small shared texture cut from a heap resident from the start, for 2D textures and 2D arrays of up to 8 layers with the view's type, layers, format and swizzle (the swizzle rule moves to treg_swizzle, which tex_by_id uses too). Only once the budget is full; at most 512 a frame, the rest written into the tables in place. DM_NVN_POOL_STANDIN=0: the dummy, as before; DM_NVN_POOL_FIRST=1 logs each first read of a texture not made and what it found, including a stand-in whose storage the game wrote since. Rebased onto 6d9edb0's read-mark rule (the PR carried #2022's earlier form); the test's readback buffer is checked for nil (lint_c). Checked: t_pool_standin 51/51 with each encoder setting (31/31 with DM_NVN_POOL_STANDIN=0), t_pool_chunks 151, t_pool_notes 81 (both settings), t_pool_seen_replace 44, t_view_swizzle 554, t_cpu_tex, t_stencil_view, t_pfv, t_int_float_view; t_tex 5,382 (P: picked blocks against the whole level, both GOB orders), also under ASan/UBSan; the host builds; lint_c at the baseline; docs_symptoms --check, test_docs, test_env_switches. The PR's replays of a player's route (M4 Pro): after the area load 31 of 31 first reads found the dummy with stand-ins off, 0 of 49 with them on (none stale); memory and frame times within run-to-run range. A MacBook Air M5 tester: three black flashes in Kakariko in 8 minutes before, none in 4 after. No game run here: only pool-table games reach it, and none has libraries on the current chain. Fixes #1969 Co-authored-by: sleepyeyedjoe <353778822+sleepyeyedjoe@users.noreply.github.com>
|
Landed on main as 6c67644, with you credited as co-author. Thank you: the per-event replay numbers and the before/after pictures made the case clear. Thanks also to Itsjonaathan for the MacBook Air M5 test. What changed on the way in:
Checked here (M-series Mac, macOS 27.2):
Not run here: no game. Only games that read through the pool table reach this, and none of the local builds has libraries on the current chain. A short session on main would confirm it. This closes #1969. |
Upstream absorbed PR seedds#2022 (pool_seen read mark kept on replacement, now unconditional, DM_NVN_POOL_SEEN_KEEP gone) and PR seedds#2075 in reworked commits; conflicts take upstream's versions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seedds#1901 seedds#1459 seedds#1928 seedds#1942 seedds#1940 seedds#1952 seedds#1611 seedds#2065 seedds#2083 seedds#2008 seedds#2079 seedds#2022 seedds#2075 seedds#2077 seedds#2020 seedds#2021 onto main 04811a6 Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014eD437mxrMnW2UwLZhpwc7 Upstream pull requests applied in this commit (their authors' work, brought onto this tree with a three-way merge; the description is in each PR): - seedds#1459 by PromethOps (open): DM_UPSCALER=metalfx takes agl's agl_ndsr_vr.pck networks too, and a variant with no curve and no power is drawn as one colour space: Ocarina of Time's upscaler is MetalFX's - seedds#1611 by Esteban-V (open): An argument block whose only change is a uniform buffer is patched instead of encoded again - seedds#1774 by uncraftablexd (closed): Compute threads that exit before a barrier keep arriving at it, and every shader loop has a cap (GPU hang defences), chain change - seedds#1824 by cocoman93 (open): Clamp texel fetches past a sampled 2D texture's edge: no hard edge in Ocarina of Time's mist - seedds#1901 by FifteenPen (open): The game's compute dispatches with no game barrier between them run side by side (profile key dispatch_overlap, on for Ocarina of Time): Kokiri Forest GPU p50 15.63 → 14.76 ms - seedds#1928 by FeReuss (open): Ocarina of Time: enc_thread on, the replay encodes on a thread of its own - seedds#1929 by FeReuss (open): Unused border corrections are compiled out of pipelines that bind no sampler with a twin (function constant DMSH_BORDER_FC) - seedds#1930 by FeReuss (open): A float add to storage memory with no result read is one native atomic add instead of a compare-and-swap loop - seedds#1940 by uncraftablexd (open): A prewarm list's lines after its #@after_first_frame line are built after the first frame, in the background, instead of in the prepare step (issue seedds#1747) - seedds#1942 by uncraftablexd (open): Frame step: late frames with the GPU idle do not step up, and a longer hold the GPU does not explain is probed - seedds#1952 by AdyaGMD (open): Compute moves ahead of scene passes past occlusion queries and conditional rendering; clears wait across dispatches - seedds#2008 by bquilty25 (open): nvn: reactive memory watchdog, 16-way view cache, and texture upload batching - seedds#2020 by padsiwono (open): Ocarina of Time v131072: Output Native draws the full output size instead of the game's 80 % frame (`DM_OUTPUT`) - seedds#2021 by padsiwono (open): Ocarina of Time v131072 renders at the launcher's Resolution (`DM_RENDER_SIZE`), docked - seedds#2022 by sleepyeyedjoe (closed): Ocarina of Time: large models no longer go black for one frame twice, 4–5 frames apart, when the game streams new textures into them (a texture ID a shader has read keeps its read mark when it gets another texture) - seedds#2030 by humanperzeus (open): A NaN colour at alpha 0 is returned as 0, as NVIDIA's blender takes a zero factor: Ocarina of Time's white clouds over the ferns are gone - seedds#2035 by Know-It-All-Brother (open): The shader build runs as worker processes, and no shader waits for a batch (Ocarina of Time's cold Mac map: 3,002 s to 1,298 s) - seedds#2065 by Raymon82777 (closed): A vertex library with layer entry points of its own gets bases for its main0 pairs: Ocarina of Time's 230 ms camera-turn hitch in Hyrule Field goes to 39 ms - seedds#2075 by sleepyeyedjoe (closed): Ocarina of Time: a patch of scenery no longer draws dark for one frame when the game gives it a sharper texture (a pool texture the walk does not make holds a stand-in of its own colours until its first read) - seedds#2077 by roandegraaf (closed): Ocarina of Time: fix the "plaid" terrain / missing texture tiles (GPU-drawn textures now reach the storage the game copies) - seedds#2079 by uncraftablexd (open): Render pipelines that share a vertex function are taken from the archive or compiled one at a time: the AGX driver crashes in createVertexProgramVariant's completion block - seedds#2083 by FifteenPen (closed): Ocarina of Time's prewarm list gets 41 more task / mesh pipelines from later play
…a 23 was built from Ours kept where main has not taken the change: the pool test of image_texture (virtual pools), need_make with seedds#2075's stand-in, the copy absorb beside main's gsb_mark, DM_SHARPEN's copy_t2t_draw_x, the render scale (seedds#1903) and fixed resolution stubs beside seedds#2020's native output and seedds#2021's render size. Main's own taken for the depth rule (fb_depth_rule), the family textures and the movie network: the opt-in shortcut of PR seedds#1331 (DM_NVN_NDVE_MOVIE_DIRECT) is removed, main runs the network as host kernels. Generated files (chain header, lock, translator binaries, symptoms, game-patches) are main's here and rebuilt in the next commit. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014eD437mxrMnW2UwLZhpwc7 Upstream pull requests applied in this commit (their authors' work, brought onto this tree with a three-way merge; the description is in each PR): - seedds#1331 by givethesourceplox (closed): Scale decoded NDVE movies on Metal and skip matched reconstruction - seedds#1903 by FifteenPen (open): Ocarina of Time takes the launcher's Render scale: the 3D views at that fraction of the game's own size, the dynamic resolution held at its top level (Kokiri Forest docked at 0.75: GPU p50 15.77 → 14.31 ms, 60 fps held) - seedds#2020 by padsiwono (open): Ocarina of Time v131072: Output Native draws the full output size instead of the game's 80 % frame (`DM_OUTPUT`) - seedds#2021 by padsiwono (open): Ocarina of Time v131072 renders at the launcher's Resolution (`DM_RENDER_SIZE`), docked - seedds#2075 by sleepyeyedjoe (closed): Ocarina of Time: a patch of scenery no longer draws dark for one frame when the game gives it a sharper texture (a pool texture the walk does not make holds a stand-in of its own colours until its first read)
…s until its first read: Ocarina of Time's cliff beside the castle town wall, and other streamed scenery, no longer draws dark for one frame when the game gives it a sharper texture (PR seedds#2075) Once pool_budget() is spent a walk makes only the textures a shader has read (pool_seen), and an ID no shader has read held the dummy, which reads 0: the first read set its bit and pool_seen_scan made the texture for the next frame, so that frame was dark. Ocarina of Time keeps a texture set of its own for each piece of distant scenery and resolution (one level each: a BC5 texture and two three-layer BC6H / BC7 arrays) and registers a new set 12 to 969 frames before the material names it. Which registrations will be read cannot be known ahead (14,455 stayed unread in 58 s of play, 9.8 GB, against 10 first reads), and making them all is what lazy making exists to avoid, so the miss frame stays; this changes what it reads. The entry holds a stand-in (pool_stand): the texture's own smallest levels, from the first of at most 1 KB on, or its last level cut down to at most 4 KB a layer, every n-th block of every n-th block row (nvn_deswizzle_pick; blocks as they are, so no decoder). It is read from the storage on the CPU into a small shared texture cut from a heap resident from the start, for 2D textures and 2D arrays of up to 8 layers with the view's type, layers, format and swizzle (the swizzle rule moves to treg_swizzle, which tex_by_id uses too). Only once the budget is full; at most 512 a frame, the rest written into the tables in place. DM_NVN_POOL_STANDIN=0: the dummy, as before; DM_NVN_POOL_FIRST=1 logs each first read of a texture not made and what it found, including a stand-in whose storage the game wrote since. Rebased onto 6d9edb0's read-mark rule (the PR carried seedds#2022's earlier form); the test's readback buffer is checked for nil (lint_c). Checked: t_pool_standin 51/51 with each encoder setting (31/31 with DM_NVN_POOL_STANDIN=0), t_pool_chunks 151, t_pool_notes 81 (both settings), t_pool_seen_replace 44, t_view_swizzle 554, t_cpu_tex, t_stencil_view, t_pfv, t_int_float_view; t_tex 5,382 (P: picked blocks against the whole level, both GOB orders), also under ASan/UBSan; the host builds; lint_c at the baseline; docs_symptoms --check, test_docs, test_env_switches. The PR's replays of a player's route (M4 Pro): after the area load 31 of 31 first reads found the dummy with stand-ins off, 0 of 49 with them on (none stale); memory and frame times within run-to-run range. A MacBook Air M5 tester: three black flashes in Kakariko in 8 minutes before, none in 4 after. No game run here: only pool-table games reach it, and none has libraries on the current chain. Fixes seedds#1969 Co-authored-by: sleepyeyedjoe <353778822+sleepyeyedjoe@users.noreply.github.com>
and seedds#2085 in upstream's versions, the host at -O2, Ocarina of Time's camera-turn hitch in Hyrule Field (230 → 39 ms), 41 more task / mesh pipelines and 4,959 render / compute pipelines in its prewarm list, GPU writes into uploaded textures kept on the GPU, compact NVN recordings; chain 8f2d8a09c3f9 PR seedds#2030 (blend_zero) stays the fork's own: upstream moved the shader build's MSL rewrites into dmsh_rewrite.py (80fc2ec), so blend_zero and BLEND_ZERO move there, dmsh_build.py finish calls dmsh_rewrite.blend_zero, and test_dmsh_blend_zero.py imports it. dmsh_chain.h regenerated (8f2d8a09c3f9). Tests: test_dmsh_*.py 26/26 OK (test_dmsh_pairs with DM_GAME=0400486024DEC000); t_pool_chunks 151/0, t_cpu_storage 135/0, t_readback 218/0, t_pool_seen_replace 44/0, t_pool_standin 31/0, t_pool_notes 81/0, t_mesh_first_use 53/0.
…e page table, so PRAGMATA's cut to the tablet no longer shows a dark frame The stand-ins of seedds#2075 left out every texture in a sparse pool, whose address range must be read through its page table (an unmapped page stops the host with SIGBUS). PRAGMATA puts all its streamed textures in sparse pools and is past the walk's budget from its first scene, so the materials first read at a scene cut still found the dummy: the whole-picture luma at the cut from the tower to the tablet fell from 65 to 22 for a frame. stand_fill gathers each layer's bytes from the stand-in's first level to the layer's end through the page table (nvn_sparse_copy, at most 256 KB a layer) and decodes them with nvn_deswizzle_from / nvn_deswizzle_pick. A texture with the SPARSE flag gets one only from its mip tail on. A page behind those levels not mapped yet: the ID keeps the dummy and stays open (stand_no 0), so it is looked at again when a mapping over its pages notes it (treg_sparse_notes) or at its next walk, where it was refused for the registration's life before. Such a refusal counts toward the 512 stand-ins a frame, so a game that maps every frame retries at most that many. Test: t_pool_standin_sparse (new, Metal validation), 23 checks, including a mapping in the same frame with no walk; with a refused ID kept closed, as on main, that check fails. t_pool_standin and t_pool_notes pass. Reference entry in rendering.md, and a line in PRAGMATA's note. Co-authored-by: uncraftablexd <352372032+uncraftablexd@users.noreply.github.com>
Fixes #1969. Built on #2022: its two commits come first, and only the last commit is this change.
In short
DM_NVN_POOL_STANDIN=0.What and why
Cause.
pool_budget()is spent, a walk makes only the textures a shader has read (pool_seen). An ID no shader has read holds the dummy, which reads 0.pool_seen_scanmakes the texture for the next frame. So the first frame is dark and the next is right.currentAllocatedSizeon a 24 GB Mac; see Ocarina of Time (0400486024DEC000 v131072, Mac): parts of the scenery flash dark for a split second when the game loads a sharper version of them #1969).Pictures. Three presents in a row at the cliff's first read, from the replays: stand-ins off in the top row (the middle frame is dark), stand-ins on in the bottom row.
The same three frames with stand-ins on, enlarged: in the middle one the cliff samples its stand-in and is a little soft.
Fix. A texture the walk leaves out for good holds a stand-in in the tables until its first read (
pool_stand).nvn_deswizzle_pick). The blocks stay as they are, so compressed formats need no decoder.tex_by_idmakes for the ID. The swizzle rule moves totreg_swizzle, which both use.DM_NVN_POOL_FIRST=1logs each first read of a texture not made yet and what it found. The totals go to the log every 300 frames.Evidence
Windowless replays of a player's recorded route past the cliff (
DM_KEYS,DM_FRAME_DELTA=0, M4 Pro, macOS 27.0.1): 10 withDM_NVN_POOL_STANDIN=0and 8 without. The replays are not deterministic (the cliff's set streams in about half of them), so the comparison is per event.Cost
Medians over the runs, stand-ins off and on:
texture type mismatch … texture ID 850appears in 6 of the 10 runs and 2 of the 8.Tests
t_pool_standin(51 checks; 31 with the switch off). It reads the stand-ins back from the GPU and covers:t_texP checks the picked blocks against the level decoded whole, in both GOB orders.t_pool_notesandt_pool_chunks. Where they checked that an unread texture past the budget is the dummy, they now check that it is not made (its stand-in, or the dummy with the switch off).t_pool_seen_replace,t_cpu_tex,t_bc_alias,t_defer_chain,t_image_usage,t_image_type,t_view_swizzle,t_stencil_view,t_pfv,t_int_float_view,t_readback;docs_symptoms.py --check,test_docs.py,test_env_switches.py.Not done
scripts/regress.pywas not run, as this Mac has no other game's build. The stand-ins act only oncepool_budget()is spent.🤖 Generated with Claude Code