/* * Copyright © 2026 mfritsche / claude-noether * SPDX-License-Identifier: MIT * * iter16: primitive-decomposition tables for transform_feedback capture * on PanVk-Bifrost (PAN_ARCH < 9 only). When XFB is active and the bound * topology is a strip/fan/adjacency variant, the Vulkan spec requires * vertices to be captured AS IF the primitive sequence were decomposed * into a list of independent primitives. iter13's pan_nir_lower_xfb * captures one entry per VS invocation, which gives one output per input * vertex — wrong for non-LIST topologies. * * This file holds the seven decomposition tables (one per affected * topology). Caller (jm/panvk_vX_cmd_draw.c CmdDraw) walks the table to * build a synthetic index buffer, overrides the bound topology to the * equivalent LIST, and dispatches as an indexed draw — the existing * pan_nir_lower_xfb formula then writes the right number of entries in * the right order. * * See ~/src/panvk-bifrost/iter16/phase2_design.md for the design lock. */ #include "panvk_macros.h" #if PAN_ARCH < 9 #include "panvk_cmd_draw.h" #include /* TRIANGLE_STRIP: 3*(N-2) outputs. * Even prim i: {i, i+1, i+2} * Odd prim i: {i, i+2, i+1} ← winding reverses, hence "winding" tests */ static uint32_t prim_count_tri_strip(uint32_t n) { return (n >= 2) ? (n - 2) : 0; } static void expected_tri_strip(uint32_t i, uint32_t *out) { uint32_t iMod2 = i & 1u; out[0] = i; out[1] = i + 1 + iMod2; out[2] = i + 2 - iMod2; } /* LINE_STRIP: 2*(N-1) outputs. Each prim i: {i, i+1} */ static uint32_t prim_count_line_strip(uint32_t n) { return (n >= 1) ? (n - 1) : 0; } static void expected_line_strip(uint32_t i, uint32_t *out) { out[0] = i; out[1] = i + 1u; } /* TRIANGLE_FAN: 3*(N-2) outputs. Each prim i: {i+1, i+2, 0} */ static uint32_t prim_count_tri_fan(uint32_t n) { return (n >= 2) ? (n - 2) : 0; } static void expected_tri_fan(uint32_t i, uint32_t *out) { out[0] = i + 1u; out[1] = i + 2u; out[2] = 0u; } /* LINE_LIST_WITH_ADJACENCY: N/4 primitives, each emits {i+1, i+2} from * the 4-vertex input window (i, i+1, i+2, i+3). N must be a multiple of 4. */ static uint32_t prim_count_line_list_adj(uint32_t n) { return n / 4u; } static void expected_line_list_adj(uint32_t i, uint32_t *out) { out[0] = 4 * i + 1u; out[1] = 4 * i + 2u; } /* LINE_STRIP_WITH_ADJACENCY: 2*(N-3) outputs. Each prim i: {i+1, i+2} */ static uint32_t prim_count_line_strip_adj(uint32_t n) { return (n >= 3) ? (n - 3) : 0; } static void expected_line_strip_adj(uint32_t i, uint32_t *out) { out[0] = i + 1u; out[1] = i + 2u; } /* TRIANGLE_LIST_WITH_ADJACENCY: N/2 inputs map to N/6 primitives, each emits * {6*i, 6*i+2, 6*i+4} from the 6-vertex input window. */ static uint32_t prim_count_tri_list_adj(uint32_t n) { return n / 6u; } static void expected_tri_list_adj(uint32_t i, uint32_t *out) { out[0] = 6 * i + 0u; out[1] = 6 * i + 2u; out[2] = 6 * i + 4u; } /* TRIANGLE_STRIP_WITH_ADJACENCY: 3*(N/2-2) outputs with winding flip on odd. * Even prim i: {2i, 2i+2, 2i+4} * Odd prim i: {2i, 2i+4, 2i+2} */ static uint32_t prim_count_tri_strip_adj(uint32_t n) { return (n >= 6) ? (3u * (n / 2u - 2u) / 3u) : 0; /* That's just (n/2 - 2) primitives, each emitting 3. */ } static void expected_tri_strip_adj(uint32_t i, uint32_t *out) { bool even = ((i & 1u) == 0u); out[0] = 2 * i + 0u; if (even) { out[1] = 2 * i + 2u; out[2] = 2 * i + 4u; } else { out[1] = 2 * i + 4u; out[2] = 2 * i + 2u; } } /* The table itself — gated to topologies that need decomposition. * LIST topologies (POINT_LIST, LINE_LIST, TRIANGLE_LIST) return NULL. */ const struct panvk_winding_table * panvk_per_arch(get_winding_table)(VkPrimitiveTopology topo) { static const struct panvk_winding_table TABLES[] = { [VK_PRIMITIVE_TOPOLOGY_LINE_STRIP] = { .verts_per_prim = 2, .prim_count = prim_count_line_strip, .decompose = expected_line_strip, .list_equiv = VK_PRIMITIVE_TOPOLOGY_LINE_LIST, .name = "LINE_STRIP", }, [VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP] = { .verts_per_prim = 3, .prim_count = prim_count_tri_strip, .decompose = expected_tri_strip, .list_equiv = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, .name = "TRIANGLE_STRIP", }, [VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN] = { .verts_per_prim = 3, .prim_count = prim_count_tri_fan, .decompose = expected_tri_fan, .list_equiv = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, .name = "TRIANGLE_FAN", }, [VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY] = { .verts_per_prim = 2, .prim_count = prim_count_line_list_adj, .decompose = expected_line_list_adj, .list_equiv = VK_PRIMITIVE_TOPOLOGY_LINE_LIST, .name = "LINE_LIST_WITH_ADJ", }, [VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY] = { .verts_per_prim = 2, .prim_count = prim_count_line_strip_adj, .decompose = expected_line_strip_adj, .list_equiv = VK_PRIMITIVE_TOPOLOGY_LINE_LIST, .name = "LINE_STRIP_WITH_ADJ", }, [VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY] = { .verts_per_prim = 3, .prim_count = prim_count_tri_list_adj, .decompose = expected_tri_list_adj, .list_equiv = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, .name = "TRIANGLE_LIST_WITH_ADJ", }, [VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY] = { .verts_per_prim = 3, .prim_count = prim_count_tri_strip_adj, .decompose = expected_tri_strip_adj, .list_equiv = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, .name = "TRIANGLE_STRIP_WITH_ADJ", }, }; if (topo >= ARRAY_SIZE(TABLES)) return NULL; const struct panvk_winding_table *t = &TABLES[topo]; /* Slots not in our table list above have verts_per_prim==0 (zero-init) */ return t->verts_per_prim ? t : NULL; } #endif /* PAN_ARCH < 9 */