Repository navigation
drm/asahi: g17: partial renders on 25G83 - #879
Merged
RyanTheTide merged 7 commits intoNov 3, 2026
Merged
RyanTheTide merged 7 commits into
RyanTheTide merged 7 commits into
Conversation
The TA arrays wrote 0 to 0x1c890, 0x1c918 and 0x1c9a8, upstream's tvb_tiling_control, tiling_control_2 and min_free_tvb_pages. macOS writes 0x200440, 4 and 0x3c on two-cluster parts, all three for every pass. Partial renders need 0x1c9a8. Without it the pages each fragment pass returns shrink geometrically, 122, 66, 37, 24, 13, 7, to none, and the tiler spins through about 140,000 empty stops until the render is killed. With it segments hold at 84-88 pages and the render finishes. 0x1c890 and 0x1c918 alone don't do that, and macOS writes them together. Signed-off-by: Jonathan Goyvaerts <jonathan.goyvaerts@gmail.com> Assisted-by: LLM
The render pool holds the USC private memory of a context's vertex and fragment threads. It is fixed at 22 MiB. When the tiler stops for a partial render its threads keep what they hold, and the fragment pass needs its own. At 22 blocks fragment pass 2 stalls for 2.2 ms when it is lucky, and otherwise never finishes and the firmware recovers with reason 0. With process_empty_tiles, pass 1 hangs the same way on its first thread. On a 3456x2234 render of 3M triangles, 22 blocks lost every run with the first stop at 255 pages. 27 blocks passed 12 of 12, heavy shaders included. Clearing the pool registers instead takes the 2.2 ms off pass 2, but then the tiler gets no pages back. 44 is measured, not derived: it leaves margin over the 27 that passed, for shaders heavier than the test's. It costs 22 MiB per rendering context in the 2 GiB lower window, on A18 Pro as well, which shares this constant. Refused allocations in a dEQP run at 8 jobs went from 0 to 15, all retried. Signed-off-by: Jonathan Goyvaerts <jonathan.goyvaerts@gmail.com> Assisted-by: LLM
The hardware writes a record per tile-map slot into gtp6 during a partial render, and 0x600 bytes is smaller than macOS's 0x1c00. Size it for the largest pool over all 64 slots. That is a superset of what macOS uses, not a layout claim, and it is the size every partial-render test ran with. Signed-off-by: Jonathan Goyvaerts <jonathan.goyvaerts@gmail.com> Assisted-by: LLM
…OS does Every array of a partial render wrote the main control word with the load and store bits of each plane set. A pass that clears its depth then loaded it in its first segment, instead of starting from the clear. macOS leaves the first segment's loads as the pass set them, stores each plane, and adds the loads for the reload arrays: 0x80045, then 0x88045. Keep the main word's loads in the first segment. A pass that loads its depth or stencil needs them there: an overflow of a grown pool with more than a few draws gives that segment real work, and a pass without them stores background depth over the real values. A colour pass at EQUAL after such a pass drew nothing at all in a test scene, and Counter-Strike 2's soundpack preview, with its depth pass overflowing twice a frame, had up to 2,937 of 5,400 tiles black. With the loads kept the scene matches a render that can't overflow byte for byte, and the preview had no black tile in 48 captures. Signed-off-by: Jonathan Goyvaerts <jonathan.goyvaerts@gmail.com> Assisted-by: LLM
Tiles a segment has no geometry for are skipped, and only the main end-of-tile program runs in the last segment. A multisample colour attachment that isn't stored is resolved by that program alone, since a partial store has to keep the samples for the reload. So a tile that only earlier segments drew to is never resolved, and its part of the resolve target stays as it was: 40-47% of the pixels of a test scene with storeOp DONT_CARE and a resolve. Ask for empty tiles in the last segment. It costs one load and store of each untouched tile, only on a render that already overflowed. A tile entirely outside a smaller render area, in a pass with loadOp DONT_CARE, reloads undefined samples and resolves them over the destination. LOAD and CLEAR already walk those tiles from the first segment. Honeykrisp could take the same path for that case. Signed-off-by: Jonathan Goyvaerts <jonathan.goyvaerts@gmail.com> Assisted-by: LLM
Every TA asked the firmware to grow the parameter buffer when it overflowed. That can't go past the maximum. In Counter-Strike 2's soundpack preview a depth pre-pass at 3456x2234 grew to the 3353-block maximum and the firmware failed it. macOS sets this word only for memoryless passes. An ordinary pass stops and flushes partial renders when the heap runs out, and the heap grows between renders. Do the same on 25G83, keeping the host's automatic growth: 9M triangles take 527 ms and 20M 1,172 ms, and images match a render that can't overflow. 26A428 keeps the grow request. This has not been run there, and its partial render path may differ. Linux has no memoryless flag, so such a pass would partial-render too; Honeykrisp always backs its attachments. Signed-off-by: Jonathan Goyvaerts <jonathan.goyvaerts@gmail.com> Assisted-by: LLM
…tion The partial-render series put the tiler segment constants between ta_registers_25g83()'s doc comment and the function, so rustdoc attached the function's description to VDM_SEGMENT_CONTROL and left the function undocumented. Move the constants above the comment. No code change. Assisted-by: LLM Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Contributor
|
Merged. I added one fixup on top (3afdeac, Ryan): the three tiler constants sat between Tested on the J616s as a one-shot of 7.3.0-aurora-m4t1103a (integration/aurora 19f71a1 + #837 + this series with the fixup + #884), boot c9c549d5. The tree is the same as the merged one:
Not run: the A18 Pro. It pays the larger render pool and keeps the grow request. A Neo one-shot is next, once the Neo is back from a reinstall. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Makes partial renders work on the M4 Pro's 25G83 firmware, so a pass that overflows the tiler heap finishes instead of losing the GPU. Today every pass asks the firmware to grow the heap, and a pass that hits the 3353-block maximum fails with reason 0. Counter-Strike 2's soundpack preview is one: a depth pre-pass at 3456x2234 grows to the maximum and the client gets
DEVICE_LOST.macOS sets TA+0x892 only for memoryless passes. An ordinary pass stops, flushes a partial render when the heap runs out, and the heap grows between renders. Turning that on was not enough: partial renders hung or produced wrong pixels on 25G83. The first five patches fix what was found, and the last one turns it on, so no commit in the series enables a broken path.
The register values come from DTrace captures of macOS on the same machine and firmware. The array word 0x80045 / 0x88045 was captured on a pass that clears depth, so keeping a loading pass's loads is inferred, and shown by the results below.
Scope.
V26A428keeps the grow request, so it shouldn't reach the arrays patches 4 and 5 change. The pool size is a shared constant, so A18 Pro pays the extra 22 MiB per context too. Patches 1 and 3 and the last patch apply toV25G83only. The segment control value is the macOS two-cluster value, so #525 would need to check it for T6041.Testing, M4 Pro (J616s, T6040), firmware 25G83
Not tested: A18 Pro, T6041, and frame rate in scenes that overflow every frame beyond CS2's preview.
Known issues
Tested tree. The tests ran on
9ff2b9a3761dplus our carried patches.aurora-wiphas had no driver, UAPI orrust/kernelchange since. Of the carried patches, four touch the driver: #837, client names in failure reports, MMU fault banks in the recovery log, and theasahidriver name. None touches this path. Patches here were reordered for this PR, and their combined driver diff is the same as the tested one except for theV25G83condition in the last patch, which keeps the same value on 25G83. Every commit compiles.