# Iteration 4 close — GREEN Closed **2026-05-19**, same session as iter1+2+3. ## Locked question (From [phase0_findings_iter4.md](phase0_findings_iter4.md)) > Sample a 4×4 R8G8B8A8_UNORM source texture (uploaded via staging buffer + vkCmdCopyBufferToImage) in a fragment shader via texelFetch into a 64×64 attachment. Verify every output pixel at (col, row) equals source texel at (col%4, row%4). ## Result: GREEN 7/7 runs PASS (1 baseline + 1 validation + 5 stability), all 4096 pixels match the tile-repeated 4×4 pattern. No GPU faults, no validation warnings. Evidence: [`phase0_evidence/iter4_texture_run.txt`](phase0_evidence/iter4_texture_run.txt). ## What the close tells us All six hypotheses in [phase0_findings_iter4.md](phase0_findings_iter4.md) were tested. **None materialized.** The headline hypothesis — that the Bifrost descriptor model would fail first — did not. PanVk-Bifrost's descriptor handling on Mali-G52 r1 v7 works for COMBINED_IMAGE_SAMPLER fragment-stage bindings. | Hypothesis | Status | |---|---| | H1: Source texture upload (`vkCmdCopyBufferToImage`) | ✗ works | | H2: Layout transition TRANSFER_DST → SHADER_READ_ONLY_OPTIMAL | ✗ works | | H3: `VkSampler` creation | ✗ works | | H4: COMBINED_IMAGE_SAMPLER descriptor binding (Bifrost desc table model) | ✗ works | | H5: NIR lowering for texelFetch on Bifrost | ✗ works | | H6: Bifrost sampled-image read ISA emission | ✗ works | ## Cumulative state (iter1+2+3+4) PanVk-Bifrost on Mali-G52 r1 v7 (Mesa 26.0.6) is functional for: - Pure Vulkan 1.0 instance + KHR extension chains - Compute pipeline + dispatch - Graphics pipeline + dynamic rendering + tile binning - Image creation, layout transitions, color attachment - Storage buffer + uniform sampler descriptor types - Texture upload (linear buffer → optimal-tiled image) - Sampled-image read via `texelFetch` - All barrier flavors (memory, buffer, image) - All transfer ops (CopyBufferToImage, CopyImageToBuffer, ClearColorImage) **Combined zero failures across ~28 total runs.** The driver gate "not well-tested on v7" remains defensive, not load-bearing. ## iter4 in-tree artifacts - [`iter4/probe_texture.c`](iter4/probe_texture.c) — texture probe - [`iter4/probe_texture.vert`](iter4/probe_texture.vert) — fullscreen tri (reused) - [`iter4/probe_texture.frag`](iter4/probe_texture.frag) — texelFetch frag - [`iter4/Makefile`](iter4/Makefile) ## Next iter — iter5 lock proposal The campaign is moving faster than predicted. Two natural next moves: **A. Vertex buffer + UBO (still small step):** add `VK_BUFFER_USAGE_VERTEX_BUFFER_BIT` + bind via `vkCmdBindVertexBuffers`, add UBO with a transform matrix, render a non-fullscreen triangle that uses both. Stress: vertex input bindings + attribute descriptions (Bifrost differs from Valhall here), UBO descriptor type, push-constant-ish data flow. **B. Skip ahead to a real workload:** ship `vkcube` or `vkmark` with `PAN_I_WANT_A_BROKEN_VULKAN_DRIVER=1` + headless surface, see where they fail under sustained use. This jumps past many minor probes and finds whatever's actually broken in real-world patterns. Going with **A**, since the operator's stated goal is TuxRacer-smoothness via Zink-on-PanVk and a sustained-app probe is closer to an iter6+ stress test than a focused iter5 lock. iter5 question: > **Render a non-fullscreen colored triangle: vertex shader reads vec2 position + vec3 color from a vertex buffer (3 vertices, 20 bytes each — pos+pad+color), applies a transform matrix from a UBO, outputs interpolated color. UBO holds an identity-with-scale matrix. Render into 64×64 R8G8B8A8_UNORM attachment. Verify: (a) center pixel of the triangle has interpolated color matching the average of the 3 vertex colors, (b) at least one pixel outside the triangle remains in the clear color.** Lock when operator opens iter5.