Skip to content

Merge Aurora Mesa aurora-wip: G17P submit, refit and leaf-pairing work - #19

Merged
maralcbr merged 376 commits into
integration/aurorafrom
sync/aurora-wip-20261027
Oct 29, 2026
Merged

maralcbr merged 376 commits into
integration/aurorafrom
sync/aurora-wip-20261027

Conversation

@DjDeveloperr

@DjDeveloperr DjDeveloperr commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

This mirrors the Aurora Mesa work merged into aurora-silicon/mesa aurora-wip since the two trees last shared history (aurora-silicon#64, 2026-10-24). aurora-wip was 40 commits ahead, so this is one merge commit.

What it brings in:

Native acceleration structures are used only on G17P (hk_g17p_rt_enabled checks AGX_CHIP_G17P), so the RT changes don't affect other chips.

Conflicts, both resolved without losing behaviour:

  • src/asahi/vulkan/hk_g17p_compression.h: both sides fixed the pNext walk the same way. omacom's form is kept; it also copies the external-memory struct with memcpy.
  • src/asahi/compiler/test/test-native-form-regressions.cpp: both sides rewrote the image-store data-type test so it no longer depends on identical chip schedules. Aurora's version is kept, because the merged test body around it is Aurora's. Its references keep float and integer separate and check that the two encodings differ.

Testing on an aarch64 host:

  • The asahi Vulkan driver builds, with the in-tree asahi_clc, which includes the g15g target.
  • agx_tests: 338 tests, 337 pass, 1 skipped; all 53 NativeForms tests pass.
  • The Aurora side was tested on A18 Pro (Neo) by its PRs.

Update 10-28, fourth merge commit: aurora-wip up to 11005a3 (aurora-silicon/mesa #84, #86–#90).

  • #84: Zink split occlusion queries read back every start, not only the last.
  • #86: hk skips the kernel queue-limits query under virtio. Bare metal is unchanged.
  • #87: consolidates the J616s daily-driver and M4 Pro Mesa work onto aurora-wip.
  • #88: the G17P BG/EOT cache key is aligned to 8 bytes, which fixes the i386 and x86_64 FEX guest builds.
  • #89: on G17P, explicit-RTE shaders lower sin/cos after the FMA repair, so the repair covers only the shader's own FMAs. Every other shader compiles as before.
  • #90: lighter G17P acceleration-structure programs:
    • the node encoders are specialized by geometry, and the refit level and propagation phases run as their own programs;
    • the plan reads the IR internal node count once, and the TLAS envelope clears instance pointers with a thread per instance;
    • on A18 Pro, AS builds are +0.4…+3.5% and refits +1.9%, with ray rows unchanged;
    • full RT CTS 47102 Pass / 10088 NotSupported / 0 Fail.

Conflicts, all resolved without losing behaviour:

  • src/asahi/clc/asahi_clc.c: the libagx target list keeps omacom's g15g (T8122) and adds aurora's g17g. target_chip() already maps both.
  • src/asahi/lib/agx_device.c: keeps omacom's agx_va_user_limit() (the T8122 runtime reservation) and adds aurora's M5 address-window layout ahead of it.
  • src/asahi/vulkan/hk_g17p_compression.h: takes aurora's comment and its depth/stencil restriction. The pNext walk below it is the same on both sides.

The merged tree builds on an aarch64 host with the in-tree asahi_clc, both libagx targets included. On A18 Pro it passes a sanity run with no GPU errors: deqp-vk api.smoke 6/6 and the gpubench light rows (render, compute, cross-queue timeline).

Remove constant branches before coalescing sample-shaded epilog coverage updates. This keeps the coverage transaction ahead of colour accesses and avoids reacquiring coverage while colour classes are held.

The folding runs for M5 coverage keys only, so G17P and G15 keep their
epilog shape.
Reuse the forced-early API mask combination for M5 per-pixel fragments. Keep the M5 sample-shaded path unchanged until its separate unroll path is qualified with a dynamic API mask.
Snapshot of the previously uncommitted native-write-class changes that
form the base of the qualified M5 driver build (libvulkan SHA-256
ae087e88c5609d25f063e8f241f5138d3129930a465a4bddaf8c36d3d20e50b3).

Signed-off-by: Diljit Singh <hey@djdev.me>
Robustness vertex shaders that loop over several texture operations
consumed each result right after issuing it, so every fetch completed
alone. With ~2000 iterations of 12 serialized round trips the vertex
stage outlasted the firmware TA progress watchdog and the device was
lost. The native compiler issues all fetches of the loop body first and
consumes them afterwards.

Add agx_nir_opt_sink_loop_sample_uses, which sinks sample-dependent ALU
to the latest top-level loop-body block dominating its uses without
moving the texture instructions, and let the M5 ILP sample schedule
handle in-loop blocks with two or more samples. Only M5 (AGX3) vertex,
geometry and tessellation stages are affected; fragment and compute
binaries are unchanged.

On M5 the five previously faulting robustness2 sampled_image 3D vertex
cases pass 3/3, and a 92,450-case CTS regression passes except one
pre-existing concurrency-only DeviceLost.

Signed-off-by: Diljit Singh <hey@djdev.me>
Implicit image planes were packed at 128-byte alignment. A sparse
binding image with several small planes (for example 3-plane 4:4:4
YCbCr at 11x137) then placed its second plane mid-page, so the opaque
bind for that plane produced a VM_BIND with a 4 KiB memory offset and a
non-page range, which the kernel rejects, and vkQueueBindSparse reported
device loss.

Start every implicit plane of a sparse binding image at the 64 KiB
sparse alignment the driver already reports, so each plane's bind range
and memory offset are page aligned.

On M5, the 48 failing image_sparse_binding.multiple_* multi-planar 4:4:4
cases now pass; the whole sparse_resources suite has no failures.

Signed-off-by: Diljit Singh <hey@djdev.me>
… query arrays

The M5 native ray-query lowering refused any shader with more live queries
than the witnessed native allocation indices (8) and any candidate-API
query array. rayqueryterminate in any-hit and intersection shaders has
eight application queries live next to the pipeline's traversal query, and
ray_query.misc.dynamic_indexing selects one of 48 queries per thread, so
both failed at pipeline creation with "unsupported query shape".

Number queries logically and map each logical query to a native slot
modulo the slot count. A slot switch between logical queries is the
existing owner-mismatch restart/replay; a resident-logical-query variable
forces that restart as well, so two logical queries sharing a slot never
read each other's payload. Query arrays larger than the slot count keep
their array form: every element gets its own state, a dynamically selected
element enters its slot through a select over the reachable slots, and its
state is staged through per-operation shadows that are resolved to selects
after lowering, so no query state reaches thread-private memory beyond
ordinary spills. Arrays up to the slot count keep the qualified split into
separate queries.

Single-query shaders compile byte-identically; multi-query shaders gain
one compare per proceed.
A storage image descriptor carries a texture for imageLoad/imageSize and a
PBE for stores. On M5 the native PBE path only covers single-sampled
views, and the texture half was kept for views without a PBE only on
G17P. Every multisampled (and linear) M5 storage view therefore got a null
texture as well, so imageLoad returned zero for every sample:
synchronization2.layout_transition.compute_transition_storage loaded a
cleared 4x rgba8 storage image and read all zeros.

Keep the readable texture on both AGX3 descriptor generations; stores to
such views stay null as before.

Advertising single-sampled storage images instead would turn the failure
into NotSupported, and on M5 it also turned about a third of image.mutable
passes into NotSupported.
On M5, compute_transition_storage passes and a 15,517-case image
load/store, mutable, reinterpret, atomic, format-property and robustness
sample has no failures.

Signed-off-by: Diljit Singh <hey@djdev.me>
The robust texel-fetch bounds check compared every coordinate component
with the texture size at the fetched level. A component that is the
constant zero lies inside every valid level and layer, so only the level
check can fail for it, and a null descriptor already swizzles every
channel to zero.

Skip those components. A fetch of texel 0 at a varying level, as in the
robustness2 vertex loops' texelFetch(t, ivec3(0), c), then needs neither
the size query at that level nor its compares.

The skip applies to Gen10 (M5) descriptors only; Gen9 keeps the
whole-vector check.

Signed-off-by: DjDeveloperr <hey@djdev.me>
Expose only the merge of adjacent if-statements with the same condition,
like nir_opt_if_empty_then exposes only the empty-then inversion. A late
code-motion pass that empties the block between two such ifs can then
merge them without the rest of nir_opt_if reshaping its control flow.

Keep absorbing following ifs into the same one, so a run of several
same-condition ifs becomes one in a single call.

Signed-off-by: DjDeveloperr <hey@djdev.me>
The M5 backend retires every outstanding scoreboard slot before a
control-flow instruction. In the robustness2 sampled_image vertex loops
each axis has a conditional texel fetch, `if (c != 0) texelFetch(t,
ivec3(0), c)`, between its unconditional samples, so even with every
consumer sunk to the end of the body the eleven or twelve samples of an
iteration completed in seven or eight serial waits. With ~2100
iterations the vertex stage still ran close to the TA watchdog and was
lost when three CTS processes shared the GPU.

Add agx_nir_opt_hoist_loop_samples: a sample in a later top-level block
of a loop body moves, with the pure computation of its sources, to the
end of the earliest top-level block before it from which only
conditional blocks without jumps, loops or side effects separate it. It
runs exactly as often as before and no sample is speculated. Only the
samples whose consumers agx_nir_opt_sink_loop_sample_uses sinks move, so
the live results keep that pass's bound. The conditional blocks left
adjacent with the same condition are merged with nir_opt_if_merge, after
which their identical fetches CSE.

In the r32i 3D vertex loop the unconditional samples now issue back to
back and the three conditional fetches become one: 12 samples and 8
serial waits per iteration become 10 samples and 3 waits, and the loop
shrinks from 413 to 328 instructions. Only geometry-side M5 stages are
affected, as for the sink pass.

The loop-sample unit test now builds valid NIR (phi sources added after
insertion never joined their definitions' use lists, and if-phi constants
sat ahead of the phis), so it also passes with assertions enabled.

Signed-off-by: DjDeveloperr <hey@djdev.me>
Sort-by-rank only distinguished constants, convergent and divergent
sources (the TODO for loop-invariant code motion). A chain inside a loop
whose sources are all convergent, such as an accumulation of texture
results with uniform coordinates plus per-iteration constants, therefore
had a single rank and was left alone once it was too long for the CSE
heuristic, so each iteration added every constant separately.

Add nir_reassociate_loop_invariant. With it, each class is split by
invariance in the innermost loop around the chain: a source defined
outside the loop, or computed inside it without side effects from such
values, ranks below the sources varying with the loop. The invariant part
of the chain is then computed first and can leave the loop through
nir_opt_preamble or loop-invariant code motion. Invariance is a bounded,
memoized walk over ALU, texture and reorderable intrinsic sources; it
only steers the order, so a conservative answer only costs optimization.

The ranks of a chain are now computed once and sorted together with its
sources by the same comparison as before. nir_opt_reassociate_loop keeps
its second, scalar-only round when callers pass extra options. Without
the new option the chains are reassociated exactly as before.

Signed-off-by: DjDeveloperr <hey@djdev.me>
The robustness2 null-descriptor vertex loops add the same three size
queries per axis to every component of their accumulator in each of the
~2100 iterations. All terms of those chains are convergent, so
reassociation gave them one rank and left the chains alone, and each
iteration spent 36 adds on values that never change.

Reassociate with nir_reassociate_loop_invariant on M5: the invariant terms
are summed first, so preamble formation computes their sum once and each
iteration adds it once per component. The r32i 2D-array null-descriptor
loop shrinks from 305 to 275 instructions.

Signed-off-by: DjDeveloperr <hey@djdev.me>
A value added twice to an accumulator, x + (y + y) after reassociation
groups a chain's duplicated sources or (x + y) + y where it does not,
costs two adds. The robustness2 vertex loops add each texture() result
twice, because texture() and textureLod(.., 0) CSE to one sample, once
per component and axis.

Add agx_nir_fuse_doubled_add, run with the late fusions on M5, which
rewrites both forms of a 32-bit doubled add to x + (y << 1), one iadd
with the source shift. The r32i 3D vertex loop loses another 12
instructions (328 to 316), the r32i 2D-array null-descriptor loop
another 12 (275 to 263).

Signed-off-by: DjDeveloperr <hey@djdev.me>
…ure queue

hk_host_as_init() creates its queue as index 0xffff of a create info whose
pQueuePriorities points at one float, and hk_queue_init() reads
pQueuePriorities[index_in_family] when no global priority is chained. The
read lands 256 KiB past the float; on the Neo it hits unmapped stack and
every device that enables accelerationStructureHostCommands crashes at
vkCreateDevice (dEQP's default device does).

Chain VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR, the class the intended 1.0
priority maps to, so no priority array is indexed.

Signed-off-by: Diljit Singh <hey@djdev.me>
HK_FRAME_ANATOMY=<path> logs, for every Nth vkQueueSubmit, each hardware
command the submit carries (render geometry, attachment load/store and
background/end-of-tile programs, draw counts, barriers handed to the
kernel) and optional per-command GPU timestamps read back lazily at the
next sampled submit.

Unset, the profiler is compiled in but inert: every hook is a single
NULL test and no hardware or kernel structure changes.
Two consecutive render pass instances on the same attachments, where the
second loads what the first stored and only state binding and
framebuffer-local barriers are recorded between them, are one tile pass
to the hardware. The second instance's draws now continue in the first
instance's render stream, which drops the store and reload of every
attachment and one command boundary.

The ended instance records where its draws ended and the tracked state;
the next instance continues only when its geometry and every attachment
match, it loads every attachment, and nothing was recorded or ordered in
between except barriers whose second scope is the fragment-test and
colour-output stages with attachment accesses only, or whose first scope
does not include graphics work. Those are applied to the merged stream
as a newly created render stream would take them.

A new sync epoch counter, bumped by every hk_sync mutator, lets the
candidate detect any intervening dependency change. The frame anatomy
profiler gains opt-in decision notes.

aurora-wip already continues render pass instances on native AGX3 (hk:
continue render pass instances on the same attachments on native AGX3,
and the later ordered-dependency form). This enables that path on M5 and
adds its frame-anatomy notes.
Records after the first of one indexed-indirect multi-draw that read
draw parameters republish the vertex stage program per record. No API
call can intervene between records, so every launch range the previous
record derived from the root, descriptor sets and their buffers is
unchanged; only the draw-parameter and draw-index addresses follow the
record. Publish from the previous record's ranges with those substituted
instead of replaying the launch, and fall back to the general path on any
mismatch. The emitted loader bytes are identical.

Also evaluate the prepared bind-tail match only on the chip whose bind
tail consumes it, as the slow dynamic-state path already does.

aurora-wip already republishes multi-draw vertex programs on native AGX3
(hk: republish multi-draw vertex programs from the previous record on
native AGX3); this enables it on M5.
A dependency between two render pass instances on the same attachments
that is not framebuffer-local, up to ALL_COMMANDS -> ALL_COMMANDS with
every memory access as Minecraft records between its sky and main
passes, previously ended the continuation candidacy. Accept it when one
tile pass is memory-equivalent to the two instances by construction:

- the first instance's stream wrote no memory but its attachments, so
  the second instance's vertex work, which the tile pass runs before the
  first instance's fragment work, reads what it would have read after it;
- the second instance writes no memory but its attachments either: a
  draw whose bound programs write external memory, transform feedback,
  software or mesh stages and adjacency end the stream first, and the
  draw continues in a new stream ordered after the whole merged pass;
- no shader can read the attachments' memory, which the merged pass
  writes only at its end: the layouts match, no attachment is in a
  feedback loop or can alias another resource, no descriptor set is bound
  for graphics, and no graphics push descriptor recorded so far in the
  command buffer names an attachment image. Later set binds and pushes
  that name one end the stream before the next draw.

Layout transitions and queue family transfers between the instances
still end the candidacy. Queries, timestamps, conditional rendering and
transform feedback commands inside an ordered continuation end it at the
command. A pre-graphics stream created for a continued draw (index
bounds passes) sits before the merged render; it no longer waits on that
render, which holds only work it cannot depend on.

aurora-wip already continues render passes across ordered dependencies on
native AGX3 (hk: continue render passes across ordered dependencies on
native AGX3); this enables its push-descriptor bookkeeping on M5 too.
On M5 a fragment shader's main part picks between a testing and a
killing coverage update with a uniform branch on the draw-time
no_epilog_discard selector, so that an epilog that discards (alpha to
coverage, depth/stencil export) can own the tests instead. With alpha
to coverage statically disabled in the pipeline and no depth/stencil
export, no epilog of the shader discards, and the selector is all ones
for every draw that can use the shader.

Record that in the fragment key and fold the selector at compile time.
The main part then ends in the single unconditional testing coverage
update the native compiler emits, instead of the uniform branch and its
second arm. Pipelines with dynamic alpha to coverage, libraries without
multisample state and shader objects keep the selector.

Minecraft's rain fragment shader (particle, translucent blend, discard,
reverse-Z test without depth writes) shrinks from 74 to 62 main-part
instructions and from 99 to 85 linked, against 71 from the native
compiler for the same shader and blend state.

aurora-wip already folds the selector on native AGX3 (hk: fold the
depth/stencil test selector under static state on AGX3), where G17P also
requires the static no-early-reject proof. This enables the fold on M5
through the same key bit, without that proof: M5 has no read-only early
reject.
The precise depth/stencil store (stores follow storeOp/resolve in the
stream that ends the pass) and the elision of a depth/stencil attachment
that no draw touches were limited to G17P. Every other AGX3 part fell back
to hk_pack_zls_control()'s forced store, so M5 wrote the whole depth plane
at the end of every render pass even for storeOp DONT_CARE, and loaded and
stored untouched depth in full-screen colour-only passes.

Both decisions depend only on Honeykrisp's control-stream split and the
kernel's partial-render ZLS word, not on the encoding, so gate them on the
AGX3 ISA family (G17P, M5 and the other parts on the AGX3B encoding)
instead of G17P.

On M5, a 23,232-case depth/stencil load/store sample (renderpass1/2,
dynamic rendering, pipeline depth/stencil, fragment operations, depth
copies) passes with no failures or GPU faults.

aurora-wip already stores and elides precisely on every native AGX3 part
(hk: store and elide depth/stencil precisely on every native AGX3 part);
this adds M5.

Signed-off-by: Diljit Singh <hey@djdev.me>
The render pass continuation (same-attachment and ordered), the
multi-draw vertex program republication and the static depth/stencil
test selector fold were gated to M5. None of them depends on the HAL300
command format: continuation keeps an ordinary render stream open and
uses the common sync tracker, the republication goes through the
shared AGX3 graphics loader, and the selector exists in every main part
that may discard. Gate them on the AGX3 instruction set instead.

G17P can move a discarding shader's read-only depth test into the
prolog at draw time, with the main part's selector off. Fold the
selector there only when the pipeline's static state rules that out
(multisampled, depth test off, depth writes on, compare ALWAYS or depth
bounds on), and never take the early reject for a folded shader.

aurora-wip already runs both on native AGX3 and the preceding commits add
M5, so only the comments that still named M5 change here.

Signed-off-by: Diljit Singh <hey@djdev.me>
With the native acceleration-structure profile, every M5 device read
the query traversal assets named by ASAHI_M5_RQ_CAPTURE_DIR at creation
(an 8 MiB code buffer at a fixed VA, an 8 MiB host image and the
intersection table), and failed with VK_ERROR_INITIALIZATION_FAILED if
any file was missing. Steam's container runtime replaces /usr, so
inside it the directory does not exist and every game's vkCreateDevice
failed (DXVK, zink), although none of them uses ray queries.

Load the assets only when the device enables rayQuery or
rayTracingPipeline, and advertise VK_KHR_ray_query only when the files
are readable in the process, so a process that cannot see them gets no
ray queries instead of no device. Devices without ray queries also stop
paying for the buffers and the fixed VA range.

Signed-off-by: Diljit Singh <hey@djdev.me>
When the AGX3 encoder refused an instruction, the compile failed with
"AGX3 binary packer refused an instruction" and only AGX_MESA_DEBUG=shaders,
with a full dump of every shader, said which. That is useless for finding
failures in a running game.

Keep the first refusal of each pack (opcode, encoder line, gate) without any
debug flag, put it into the compile refusal, and print that refusal in hk's
"constant program failed to compile" fallback warnings.

Signed-off-by: Diljit Singh <hey@djdev.me>
The f32mul forms that encode persp_mul carry scoreboard waits only for
slots 1..6, but AGX3C allocates eight slots. A persp_mul that had to wait
for an asynchronous result on slot 7 or 8 therefore failed the whole
compile, and the fragment shader could not be created: Skyrim SE loses
62 of its 7615 fragment shaders this way (VK_ERROR_UNKNOWN from pipeline
creation, so the draws are dropped).

When the form cannot carry the wait, retire it first on an identity of the
destination, as already done for other producers without a suitable wait
field, and encode the multiply without a wait.

Signed-off-by: Diljit Singh <hey@djdev.me>
A cube sample whose texture and sampler operands are uniforms or immediates
groups CEM's two face finalizers with its own result slot, and the finalizer
form's slot field only names slots 1..7. On AGX3C the round-robin allocator
also hands out slot 8, and such a sample made the whole shader unencodable.
Every fragment shader whose constant program hoists cube descriptors then
lost the constant program (505 of Skyrim SE's 7615 fragment shaders,
"G17P fragment constant program failed to compile; compiling without it").

Let the slot allocator take a per-instruction slot ceiling and give these
samples 1..7. Register descriptors stage on slot 6 and are unaffected.

Signed-off-by: Diljit Singh <hey@djdev.me>
AGX3C encodes rcp and rsqrt without a saturating form, so a saturate folded
into one by the fmov propagation made the shader unencodable: 6 of Skyrim
SE's fragment shaders clamp a reciprocal and could not be created at all.
Keep the separate saturating move for those two operations on AGX3C.

Signed-off-by: Diljit Singh <hey@djdev.me>
A new dynamic offset (vkCmdBindDescriptorSets per draw, as D3D translation
layers and zink issue) republishes the root. On M5 that also republished
the fragment program, whose uniform ranges address the root, and with it
the whole HAL300 graphics-state block: about 250 ns of GPU time per draw.

Record which root bytes each shader can read, by classifying its
constant-offset root loads against the spans a draw may republish without
the fragment stage seeing them: the dynamic buffer table and the vertex
input tables. Any other root access counts as reading everything. When the
new root differs from the fragment program's only in spans the fragment
shader does not read, the program keeps the root it was published with:
that root stays valid for the command buffer and holds the same bytes
wherever the shader reads, so the published program and the PPP state stay
as they are.

Twin benchmark, 1210 direct draws with a new dynamic uniform buffer offset
per draw: pass span 1360 us -> 1108 us, matching the same draws with fixed
bindings (1067 us), identical image.

Signed-off-by: Diljit Singh <hey@djdev.me>
The M5 task payload lowering refused every shader with a payload atomic,
so pipeline creation failed for all 20 glsl.atomic_operations.*_task_payload
cases once task shaders stopped being NotSupported. The payload is ordinary
memory at the native payload address, so its atomics are global atomics
there; the generic lowering then handles them like any other, including
the float operations.

Signed-off-by: Diljit Singh <hey@djdev.me>
@DjDeveloperr

Copy link
Copy Markdown
Collaborator Author

Updates since the aurora-wip #84-#90 sync:

Mesa CI on 14d5e38 passed (source, x86_64 and aarch64 builds): https://github.com/omacom/mesa/actions/runs/40755713326

#229 gained 6c66b41a ("drm/asahi: restore the DRM driver's module owner"),
so its head moved from 1032928116c1. Re-pin the kernel contract to the
new head. The UAPI header is the same in both, so the ABI check is
unchanged. This branch still merges after #229.

Signed-off-by: DjDeveloperr <hey@djdev.me>
@DjDeveloperr

Copy link
Copy Markdown
Collaborator Author

Re-pinned the kernel contract to omacom/linux-aurora#229's new head 6c66b41a (ebc78bf), after #229 gained the restored DRM module owner. The UAPI header is unchanged, and this still merges after #229.

@chriskearney chriskearney left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Source review of ebc78bf786f5e4a61a3c4e89c5b645c8b05e6990: requesting changes for the G15 occlusion-query availability path below. This is a source-level correctness finding; a focused M3 Pro hardware reproducer is still pending. Native build tests passed 122/122, and a compiled UAPI-layout comparison against the tested #229/#244 kernel found no incompatible common fields. Those checks do not validate GPU query completion ordering. Four-board benchmark results will follow separately once the test and restoration checks are complete.

/* A retirement timestamp (any value but the queued store's 1) was
* written after the end-of-render maintenance: no submission wait. */
if (available &&
!(pool->avail_shift && hk_query_available_value(pool, query) > 1)) {
VkResult done = hk_oq_submission_done(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Keep the submission-completion check for G15 stamped occlusion queries

The > 1 shortcut treats every render timestamp as post-maintenance, but that holds for the G17 retirement implementation, not the G15 firmware timestamps. The stamped path is enabled on G15S/M3 Pro. hk_try_stamp_available() assigns consecutive queries to frag.end, vtx.end, frag.start, then vtx.start. The G15 kernel resolves all four destinations and places the vertex timestamps in the TA work item. A vertex timestamp can therefore make a query available before its fragment counts reach memory. Skipping hk_oq_submission_done() here allows vkGetQueryPoolResults(..., WAIT_BIT) to return success with zero or incomplete counts.

A minimal trigger is two queries in one render pass, followed by reading query 1 first, before waiting for the submission fence. One query plus an in-pass API timestamp occupying frag.end also reaches the early slot. The existing end-of-render maintenance explanation above this function describes why the submission wait is necessary. A batched read beginning with query 0 can mask this ordering issue.

Please retain the submission wait on G15 or restrict this shortcut to a kernel/chip path with a proven post-maintenance timestamp contract. G17's retirement check and destination publication provide that ordering; this finding does not apply to that path or to M1's unstamped path. Add a G15 regression that reads the later query first and compares against the completed-fence result. No hardware reproduction is claimed yet.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, thanks for the hardware confirmation. Fixed in 1ccff51 and 2b1bc1e (aurora-silicon#94). hk_oq_render_stamps_supported() now gates the stamped pool. It returns true only for chips whose kernel writes the slots at render retirement: G17P, and M5, which runs the same G17 retirement path. G15 (and G16G) no longer bind the timestamp buffer, so every query end keeps the queued availability store, and readback keeps hk_oq_submission_done(), as in your 7f0206e control arm. test-hk-query-oq checks the chip decision. A G15 regression that reads the later query first needs M3 hardware, so it would come from your probe rather than from CI.

@maralcbr maralcbr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DjDeveloperr thanks. The write-up made a 371-commit sync reviewable. I read it by area, with the shipped chips as the lens: M1/M2 (G13/G14), M3 (G15G/S/C) and Neo (G17P). I didn't read every M5 line. One blocker, on M3 only.

Blocker: M3 treats stage timestamps as render retirement

Occlusion availability carried by renders (#62) is on for every native AGX3 chip, not only G17P:

  • stamped = occlusion && agx_native_graphics() (hk_query_pool.c:575), and agx_native_graphics() is true on G15G/S/C.
  • hk_defer_oq_available() has no chip check (hk_query_oq.c:22-41). On M3 the forward resolver runs (hk_query_oq.c:266-274). When it returns HK_OQ_STAMPED, it drops the post stream that held the queued availability store (hk_query_oq.c:152-153).
  • Slots are handed out in the order {3, 1, 2, 0} (hk_query_oq.c:109), and hk_queue.c:332-341 maps them to vertex start, vertex end, fragment start and fragment end.
  • hk_GetQueryPoolResults() takes any availability value above 1 as a retirement timestamp and skips hk_oq_submission_done() (hk_query_pool.c:1230-1237).

The M3 kernel doesn't write retirement times. On G15 the firmware writes each of the four values straight from the stage's start and end records (g15/timestamp.rs, header comment; the TA work's timestamp tail at g15/encode.rs:679-687). So the vertex-end stamp lands when tiling finishes, before the fragment work that writes the occlusion counts. Fragment start lands earlier still.

Failure scenario: two occlusion queries end in the same render on an M3. The second one gets slot 1 (vertex end). Its availability turns nonzero once tiling finishes, and vkGetQueryPoolResults returns its count as available, before the fragment work has written it.

The G17 kernel keeps timestamp destinations until retirement (g17/completion.rs), which fits the assumption, and the A18 Pro CTS was clean. So the simplest fix is to stamp only on G17P: stamped = occlusion && dev->chip == AGX_CHIP_G17P. M3 then keeps the queued store, as on the base. If you'd rather keep it on M3, it needs fragment end only, plus a completion check on readback. I found this in the code and haven't reproduced it on hardware.

What I checked

  • Merges. I reverse-applied omacom's side (4911479..7f0206e, #17 included) to the head tree. 76 of the 83 source files apply as-is. The other seven keep omacom's behaviour:

    • hk_physical_device.c: the G15S native_agx3 gate, with M5 OR'd in (:105-108);
    • asahi_clc.c: g15g next to g17g;
    • agx_device.c: the T8122 USC/VA reservation and agx_va_user_limit();
    • hk_g17p_compression.h: omacom's pNext walk;
    • hk_cmd_draw.c: the variables 87ba417 brings back;
    • the two compiler tests: test-native-form-regressions.cpp keeps Aurora's image-store test, as your description says, and test-preamble-modifiers.cpp differs only by the new argument.
  • UAPI. I compiled both headers (this one, and the kernel's at integration/aurora, which now has #229) and compared every shared struct, member and constant. They match:

    • params_global is 616 bytes, gpu_hal_generation is at 596, and the limits tail is at 600/608;
    • CUMULATIVE_SIGNALS is bit 29, and the ioctl numbers agree;
    • the kernel's get_params copies min(its size, requested), so a pre-#229 kernel leaves the tail zero, and the M3 runtime's 600-byte minimum passes.

    A matching layout says nothing about timestamp semantics, hence the blocker.

  • Shared Mesa code.

    • The NIR additions are opt-in. nir_opt_if_merge is a new entry point, and nir_reassociate_loop_invariant is a new flag; without the flag, rank() is unchanged.
    • vk_queue.c changes every driver on the common runtime, but our package builds only hk (-Dvulkan-drivers=asahi). The dropped-signal path records the loss first and doesn't signal twice.
    • The common BVH leaf changes don't reach M1–M3. Acceleration structures are on only for G17P, G16S and the M5 bring-up profile (hk_physical_device.c:1923-1951), and ray_query = false (:117).
  • M5 and M4 Pro stay off. M5 needs ASAHI_M5_BRINGUP=1, and agx_params_driver_supported() still refuses T6040.

  • CI. The Mesa CI run for this head built PR merge commit bf87c39 on aarch64 and x86_64. All 156 configured meson tests passed on both, including agx_tests, hk_query_oq, vk_queue_dropped_signal and the NIR tests. The kernel ABI check against 6c66b41a matched.

Changes that reach shipped chips, with no hardware run cited for them

These are edge checks after merge, not blockers.

  1. Occlusion counters taken on first use, every chip (c46b868).
    • Counters now come from the hardware heap when a command first uses a query: hk_query_pool.c:803-823 for the allocation and its compare-and-swap, :865-869 for the begin/end path, :1321-1328 for copies recorded before their producer.
    • Running out of counters is now a command-buffer error rather than a pool-creation failure.
    • The code holds up, including a reset recorded before the counter exists; on AGX2 the libagx reset handles the unassigned value.
    • The commit's 450 CTS passes don't name a chip.
  2. Uniform-address atomics, M3 and Neo (agx_compile.c:13063-13073). The base kept nir_opt_uniform_atomics off on all AGX3 (uniform_atomics = !is_agx3). It's now on for AGX3B in the compute, vertex, tessellation-control and fragment stages. That includes G15, despite the is_g17p name (:12992, which checks for AGX3B). Renaming it to is_agx3b would help.
  3. Counter-Strike 2 option, M1/M2 and Neo (6f3da56, 00-hk-defaults.conf:36-46, hk_image.c:488-500).
    • application_name_match="csgo" turns on hk_mutable_format_srgb_pair. That makes CS2's otherwise-eligible mutable images compressible when they have no format list and have an sRGB/UNORM twin. Before, they never were.
    • M3 isn't affected, since allow_agx3 doesn't admit G15 (hk_image.c:435-439).
    • If CS2 ever creates a view through another channel layout, it reads compressed data through the wrong layout; the check only logs once (hk_image_view.c:911-925).
    • It's an app-specific default, so I'm flagging it for the maintainers. Which chips was it run on?
  4. M1 Pro/Max: one command per submission (hk_queue.c:428-438). G13X now submits each control stream in its own ioctl.

Non-blocking

  • UAPI bits the kernel hasn't reserved. This header defines feature bits 17–24, 26 and 28, command types 5–7, and BIND_SPARSE_TILE. linux-aurora defines none of them. Every use is gated on its feature bit, so nothing runs today. But if the kernel later gives one of those bits to something else, Mesa enables an M5 path it doesn't have. Please reserve them in the kernel header, in a small linux-aurora PR.
  • High-priority GL contexts (agx_pipe.c:1951-1964). High and realtime contexts now ask for HIGH and fall back to MEDIUM. On M1–M3 that costs one failed ioctl per such context. Fine as is.
  • Unreferenced test file. src/asahi/compiler/test-agx3-m5-instructions.c isn't built; meson uses compiler/test/test-agx3-m5-instructions.c (src/asahi/lib/meson.build:325).
  • M5 pNext walks. Two M5 loops in hk_g17p_compression.h (381 and 497) still use the memcpy-then-cast walk that omacom replaced at 153. M5 only.

Testing

On edge, after the fix and the merge:

  • occlusion queries on M1 or M2, on M3, and on an M1 Pro/Max (DXVK, or the CTS query_pool occlusion group);
  • an atomics check on M3 (compute and fragment, including returned values);
  • a GL/Vulkan desktop smoke on M1/M2/M3 for the shared register-allocator retry;
  • CS2 on M1/M2.

@chriskearney

Copy link
Copy Markdown

Hardware confirmation of the G15 query issue at ebc78bf, on M3 Pro / G15S B1, using the fixed #235+#229+#244 kernel (7.2.11-9-3504.2-ARCH, #244 071f1581; source union 90e5e49d07aecf3a784a3e82ddd679bc59835173). The same kernel was used for both Mesa arms.

The probe uses PRECISE occlusion queries around 1024×1024 fullscreen draws, one render pass, host reset, no subsequent command touching the pool, and single-query WAIT_BIT|64_BIT reads in order [1,0] or [1,3,2,0] before the fence wait. It then waits for the fence and reads all queries again.

Mesa Variant Rounds Early incorrect reads After-fence incorrect reads
Existing 7f0206e79f9 2 queries 8 0/16 0/16
#19 ebc78bf786f 2 queries 8 16/16 0/16
Existing 7f0206e79f9 4 queries 8 0/32 0/32
#19 ebc78bf786f 4 queries 8 32/32 0/32

Every incorrect early result was 0, with vkGetQueryPoolResults returning success and the submission fence still unsignaled. Every corresponding after-fence result was 1,048,576. Thus the early availability is observably incorrect, including under WAIT_BIT.

Both reported candidate variants completed with the actual pinned staged libvulkan_asahi.so observed in the workload's mappings (SHA256 7202b6ad9c0add949069d8a25a8952ad93e4c99ea369a8df27cfdbbe1ff2398a); processes drained and their fault-cursor checks passed. Probe binary SHA256 31b073dfad3defef7af23cca2d19be254a0267bfd8c9bacfbc64ce6e0a4da779, source-manifest SHA256 6134f7af420d1735364d8f7d6febaf347a49fcb97972bfb9a46273bf155d7cbd.

Bounds: the full four-variant candidate probe did not complete. Its later one-query-plus-timestamp variant ended too quickly for the mapping witness and was rejected; that output is not counted, and the candidate copy-results control was not reached. The existing-Mesa four-variant control completed cleanly. The two basic occlusion CTS cases passed on both Mesa arms, illustrating why those tests alone did not catch this ordering bug. Benchmark results and restoration checks are being collected separately. The G15 changes request remains.

@chriskearney

Copy link
Copy Markdown

Four-board benchmark and validation report for Mesa #19 ebc78bf. Approval remains blocked by the hardware-confirmed G15 occlusion-query bug (reproducer results).

Each board used the same fixed DJ kernel in A and B: source union 90e5e49d07aecf3a784a3e82ddd679bc59835173 (#235 07d64c6b + #229 owner fix 6c66b41a + #244 071f1581), kernel 7.2.11-9-3504.2-ARCH. A used that board’s existing Mesa; B used isolated staged #19 packages. The installed desktop Mesa was not replaced. Controls differ between boards: M1/M1 Air 147a62f6190, M3 Pro 7f0206e79f9, Neo 7c86130d26c.

Seven workloads, one warm-up and three measured runs each, plus clean and deliberately failing compute controls: 30 attributed benchmark runs per arm. All eight selected A/B arms completed their benchmark gates (240 runs including controls/warm-ups), with native driver mapping, result checks, fault-cursor checks and process cleanup. The runner used a private user HOME/cache and the same pinned Vulkan helper in both arms.

Observed median change in submit-and-wait wall time, B versus A. Positive means slower. These are descriptive n=3 samples, not a performance signoff. CPU affinity/frequency were not fixed.

Workload M1 M1 Air M3 Pro Neo
raster +0.1% -0.6% -1.4% -3.2%
texture +1.6% +4.4% +0.4% -0.3%
compute +0.1% +2.5% +0.5% -1.2%
copy +1.9% -1.4% +0.2% -4.3%
submit-single -1.4% -3.1% -25.9% -1.2%
submit-batched +15.2% -3.7% -8.8% +2.1%
bo-lifetime +7.8% -1.5% -4.6% -7.9%
GPU timestamp timing changes for the same runs
Workload M1 M1 Air M3 Pro Neo
raster -0.0% +0.0% -1.2% +0.2%
texture +1.7% +5.0% +0.5% -0.1%
compute -0.9% +1.7% +1.8% -1.2%
copy +0.1% +0.1% -3.0% -7.5%
submit-single -2.3% -2.1% -2.1% -2.2%
submit-batched -0.1% +0.0% +4.3% -2.5%
bo-lifetime -1.1% +0.1% -1.0% -5.0%

The CPU-sensitive numbers varied between jobs. For example, M1 batched-submit was −3.3% in the first #19 arm but +15.2% in the later arm, while GPU time stayed about −0.1%. M3 single-submit control medians ranged from 17.90 to 23.59 ms across A runs; #19 was 17.49 ms. The −25.9% cell above uses the immediately preceding A control and must not be read as an established speedup. Raw min/median/max and CPU/GPU metrics are retained; none of these timings overrides the correctness blocker.

Validation bounds:

  • Native M5 build tests: 122/122 passed, 0 failed/0 skipped (73 Asahi suite tests). The package builder ordinarily skips check(); these were actually run in a separate same-source validation build, not inferred from a green package build.
  • Compiled-layout UAPI comparison against the tested kernel: 277 shared names/values/offsets/sizes matched; optional/new commands remain gated. This is an ABI check, not a GPU conformance result.
  • Existing-Mesa GL/Vulkan pixel checks passed on all four boards under the DJ kernel. Strict staged-Merge Aurora Mesa aurora-wip: G17P submit, refit and leaf-pairing work #19 GL/Vulkan provider/readback validation passed on M1 Air and Neo using an isolated correction to the lab’s provider checker. M1 and M3 did not earn that qualification: M1 stopped at a process-image completion-witness race; M3 stopped on monitor drift and a DCP timing warning also present in its existing-Mesa control boot. Their Vulkan benchmark attribution/results passed separately.
  • The lab’s original provider builtin rejected DRM character-device memory maps as nonregular graphics libraries. Its failures remain recorded; the corrected extra check did not turn those overall jobs into passes. An earlier Air arm also stopped on a GUI traversal permission issue. No infrastructure failure was counted as a Mesa pass.
  • M3 basic occlusion CTS: 2/2 passed in A and B. The focused later-query-first probe independently reproduced 48/48 early zero results in B, with correct after-fence results. Its subsequent short variant lacked an attribution witness and stopped, so this is not a full four-variant B pass.
  • Neo’s focused four-variant OQ control completed cleanly on both existing Mesa and Merge Aurora Mesa aurora-wip: G17P submit, refit and leaf-pairing work #19; this supports the G17 completion-ordering distinction. It does not qualify G15 or all applications.

Selected benchmark jobs:

Board A B
m1 j1028-200240-dc94 j1028-201926-52af
m1air j1028-195658-f834 j1028-201926-0334
m3pro j1028-201526-3568 j1028-201936-ce6b
neo j1028-201526-12fa j1028-202040-a8d7

All four machines returned to their original kernels. Installed packages, boot.bin, original UKIs and original kernel modules were verified; temporary staging and permissions were removed and leases released. M1 Air’s unrelated 03:00 hourly snapshot/menu addition was verified and preserved. This was staged diagnostic coverage, not installed-desktop qualification, full CTS, or RC approval.

Native AGX3 occlusion pools let a render carry a query's availability
in one of its four retirement timestamp slots, and a CPU read that
finds such a timestamp skips the submission wait. That assumes the
kernel writes every slot when it retires the render, after the
end-of-render maintenance that makes the counts visible. The G17
kernel does. On G15 (M3) the firmware writes each slot from its
stage's own start or end record: the vertex end lands when tiling
finishes, before the fragment work writes the counts. With two
queries in one render, the second one rides on the vertex-end slot,
and vkGetQueryPoolResults, even with WAIT_BIT, returns its count as
available before the fragment work wrote it. On an M3 Pro, reading
the later query first before the fence returned 0 in 48 of 48 reads,
against the correct count after the fence.

Add hk_oq_render_stamps_supported(), true only for G17P, where the
path was measured, and gate the stamped pool on it. Every other chip
keeps the queued availability store and the submission wait on
readback, as before the stamped path. Other chips whose kernel writes
the slots at retirement can be added once qualified.

test-hk-query-oq checks the chip decision.

(cherry picked from commit c68abcd)
M5 (G17G/G17S) runs the same G17 kernel runtime as G17P for render
retirement. A render completes only once its completion stamps cover
it, after the end-of-render maintenance (g17/queue/render/retirement.rs,
complete_poll()). The host then writes the four stage timestamps into
the requested slots before signalling the fence (g17/completion.rs,
Output::publish()). Only the render publication and its notification
differ on M5. A stamp in a query's availability slot therefore means
the counts are visible there too, so include G17G and G17S in
hk_oq_render_stamps_supported(). G15 and G16G keep the queued store.

(cherry picked from commit 9876607)
@DjDeveloperr

Copy link
Copy Markdown
Collaborator Author

@maralcbr @chriskearney thanks both. chriskearney, thanks especially for the M3 Pro reproducer.

Blocker (G15 occlusion availability): fixed in 1ccff51 and 2b1bc1e, first merged into aurora-wip as aurora-silicon#94.

  • hk_oq_render_stamps_supported() gates the stamped pool. It returns true only for chips whose kernel writes the four slots at render retirement: G17P, and M5 (G17G/G17S).
  • M5 is included because its kernel runs the same G17 retirement path. A render completes only once its completion stamps cover it, after the end-of-render maintenance (g17/queue/render/retirement.rs, complete_poll()). The host then writes the stage timestamps into the slots right before signalling the fence (g17/completion.rs, Output::publish()).
  • G15 (and G16G) no longer bind the pool's timestamp buffer. So every query end keeps the queued availability store, and readback keeps hk_oq_submission_done(). That's the same path as chriskearney's 7f0206e control arm, which had 0 early reads.
  • test-hk-query-oq now checks the chip decision (55/55). The touched files build with -Dwerror=true.
  • I can't run the later-query-first probe here, since we have no M3. A rerun of chriskearney's probe on this head would close it.

CS2 option (hk_mutable_format_srgb_pair, 6f3da56): it was validated only on the M5 Pro, in our Counter-Strike 2 lane. The commit message describes M5's compressed-data decode, and I have no record of runs on M1/M2 or the Neo. The reasoning carries over: CS2 creates those images mutable without a format list, but only views them through the sRGB/UNORM twin. That's still an untested assumption on those chips. I'm happy to scope the option to M5 in this sync, a one-line gate on the driconf default, so M1/M2 and the Neo keep their current behaviour. Or it can stay as is for the CS2-on-M1/M2 edge check. Your call.

Other points:

  • UAPI bits: I'll reserve feature bits 17–24, 26 and 28, command types 5–7 and BIND_SPARSE_TILE in a small linux-aurora PR and link it here.
  • Uniform atomics on G15 (agx_compile.c): noted. The is_g17p name is misleading, since it means AGX3B; I'll leave the rename out of this sync, as it touches a lot. The M3 atomics edge check covers the behaviour.
  • Unreferenced src/asahi/compiler/test-agx3-m5-instructions.c and the two M5 pNext walks in hk_g17p_compression.h: I'll clean these up on aurora-wip and mirror them here.
  • Edge test list: understood. Our hardware is the Neo and an M2 host, with no M3, so the M1/M2/M3 runs in your list are beyond what I can cover here.

Mesa CI on 2b1bc1e is running: https://github.com/omacom/mesa/actions/runs/40814062291

@DjDeveloperr

Copy link
Copy Markdown
Collaborator Author

UAPI reservation: omacom/linux-aurora#273 reserves feature bits 17–24, 26 and 28, command types 5–7 and DRM_ASAHI_BIND_SPARSE_TILE under this header's names and values (header only; KUnit and a modular build pass). The aurora-silicon mirror is aurora-silicon/linux#568. Mesa CI on 2b1bc1e passed: https://github.com/omacom/mesa/actions/runs/40814062291

src/asahi/compiler/test-agx3-m5-instructions.c is an early copy of
compiler/test/test-agx3-m5-instructions.c. Meson only builds the one
under test/ (src/asahi/lib/meson.build), and nothing else refers to the
stray copy.

(cherry picked from commit 390bcee)
The M5 colour, G17S format-list and M5 depth eligibility checks in
hk_g17p_compression.h copied a whole VkBaseInStructure per chain link
and read VkExternalMemoryImageCreateInfo::handleTypes through a cast of
the chain pointer. Read their chains as the G17P walk on this branch
already does: copy each sType and the next pointer, and copy the
external-memory info into a local. The file now matches aurora-wip.

hk_g17p_compression_test: 4069 checks pass.

(cherry picked from aurora-silicon/mesa commits ea89aed and
3142c63; the G17P walk already had this form here)
@DjDeveloperr

Copy link
Copy Markdown
Collaborator Author

Cleanups from the review are in, both first merged into aurora-wip (aurora-silicon#95, #96):

  • 4418ce2 removes the unreferenced src/asahi/compiler/test-agx3-m5-instructions.c.
  • 463b1a7 makes the M5 colour, G17S format-list and M5 depth chain walks in hk_g17p_compression.h match this branch's G17P walk: copy each sType and the next pointer, and memcpy the external-memory info rather than casting the chain pointer. The file now matches aurora-wip, and hk_g17p_compression_test passes its 4069 checks.

Mesa CI on 463b1a7 passed: https://github.com/omacom/mesa/actions/runs/40815496345

@chriskearney chriskearney left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The G15 occlusion-query blocker is fixed. Hardware-tested head: 2b1bc1e818cf08e091f3d73204494a86759b3e46; approval/source-reviewed head: 463b1a71d47eb83554b3e0efa178a764af6e6479. I reviewed the four-file fix from ebc78bf7 and reran the later-query-first reproducer on the same M3 Pro (G15S/J516S). The two later cleanup commits are accounted for separately below.

Mesa arm Qualified reads Incorrect pre-fence-wait results Incorrect after-fence results Reads returning while the fence was unsignalled
Previous #19 ebc78bf7 (earlier hardware run) 48 48 (all zero) 0 48
Fresh installed control 7f0206e79f9 48 0 0 0
Fixed #19 2b1bc1e8, isolated staged driver 48 0 0 0

Both fresh arms used kernel 7.2.11-9-3504.2-ARCH (source union 90e5e49d), the same probe binary 31b073df / source manifest 6134f7af, 1024×1024 PRECISE queries, 2048 fragment iterations, and eight rounds each of the two-query and four-query variants. Query 1 is read first with single-index WAIT_BIT, before any explicit fence wait. Every value was 1,048,576 in both fresh arms. The runner change only selects those two variants and verifies their 48-read total; the one-query/timestamp and GPU-copy variants were not rerun. This is targeted regression evidence, not full Vulkan conformance.

The fixed driver mapping was witnessed at the job's staged path and matched the production archive member ac2fdacd; the installed driver was not mapped in that arm. Job/input/boot/kernel/library identities, per-run fault cursors, process-group drain and temporary-directory cleanup passed. No new kernel fault appeared during either focused probe. Control job: j1028-205708-5268; fixed-head job: j1028-210158-b1f1. Production build: b1028-205249-676f.

Source reasoning agrees with the hardware result: hk_oq_render_stamps_supported() admits G17P and M5's G17 chips only. G15 pools retain avail_shift == 0 and no stamped pool handle, so query completion uses the queued availability store and CPU readback keeps the submission-completion wait. The new chip tests cover G15/G16 negatives and G17P/G17G/G17S positives. A separate same-head native M5 validation build actually ran 122/122 tests, including 55/55 hk_query_oq checks; the production builder skips check(), so the test result is not inferred from package-build success. CI for the approval head passed the source, x86_64 and aarch64 jobs.

Validation limits: the existing-Mesa GL/Vulkan pixel control passed. The staged arm's managed provider checker still rejected DRM character-device maps; its isolated corrected extra checker could not obtain its required process-image witness at the completion boundary. Those failures remain recorded, and the overall staged job is not presented as a pass or as staged GL/installed-desktop qualification. They do not invalidate the separately attributed Vulkan reproducer. The stock-Mesa system-check control passed. On the fixed-head boot, OMT flagged hardware.probe-errors and input.ambient-light: AOP firmware had crashed during startup, before the staged provider tests. Those failures are retained separately; no Mesa cause is established by this run.

The M3 Pro returned to its original 7.2.11-9-1-ARCH boot with AOP healthy and both ambient-light IIO devices present. Original boot.bin, UKIs, modules, configs, package versions and known-good records matched the saved audit. The original agent and parent permissions were restored, audio mute/volume matched exactly, staging was removed and this campaign’s lease released.

Successor 463b1a71 adds only two changes after the hardware-tested head: deletion of an unbuilt duplicate M5 instruction-test source (the registered copy is unchanged), and pNext copying in three M5 compression helpers. All three helpers reject non-M5 chips before walking the chain; the OQ files are byte-identical. The existing compression policy host suite passed 4,069 checks with both old and new headers. Correction after function-targeted mutation checks: all three chip-gate removals are caught. HAL200/G17P chain truncation is caught, while M5 colour, MSAA and depth later-link truncations are not caught at this head. The initial occurrence-based mutant labels incorrectly called the HAL200 walk the M5 colour walk; the corrected results are recorded in the later approval review. This successor review is source/host evidence, not another hardware run.

Approving this reviewed head: the blocking correctness regression is closed by the source fix and focused M3 hardware evidence. The earlier four-board benchmark report remains descriptive coverage of ebc78bf7; its small-sample timings are not a performance signoff, and no new Neo/M5 hardware result is claimed here.

The M5 depth eligibility test only put external memory as the first
chain link, so a walk that stopped after one link still passed. Check a
format list followed by external memory, and by the WSI marker, as
refusals, and the format list alone as eligible.

hk_g17p_compression_test: 4078 checks pass; the same test fails when the
depth walk stops after its first link.

(cherry picked from commit 4476f9c)
@DjDeveloperr

Copy link
Copy Markdown
Collaborator Author

@chriskearney thanks for the re-run and the review. On the test gap you found, acb855f (aurora-silicon#97) puts external memory and the WSI marker behind a format list in the M5 depth chain test. The suite is now 4078 checks, and it fails when the depth walk stops after its first link. Test-only. Mesa CI on acb855f passed: https://github.com/omacom/mesa/actions/runs/40816598657

@chriskearney chriskearney left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval refreshed at acb855f. It adds only the later-link M5 depth-chain regression tests; all production code, including the fixed G15 OQ path, is byte-identical to the previously reviewed head.

The existing host compression test passes 4,078 checks. An accurately targeted depth-walk truncation passes the old test and fails the new test at the later external-memory link, confirming the added regression coverage. Current source/x86_64/aarch64 CI is green.

Correction to the mutation labels in my earlier review: the initial script selected repeated statements by occurrence and labelled the HAL200/G17P walk as M5 colour. A function-targeted rerun shows HAL200 truncation is caught, and the new commit now catches M5 depth truncation. M5 colour and MSAA later-link truncation remain uncovered; all three chip-gate removals are caught. The earlier review text has been corrected. These are coverage limits, not observed failures in the actual refactor.

The M3 hardware evidence and its limits still apply: exact tested driver head 2b1bc1e8 returned 48/48 correct query values, versus 48 early zeros before the fix, on the same kernel and probe. No hardware run is claimed for the later M5-only cleanup or this test-only commit. The broader provider-check and AOP/OMT failures remain recorded separately; the M3 originals were restored and verified. No merge performed.

@maralcbr maralcbr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving at acb855f. The M3 blocker is fixed: hk_oq_render_stamps_supported() limits stamped pools to G17P and M5. G15 and G16G pools get no timestamp binding and avail_shift == 0, so they keep the queued availability store, and readback keeps hk_oq_submission_done() (hk_query_pool.c:576-578, 647, 969-976, 1235-1237). GPU copies pass the real shift. Chris's M3 Pro rerun on 2b1bc1e read 48/48 correct values, against 48 early zeros before the fix. The later commits only touch M5 or tests, and CI on this head passed.

Thanks for the cleanups and omacom/linux-aurora#273.

Still open after merge:

  • CS2 option: validated on M5 Pro only. Leaving it as is for now; the CS2-on-M1/M2 edge check decides whether it gets scoped to M5.
  • Edge checks from my earlier review: occlusion queries on M1/M2 and M1 Pro/Max, atomics on M3, and a desktop smoke test.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants