initial seed: retrofit campaign lineage from local working trees

panvk-bifrost campaigns (r1..r4 Vulkan compositor + r5.video1 Vulkan
video decode) shipped before this repo existed; the deliverable
patches live in marfrit-packages, but the reasoning chain, phase docs,
and source-state evidence lived only in local working trees on the
development host.

This retrofit imports:
- mesa-panvk-bifrost/   — r1..r4 era phase docs (iter1..iter18)
                          (libmali stub blobs at iter18/blob/ excluded
                          — 109MB of RE artifacts replaced with a README
                          pointer)
- mesa-panvk-bifrost-video/ — sibling campaign phase docs + probe
- evidence/             — frozen .tgz source snapshots at each milestone
                          (basis for the 0005 patch diff generation)

Future iterations should branch off here from day one, so each iter is
a commit rather than a snapshot. See [[feedback-session-local-process-pins]]
for the process drift this retrofit closes.

Total: 1.9 MB across 124 files.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-23 05:25:37 +02:00
parent 430d0da278
commit a4e7d8ab90
124 changed files with 22551 additions and 1 deletions
@@ -0,0 +1,442 @@
#!/usr/bin/env python3
"""
iter13: apply VK_EXT_transform_feedback implementation to Mesa 26.0.6 PanVk.
Run from inside /home/mfritsche/mesa-build/mesa-26.0.6/ on ohm.
Idempotent — checks if changes are already present and skips if so.
The implementation is single-variant (Vulkan spec allows undefined behavior
for XFB-output shaders bound outside Begin/EndTransformFeedback, so we
don't need defensive two-variant compilation for v1).
Files modified:
1. src/panfrost/vulkan/panvk_shader.h
2. src/panfrost/vulkan/panvk_vX_physical_device.c
3. src/panfrost/vulkan/panvk_vX_shader.c
4. src/panfrost/vulkan/panvk_cmd_draw.h
5. src/panfrost/vulkan/jm/panvk_vX_cmd_draw.c
6. src/panfrost/vulkan/meson.build
Files created:
7. src/panfrost/vulkan/jm/panvk_vX_cmd_xfb.c
"""
import os
import sys
ROOT = os.path.abspath(os.path.dirname(__file__)) if "MESA_ROOT" not in os.environ else os.environ["MESA_ROOT"]
# Default: assume cwd is mesa root
if os.path.basename(os.getcwd()).startswith("mesa-"):
ROOT = os.getcwd()
print(f"[iter13] applying patches under {ROOT}")
def replace_once(path, old, new, marker_in_new=None):
"""Replace `old` with `new` in file at path. If `marker_in_new` is in the
file already, treat as already-applied and skip."""
full = os.path.join(ROOT, path)
with open(full) as f:
content = f.read()
if marker_in_new and marker_in_new in content:
print(f" [skip] {path} — already patched ({marker_in_new!r} present)")
return
if old not in content:
print(f" [FAIL] {path} — expected pattern not found:\n {old[:100]!r}")
sys.exit(2)
count = content.count(old)
if count > 1:
print(f" [FAIL] {path} — pattern matches {count} times, need exactly 1")
sys.exit(2)
new_content = content.replace(old, new)
with open(full, "w") as f:
f.write(new_content)
print(f" [ok] {path}")
def create_file(path, content, skip_if_exists=True):
full = os.path.join(ROOT, path)
if skip_if_exists and os.path.exists(full):
print(f" [skip] {path} — exists")
return
os.makedirs(os.path.dirname(full), exist_ok=True)
with open(full, "w") as f:
f.write(content)
print(f" [ok] {path} (created)")
# ============================================================
# 1. panvk_shader.h — extend vs sysval struct (PAN_ARCH < 9)
# ============================================================
print("\n[1/7] panvk_shader.h — add num_vertices + xfb_address[4] to vs sysvals")
replace_once(
"src/panfrost/vulkan/panvk_shader.h",
""" struct {
#if PAN_ARCH < 9
int32_t raw_vertex_offset;
#endif
int32_t first_vertex;
int32_t base_instance;
uint32_t noperspective_varyings;
} vs;""",
""" struct {
#if PAN_ARCH < 9
int32_t raw_vertex_offset;
uint32_t num_vertices; /* iter13: XFB needs per-draw vertex count */
uint32_t _pad_xfb; /* keep 8-byte alignment before u64 array */
aligned_u64 xfb_address[4]; /* iter13: 4 transform feedback buffer base addresses */
#endif
int32_t first_vertex;
int32_t base_instance;
uint32_t noperspective_varyings;
} vs;""",
marker_in_new="xfb_address[4]",
)
# ============================================================
# 2. panvk_vX_physical_device.c — expose ext + features + properties
# ============================================================
print("\n[2/7] panvk_vX_physical_device.c — expose VK_EXT_transform_feedback")
# A. Add extension to the ext list (find a stable nearby line)
replace_once(
"src/panfrost/vulkan/panvk_vX_physical_device.c",
" .EXT_robustness2 = true,",
""" .EXT_robustness2 = true,
.EXT_transform_feedback = PAN_ARCH < 9, /* iter13: JM-class only for now */""",
marker_in_new="EXT_transform_feedback",
)
# B. Add features. The features block has /* VK_KHR_robustness2 */ nearby.
replace_once(
"src/panfrost/vulkan/panvk_vX_physical_device.c",
""" /* VK_KHR_robustness2 */
.robustBufferAccess2 = PAN_ARCH >= 11,
.robustImageAccess2 = false,
.nullDescriptor = true,""",
""" /* VK_KHR_robustness2 */
.robustBufferAccess2 = PAN_ARCH >= 11,
.robustImageAccess2 = false,
.nullDescriptor = true,
/* VK_EXT_transform_feedback (iter13) */
.transformFeedback = PAN_ARCH < 9,
.geometryStreams = false,""",
marker_in_new=".transformFeedback = PAN_ARCH < 9",
)
# C. Add properties. Anchor to the existing /* VK_KHR_robustness2 */ properties
# block near line 1019. We'll add right after it.
replace_once(
"src/panfrost/vulkan/panvk_vX_physical_device.c",
""" /* VK_KHR_robustness2 */
.robustStorageBufferAccessSizeAlignment = 1,
.robustUniformBufferAccessSizeAlignment = 1,""",
""" /* VK_KHR_robustness2 */
.robustStorageBufferAccessSizeAlignment = 1,
.robustUniformBufferAccessSizeAlignment = 1,
/* VK_EXT_transform_feedback (iter13) */
.maxTransformFeedbackStreams = 1,
.maxTransformFeedbackBuffers = 4,
.maxTransformFeedbackBufferSize = UINT32_MAX,
.maxTransformFeedbackStreamDataSize = 512,
.maxTransformFeedbackBufferDataSize = 512,
.maxTransformFeedbackBufferDataStride = 2048,
.transformFeedbackQueries = false,
.transformFeedbackStreamsLinesTriangles = false,
.transformFeedbackRasterizationStreamSelect = false,
.transformFeedbackDraw = false,""",
marker_in_new="maxTransformFeedbackStreams",
)
# ============================================================
# 3. panvk_vX_shader.c — intrinsic lowering + NIR pass wiring
# ============================================================
print("\n[3/7] panvk_vX_shader.c — intrinsic lowering + pan_nir_lower_xfb wiring")
# A. Add intrinsic cases inside the PAN_ARCH < 9 block.
# Anchor to the existing `vs.raw_vertex_offset` case.
replace_once(
"src/panfrost/vulkan/panvk_vX_shader.c",
"""#if PAN_ARCH < 9
case nir_intrinsic_load_raw_vertex_offset_pan:
val = load_sysval(b, graphics, bit_size, vs.raw_vertex_offset);
break;""",
"""#if PAN_ARCH < 9
case nir_intrinsic_load_raw_vertex_offset_pan:
val = load_sysval(b, graphics, bit_size, vs.raw_vertex_offset);
break;
case nir_intrinsic_load_num_vertices: /* iter13: XFB index calc */
val = load_sysval(b, graphics, bit_size, vs.num_vertices);
break;
case nir_intrinsic_load_xfb_address: { /* iter13: XFB buffer N base address */
unsigned idx = nir_intrinsic_base(intr);
switch (idx) {
case 0: val = load_sysval(b, graphics, bit_size, vs.xfb_address[0]); break;
case 1: val = load_sysval(b, graphics, bit_size, vs.xfb_address[1]); break;
case 2: val = load_sysval(b, graphics, bit_size, vs.xfb_address[2]); break;
case 3: val = load_sysval(b, graphics, bit_size, vs.xfb_address[3]); break;
default: return false;
}
break;
}""",
marker_in_new="load_num_vertices",
)
# B. Wire pan_nir_lower_xfb into the lowering chain.
# We want it right after nir_lower_system_values runs.
# Look for the existing call.
replace_once(
"src/panfrost/vulkan/panvk_vX_shader.c",
""" NIR_PASS(_, nir, nir_lower_system_values);
nir_lower_compute_system_values_options options = {""",
""" NIR_PASS(_, nir, nir_lower_system_values);
#if PAN_ARCH < 9
/* iter13: VK_EXT_transform_feedback — if the shader has XFB output
* decorations, run the Mesa standard XFB-info NIR pass + Panfrost's
* own NIR lowering that turns store_output into nir_store_global
* to the per-buffer base address (the panvk lowering above wires
* nir_load_xfb_address to vs.xfb_address[N]). Single-variant: if
* an app binds an XFB pipeline outside vkCmdBeginTransformFeedback,
* the writes go to address 0 — undefined behavior per spec. */
if (nir->info.stage == MESA_SHADER_VERTEX &&
nir->xfb_info != NULL) {
NIR_PASS(_, nir, pan_nir_lower_xfb);
}
#endif
nir_lower_compute_system_values_options options = {""",
marker_in_new="pan_nir_lower_xfb",
)
# C. Add #include for pan_nir.h at the top (where pan_nir_lower_xfb is declared)
replace_once(
"src/panfrost/vulkan/panvk_vX_shader.c",
'#include "panvk_shader.h"',
'#include "panvk_shader.h"\n#include "pan_nir.h" /* iter13: pan_nir_lower_xfb */',
marker_in_new='/* iter13: pan_nir_lower_xfb */',
)
# ============================================================
# 4. panvk_cmd_draw.h — add XFB state struct + pipeline state member
# ============================================================
print("\n[4/7] panvk_cmd_draw.h — add panvk_xfb_state to cmd buffer state")
# We add a definition and inject xfb into the graphics state.
# We need to find the right place. Looking at the file: there's a `struct
# panvk_graphics_state` or similar that holds per-cmdbuf graphics state.
# This is intrinsically file-specific; we need to read the file to find the right spot.
# For now, place a self-contained inclusion at the top of the file and add
# state as a separate sibling struct in the gfx state. The cleaner long-term
# place is inside the existing graphics state struct.
# Defer the inclusion approach. Instead use a forward declaration + put the
# struct definition in jm/panvk_vX_cmd_xfb.c and reference via include.
# Actually let's just add a state struct to panvk_cmd_draw.h after the sysvals member.
replace_once(
"src/panfrost/vulkan/panvk_cmd_draw.h",
" struct panvk_graphics_sysvals sysvals;",
""" struct panvk_graphics_sysvals sysvals;
#if PAN_ARCH < 9
/* iter13: VK_EXT_transform_feedback state (JM-class only for now). */
struct {
bool active;
uint32_t buffer_count;
struct {
uint64_t addr;
uint64_t offset;
uint64_t size;
} buffers[4];
} xfb;
#endif""",
marker_in_new="iter13: VK_EXT_transform_feedback state",
)
# ============================================================
# 5. panvk_vX_cmd_draw.c (arch-templated, NOT jm/) — populate XFB sysvals
# ============================================================
print("\n[5/7] panvk_vX_cmd_draw.c — populate vs.num_vertices + vs.xfb_address[] inside the PAN_ARCH<9 block")
# Insert just inside the existing `#if PAN_ARCH < 9` block where
# raw_vertex_offset is set. info->vertex.count is available in scope.
replace_once(
"src/panfrost/vulkan/panvk_vX_cmd_draw.c",
"""#if PAN_ARCH < 9
set_gfx_sysval(cmdbuf, dirty_sysvals, vs.raw_vertex_offset,
info->vertex.raw_offset);
set_gfx_sysval(cmdbuf, dirty_sysvals, layer_id, info->layer_id);
#endif""",
"""#if PAN_ARCH < 9
set_gfx_sysval(cmdbuf, dirty_sysvals, vs.raw_vertex_offset,
info->vertex.raw_offset);
set_gfx_sysval(cmdbuf, dirty_sysvals, layer_id, info->layer_id);
/* iter13: VK_EXT_transform_feedback sysvals — always set (per draw),
* reflect bound XFB state. set_gfx_sysval is a no-op if value unchanged. */
set_gfx_sysval(cmdbuf, dirty_sysvals, vs.num_vertices, info->vertex.count);
{
const struct panvk_cmd_graphics_state *_gfx = &cmdbuf->state.gfx;
uint64_t _xa0 = 0, _xa1 = 0, _xa2 = 0, _xa3 = 0;
if (_gfx->xfb.active) {
if (_gfx->xfb.buffer_count > 0)
_xa0 = _gfx->xfb.buffers[0].addr + _gfx->xfb.buffers[0].offset;
if (_gfx->xfb.buffer_count > 1)
_xa1 = _gfx->xfb.buffers[1].addr + _gfx->xfb.buffers[1].offset;
if (_gfx->xfb.buffer_count > 2)
_xa2 = _gfx->xfb.buffers[2].addr + _gfx->xfb.buffers[2].offset;
if (_gfx->xfb.buffer_count > 3)
_xa3 = _gfx->xfb.buffers[3].addr + _gfx->xfb.buffers[3].offset;
}
set_gfx_sysval(cmdbuf, dirty_sysvals, vs.xfb_address[0], _xa0);
set_gfx_sysval(cmdbuf, dirty_sysvals, vs.xfb_address[1], _xa1);
set_gfx_sysval(cmdbuf, dirty_sysvals, vs.xfb_address[2], _xa2);
set_gfx_sysval(cmdbuf, dirty_sysvals, vs.xfb_address[3], _xa3);
}
#endif""",
marker_in_new="iter13: VK_EXT_transform_feedback sysvals",
)
# ============================================================
# 6. NEW: jm/panvk_vX_cmd_xfb.c — Vulkan command handlers
# ============================================================
print("\n[6/7] jm/panvk_vX_cmd_xfb.c — XFB Vulkan command handlers (NEW FILE)")
xfb_c = r'''/*
* Copyright © 2026 mfritsche / claude-noether
* SPDX-License-Identifier: MIT
*
* iter13: VK_EXT_transform_feedback command handlers for the JM
* architecture path (Bifrost v6/v7 + Valhall-JM v9).
*
* The runtime contract:
* - vkCmdBindTransformFeedbackBuffersEXT: stash (gpu_addr, offset, size)
* for each slot into cmdbuf->state.gfx.xfb.buffers[].
* - vkCmdBeginTransformFeedbackEXT: set cmdbuf->state.gfx.xfb.active = true.
* Mark sysvals dirty so the next draw re-emits vs.xfb_address[].
* - vkCmdEndTransformFeedbackEXT: set active = false.
*
* Counter buffers (firstCounterBuffer/counterBufferCount/pCounterBuffers/
* pCounterBufferOffsets) are accepted by API but ignored — v1 doesn't
* support pause/resume. transformFeedbackDraw is advertised as false.
*
* Per-draw integration: jm/panvk_vX_cmd_draw.c reads cmdbuf->state.gfx.xfb
* and populates vs.xfb_address[i] for shader use. The pan_nir_lower_xfb
* pass in panvk_vX_shader.c emits nir_load_xfb_address(i) which lowers
* (via panvk_vX_shader.c sysval handler) to a load from the per-draw
* sysval push area.
*/
#include "vk_log.h"
#include "panvk_cmd_buffer.h"
#include "panvk_cmd_draw.h"
#include "panvk_buffer.h"
#include "panvk_entrypoints.h"
VKAPI_ATTR void VKAPI_CALL
panvk_per_arch(CmdBindTransformFeedbackBuffersEXT)(
VkCommandBuffer commandBuffer,
uint32_t firstBinding,
uint32_t bindingCount,
const VkBuffer *pBuffers,
const VkDeviceSize *pOffsets,
const VkDeviceSize *pSizes)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
struct panvk_cmd_graphics_state *gfx = &cmdbuf->state.gfx;
for (uint32_t i = 0; i < bindingCount; i++) {
uint32_t slot = firstBinding + i;
if (slot >= 4)
continue;
VK_FROM_HANDLE(panvk_buffer, buf, pBuffers[i]);
gfx->xfb.buffers[slot].addr = panvk_buffer_gpu_ptr(buf, 0);
gfx->xfb.buffers[slot].offset = pOffsets[i];
gfx->xfb.buffers[slot].size =
(pSizes != NULL && pSizes[i] != VK_WHOLE_SIZE)
? pSizes[i]
: (buf->vk.size - pOffsets[i]);
}
if (firstBinding + bindingCount > gfx->xfb.buffer_count)
gfx->xfb.buffer_count = firstBinding + bindingCount;
}
VKAPI_ATTR void VKAPI_CALL
panvk_per_arch(CmdBeginTransformFeedbackEXT)(
VkCommandBuffer commandBuffer,
uint32_t firstCounterBuffer,
uint32_t counterBufferCount,
const VkBuffer *pCounterBuffers,
const VkDeviceSize *pCounterBufferOffsets)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
struct panvk_cmd_graphics_state *gfx = &cmdbuf->state.gfx;
/* Counter buffers ignored in v1 — see VkPhysicalDeviceTransformFeedback
* PropertiesEXT.transformFeedbackDraw = false in panvk_vX_physical_device.c.
*/
(void)firstCounterBuffer;
(void)counterBufferCount;
(void)pCounterBuffers;
(void)pCounterBufferOffsets;
gfx->xfb.active = true;
/* Per-draw set_gfx_sysval picks up the change automatically — no
* explicit dirty marking required (set_gfx_sysval uses memcmp +
* BITSET to detect state diffs and re-emit sysvals). */
}
VKAPI_ATTR void VKAPI_CALL
panvk_per_arch(CmdEndTransformFeedbackEXT)(
VkCommandBuffer commandBuffer,
uint32_t firstCounterBuffer,
uint32_t counterBufferCount,
const VkBuffer *pCounterBuffers,
const VkDeviceSize *pCounterBufferOffsets)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
struct panvk_cmd_graphics_state *gfx = &cmdbuf->state.gfx;
(void)firstCounterBuffer;
(void)counterBufferCount;
(void)pCounterBuffers;
(void)pCounterBufferOffsets;
gfx->xfb.active = false;
}
'''
create_file("src/panfrost/vulkan/jm/panvk_vX_cmd_xfb.c", xfb_c)
# ============================================================
# 7. meson.build — register the new file in the jm_files array
# ============================================================
print("\n[7/7] meson.build — register jm/panvk_vX_cmd_xfb.c")
replace_once(
"src/panfrost/vulkan/meson.build",
"jm_files = [\n 'jm/panvk_vX_bind_queue.c',",
"jm_files = [\n 'jm/panvk_vX_bind_queue.c',\n 'jm/panvk_vX_cmd_xfb.c', # iter13",
marker_in_new="iter13",
)
print("\n[iter13] all patches applied — run incremental ninja build next")