# iter19 — XFB store channel-extract fix for packed varyings (campaign close) Shipped: 2026-05-25 PR: https://git.reauktion.de/marfrit/marfrit-packages/pulls/96 Merge commit: 902de73a02d9 Package: `mesa-panvk-bifrost-26.0.6.r7-1-aarch64` ## What shipped Single 21+/3- line patch to `src/panfrost/vulkan/panvk_vX_xfb_lower.c` (`0007-panvk-bifrost-xfb-component-base-fix.patch`). Eliminates a reproducible SIGSEGV in `vkCreateGraphicsPipeline` for any shader with XFB-bound varyings declared at non-zero `layout (component=N)`. ## How surfaced The r5 full dEQP-VK sweep (2,258,378 tests, 97.65% runnable pass rate) on 2026-05-24/25 included 27 fails in `transform_feedback` plus a SIGKILL on the chunk when `transform_feedback.simple.holes_vert` hit `FATAL ERROR: Test program crashed`. Isolated repro produced a userspace SIGSEGV with no kernel GPU fault (dmesg clean), pointing at a pure libvulkan_panfrost bug. ## Root cause `lower_xfb_output_iter17` (and identically upstream `pan_nir_lower_xfb.c::lower_xfb_output`, which carries a `// TODO`) computed the source-channel mask as `mask << channel_idx`. `channel_idx` is the varying-location component (0..3) but `src` only contains channels starting at `nir_intrinsic_component(intr)`. For a scalar declared `layout (component=2) flat out float vegeta`, NIR emits `store_output src=, component=2`, and the lowering computed `mask << 2` against a 1-component src — out-of-range; the malformed nir_def then segfaulted during downstream NIR constant-folding in `nir_constant_expressions.c::evaluate_*`. The `assert(nir_intrinsic_component(intr) == 0)` precondition was inherited from upstream Mesa as a documented `// TODO`; release builds (`-DNDEBUG`) elided it, turning the precondition violation directly into a SIGSEGV. ## Fix 1. Compute `src_channel = channel_idx - nir_intrinsic_component(intr)` and use `mask << src_channel` instead. 2. Convert both elided asserts to explicit release-mode early-return guards (closes the same elision class as the original bug). 3. Add a dispatcher-side comment explaining why `i*2+j` is the varying-location component index. ## Verification | Family | Result | |----------------------------------------------|-------------------| | `transform_feedback.simple.holes_vert` | Crash → Fail (color) | | `transform_feedback.simple.holes_extra_draw_vert` | Crash → Fail (color) | | `transform_feedback.simple.basic_*` | 36/36 Pass | | `transform_feedback.simple.depth_clip_*` | 1 Pass + 4 NotSupp | | `transform_feedback.simple.lines_or_triangles*` | 16 NotSupp | | `transform_feedback.simple.holes_geom*` | NotSupp (no GS on G52) | Zero new regressions on previously passing tests. ## Process Full 8-step bugfix process followed: - Phase 0 — characterize: `iter19/phase0_holes_vert_close.md` - Phase 1 — source map: `iter19/phase1_holes_vert_situation.md` - Phase 2 — root-cause + ranked options: `iter19/phase2_holes_vert_situation.md` - Phase 3 — implement + inner-test: `iter19/phase3_holes_vert_close.md` - Phase 4 — wider verify (focused subset) - Phase 5 — 2nd-model review: **APPROVE WITH CHANGES (non-blocking)** → defensive-guards added - Phase 7 — final retest: clean - Phase 8 — ship: r7 PR → CI green → marfrit repo published → ohm `pacman -S` upgraded → smoke test confirms Crash→Fail ## Three-point ship-check verdict 1. ✓ PR #96 merged at `902de73a02d9` 2. ✓ CI runs #1380 (`mesa-panvk-bifrost-aarch64`) and #1381 (`mesa-panvk-bifrost-video-aarch64`) both `success` 3. ✓ `pacman -Q mesa-panvk-bifrost` on ohm reports `26.0.6.r7-1`; installed lib BuildID `9f8dacfc...`; holes_vert no longer SIGSEGVs ## Open follow-ups (not blocking r7 ship) - **iter20: holes_vert color-check residual** — when `panvk_per_arch(nir_lower_xfb)` removes the original store_output post-XFB-lowering, varyings that are *both* XFB-bound *and* read by the fragment shader lose their rasterizer-side path. holes_vert's fragment shader sees `goku`/`vegeta` as undef → outputs black instead of expected blue. Naive "keep store_output" probe fixed color but blew up compile-time on `max_output_components_128` — needs a more nuanced fix. - **iter20+: pre-existing latent crashes** unmasked by r7 — `dEQP-VK.transform_feedback.simple.max_output_components_{64,128,256}` coredump on shipped r6 baseline too (confirmed independently); they were never reached during the r5 sweep because the watchdog killed transform_feedback after holes_vert. ## Lineage | rev | What | Date | |-----|-----------------------------------------------|------------| | r1 | KHR_robustness2 + nullDescriptor + nullDescriptor on Bifrost | iter8 | | r2 | has_vk1_1/has_vk1_2 = true on Bifrost | iter9 | | r3 | VK_EXT_transform_feedback (iter13) | iter13 | | r4 | XFB primitive decomposition (iter17) | iter17 | | r5 | fragmentStoresAndAtomics = true | 2026-05-23 | | r6 | VK_EXT_legacy_dithering | 2026-05-25 | | **r7** | **XFB packed-varying channel-extract fix** | **2026-05-25** |