/* * 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; } }