| /* |
| * Copyright 2023 Alyssa Rosenzweig |
| * Copyright 2023 Valve Corporation |
| * Copyright 2025 Collabora Ltd. |
| * SPDX-License-Identifier: MIT |
| */ |
| |
| #include "compiler/libcl/libcl.h" |
| #include "compiler/shader_enums.h" |
| |
| #pragma once |
| |
| /* Swap the two non-provoking vertices in odd triangles. This generates a vertex |
| * ID list with a consistent winding order. |
| * |
| * Holding prim and flatshade_first constant, the map : [0, 1, 2] -> [0, 1, 2] |
| * is its own inverse. It is hence used both vertex fetch and transform |
| * feedback. |
| */ |
| static inline uint32_t |
| poly_map_vertex_in_tri_strip(uint32_t prim, uint32_t vert, bool flatshade_first) |
| { |
| unsigned pv = flatshade_first ? 0 : 2; |
| |
| bool even = (prim & 1) == 0; |
| bool provoking = vert == pv; |
| |
| return (provoking || even) ? vert : ((3 - pv) - vert); |
| } |
| |
| static inline uint32_t |
| poly_vertex_id_for_line_loop(uint32_t prim, uint32_t vert, uint32_t num_prims) |
| { |
| /* (0, 1), (1, 2), (2, 0) */ |
| if (prim == (num_prims - 1) && vert == 1) |
| return 0; |
| else |
| return prim + vert; |
| } |
| |
| static inline uint32_t |
| poly_vertex_id_for_tri_fan(uint32_t prim, uint32_t vert, bool flatshade_first) |
| { |
| /* Vulkan spec section 20.1.7 gives (i + 1, i + 2, 0) for a provoking |
| * first. OpenGL instead wants (0, i + 1, i + 2) with a provoking last. |
| * Piglit clipflat expects us to switch between these orders depending on |
| * provoking vertex, to avoid trivializing the fan. |
| * |
| * Rotate accordingly. |
| */ |
| if (flatshade_first) { |
| vert = (vert == 2) ? 0 : (vert + 1); |
| } |
| |
| /* The simpler form assuming last is provoking. */ |
| return (vert == 0) ? 0 : prim + vert; |
| } |
| |
| static inline uint32_t |
| poly_vertex_id_for_tri_strip_adj(uint32_t prim, uint32_t vert, |
| uint32_t num_prims, bool flatshade_first) |
| { |
| /* See Vulkan spec section 20.1.11 "Triangle Strips With Adjancency". |
| * |
| * There are different cases for first/middle/last/only primitives and for |
| * odd/even primitives. Determine which case we're in. |
| */ |
| bool last = prim == (num_prims - 1); |
| bool first = prim == 0; |
| bool even = (prim & 1) == 0; |
| bool even_or_first = even || first; |
| |
| /* When the last vertex is provoking, we rotate the primitives |
| * accordingly. This seems required for OpenGL. |
| */ |
| if (!flatshade_first && !even_or_first) { |
| vert = (vert + 4u) % 6u; |
| } |
| |
| /* Offsets per the spec. The spec lists 6 cases with 6 offsets. Luckily, |
| * there are lots of patterns we can exploit, avoiding a full 6x6 LUT. |
| * |
| * Here we assume the first vertex is provoking, the Vulkan default. |
| */ |
| uint32_t offsets[6] = { |
| 0, |
| first ? 1 : (even ? -2 : 3), |
| even_or_first ? 2 : 4, |
| last ? 5 : 6, |
| even_or_first ? 4 : 2, |
| even_or_first ? 3 : -2, |
| }; |
| |
| /* Ensure NIR can see thru the local array */ |
| uint32_t offset = 0; |
| for (uint32_t i = 1; i < 6; ++i) { |
| if (i == vert) |
| offset = offsets[i]; |
| } |
| |
| /* Finally add to the base of the primitive */ |
| return (prim * 2) + offset; |
| } |
| |
| static inline uint32_t |
| poly_vertex_id_for_topology(enum mesa_prim mode, bool flatshade_first, |
| uint32_t prim, uint32_t vert, uint32_t num_prims) |
| { |
| switch (mode) { |
| case MESA_PRIM_POINTS: |
| case MESA_PRIM_LINES: |
| case MESA_PRIM_TRIANGLES: |
| case MESA_PRIM_LINES_ADJACENCY: |
| case MESA_PRIM_TRIANGLES_ADJACENCY: |
| /* Regular primitive: every N vertices defines a primitive */ |
| return (prim * mesa_vertices_per_prim(mode)) + vert; |
| |
| case MESA_PRIM_LINE_LOOP: |
| return poly_vertex_id_for_line_loop(prim, vert, num_prims); |
| |
| case MESA_PRIM_LINE_STRIP: |
| case MESA_PRIM_LINE_STRIP_ADJACENCY: |
| /* (i, i + 1) or (i, ..., i + 3) */ |
| return prim + vert; |
| |
| case MESA_PRIM_TRIANGLE_STRIP: { |
| /* Order depends on the provoking vert. |
| * |
| * First: (0, 1, 2), (1, 3, 2), (2, 3, 4). |
| * Last: (0, 1, 2), (2, 1, 3), (2, 3, 4). |
| * |
| * Pull the (maybe swapped) vert from the corresponding primitive |
| */ |
| return prim + poly_map_vertex_in_tri_strip(prim, vert, flatshade_first); |
| } |
| |
| case MESA_PRIM_TRIANGLE_FAN: |
| return poly_vertex_id_for_tri_fan(prim, vert, flatshade_first); |
| |
| case MESA_PRIM_TRIANGLE_STRIP_ADJACENCY: |
| return poly_vertex_id_for_tri_strip_adj(prim, vert, num_prims, |
| flatshade_first); |
| |
| default: |
| return 0; |
| } |
| } |