nir: allow to force line strip out in nir_create_passthrough_gs
[platform/upstream/mesa.git] / src / compiler / nir / nir_passthrough_gs.c
1 /*
2  * Copyright © 2022 Collabora Ltc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23
24 #include "nir.h"
25 #include "nir_builder.h"
26
27 static unsigned int
28 gs_in_prim_for_topology(enum shader_prim prim)
29 {
30    switch (prim) {
31    case SHADER_PRIM_QUADS:
32       return SHADER_PRIM_LINES_ADJACENCY;
33    default:
34       return prim;
35    }
36 }
37
38 static enum shader_prim
39 gs_out_prim_for_topology(enum shader_prim prim)
40 {
41    switch (prim) {
42    case SHADER_PRIM_POINTS:
43       return SHADER_PRIM_POINTS;
44    case SHADER_PRIM_LINES:
45    case SHADER_PRIM_LINE_LOOP:
46    case SHADER_PRIM_LINES_ADJACENCY:
47    case SHADER_PRIM_LINE_STRIP_ADJACENCY:
48    case SHADER_PRIM_LINE_STRIP:
49       return SHADER_PRIM_LINE_STRIP;
50    case SHADER_PRIM_TRIANGLES:
51    case SHADER_PRIM_TRIANGLE_STRIP:
52    case SHADER_PRIM_TRIANGLE_FAN:
53    case SHADER_PRIM_TRIANGLES_ADJACENCY:
54    case SHADER_PRIM_TRIANGLE_STRIP_ADJACENCY:
55    case SHADER_PRIM_POLYGON:
56       return SHADER_PRIM_TRIANGLE_STRIP;
57    case SHADER_PRIM_QUADS:
58    case SHADER_PRIM_QUAD_STRIP:
59    case SHADER_PRIM_PATCHES:
60    default:
61       return SHADER_PRIM_QUADS;
62    }
63 }
64
65 static unsigned int
66 vertices_for_prim(enum shader_prim prim)
67 {
68    switch (prim) {
69    case SHADER_PRIM_POINTS:
70       return 1;
71    case SHADER_PRIM_LINES:
72    case SHADER_PRIM_LINE_LOOP:
73    case SHADER_PRIM_LINES_ADJACENCY:
74    case SHADER_PRIM_LINE_STRIP_ADJACENCY:
75    case SHADER_PRIM_LINE_STRIP:
76       return 2;
77    case SHADER_PRIM_TRIANGLES:
78    case SHADER_PRIM_TRIANGLE_STRIP:
79    case SHADER_PRIM_TRIANGLE_FAN:
80    case SHADER_PRIM_TRIANGLES_ADJACENCY:
81    case SHADER_PRIM_TRIANGLE_STRIP_ADJACENCY:
82    case SHADER_PRIM_POLYGON:
83       return 3;
84    case SHADER_PRIM_QUADS:
85    case SHADER_PRIM_QUAD_STRIP:
86       return 4;
87    case SHADER_PRIM_PATCHES:
88    default:
89       unreachable("unsupported primitive for gs input");
90    }
91 }
92
93 static unsigned int
94 array_size_for_prim(enum shader_prim prim)
95 {
96    switch (prim) {
97    case SHADER_PRIM_POINTS:
98       return 1;
99    case SHADER_PRIM_LINES:
100    case SHADER_PRIM_LINE_LOOP:
101    case SHADER_PRIM_LINE_STRIP:
102       return 2;
103    case SHADER_PRIM_LINES_ADJACENCY:
104    case SHADER_PRIM_LINE_STRIP_ADJACENCY:
105       return 4;
106    case SHADER_PRIM_TRIANGLES:
107    case SHADER_PRIM_TRIANGLE_STRIP:
108    case SHADER_PRIM_TRIANGLE_FAN:
109    case SHADER_PRIM_POLYGON:
110       return 3;
111    case SHADER_PRIM_TRIANGLES_ADJACENCY:
112    case SHADER_PRIM_TRIANGLE_STRIP_ADJACENCY:
113       return 6;
114    case SHADER_PRIM_QUADS:
115    case SHADER_PRIM_QUAD_STRIP:
116       return 4;
117    case SHADER_PRIM_PATCHES:
118    default:
119       unreachable("unsupported primitive for gs input");
120    }
121 }
122
123 /*
124  * A helper to create a passthrough GS shader for drivers that needs to lower
125  * some rendering tasks to the GS.
126  */
127
128 nir_shader *
129 nir_create_passthrough_gs(const nir_shader_compiler_options *options,
130                           const nir_shader *prev_stage,
131                           enum shader_prim primitive_type,
132                           bool emulate_edgeflags,
133                           bool force_line_strip_out)
134 {
135    unsigned int vertices_out = vertices_for_prim(primitive_type);
136    emulate_edgeflags = emulate_edgeflags && (prev_stage->info.outputs_written & VARYING_BIT_EDGE);
137    bool needs_closing = (force_line_strip_out || emulate_edgeflags) && vertices_out >= 3;
138    nir_builder b = nir_builder_init_simple_shader(MESA_SHADER_GEOMETRY,
139                                                   options,
140                                                   "gs passthrough");
141
142    nir_shader *nir = b.shader;
143    nir->info.gs.input_primitive = gs_in_prim_for_topology(primitive_type);
144    nir->info.gs.output_primitive = (force_line_strip_out || emulate_edgeflags) ?
145       SHADER_PRIM_LINE_STRIP : gs_out_prim_for_topology(primitive_type);
146    nir->info.gs.vertices_in = vertices_out;
147    nir->info.gs.vertices_out = needs_closing ? vertices_out + 1 : vertices_out;
148    nir->info.gs.invocations = 1;
149    nir->info.gs.active_stream_mask = 1;
150
151    nir_variable *in_vars[VARYING_SLOT_MAX];
152    nir_variable *out_vars[VARYING_SLOT_MAX];
153    unsigned num_inputs = 0, num_outputs = 0;
154
155    /* Create input/output variables. */
156    nir_foreach_shader_out_variable(var, prev_stage) {
157       assert(!var->data.patch);
158
159       char name[100];
160       if (var->name)
161          snprintf(name, sizeof(name), "in_%s", var->name);
162       else
163          snprintf(name, sizeof(name), "in_%d", var->data.driver_location);
164
165       nir_variable *in = nir_variable_create(nir, nir_var_shader_in,
166                                              glsl_array_type(var->type,
167                                                              array_size_for_prim(primitive_type),
168                                                              false),
169                                              name);
170       in->data.location = var->data.location;
171       in->data.location_frac = var->data.location_frac;
172       in->data.driver_location = var->data.driver_location;
173       in->data.interpolation = var->data.interpolation;
174       in->data.compact = var->data.compact;
175
176       in_vars[num_inputs++] = in;
177
178       nir->num_inputs++;
179       if (in->data.location == VARYING_SLOT_EDGE)
180          continue;
181
182       if (var->data.location != VARYING_SLOT_POS)
183          nir->num_outputs++;
184
185       if (var->name)
186          snprintf(name, sizeof(name), "out_%s", var->name);
187       else
188          snprintf(name, sizeof(name), "out_%d", var->data.driver_location);
189
190       nir_variable *out = nir_variable_create(nir, nir_var_shader_out,
191                                               var->type, name);
192       out->data.location = var->data.location;
193       out->data.location_frac = var->data.location_frac;
194       out->data.driver_location = var->data.driver_location;
195       out->data.interpolation = var->data.interpolation;
196       out->data.compact = var->data.compact;
197
198       out_vars[num_outputs++] = out;
199    }
200
201    unsigned int start_vert = 0;
202    unsigned int end_vert = vertices_out;
203    unsigned int vert_step = 1;
204    switch (primitive_type) {
205    case PIPE_PRIM_LINES_ADJACENCY:
206    case PIPE_PRIM_LINE_STRIP_ADJACENCY:
207       start_vert = 1;
208       end_vert += 1;
209       break;
210    case PIPE_PRIM_TRIANGLES_ADJACENCY:
211    case PIPE_PRIM_TRIANGLE_STRIP_ADJACENCY:
212       end_vert = 5;
213       vert_step = 2;
214       break;
215    default:
216       break;
217    }
218
219    nir_variable *edge_var = nir_find_variable_with_location(nir, nir_var_shader_in, VARYING_SLOT_EDGE);
220    for (unsigned i = start_vert; i < end_vert || needs_closing; i += vert_step) {
221       int idx = i < end_vert ? i : start_vert;
222       /* Copy inputs to outputs. */
223       for (unsigned j = 0, oj = 0; j < num_inputs; ++j) {
224          if (in_vars[j]->data.location == VARYING_SLOT_EDGE) {
225             continue;
226          }
227          /* no need to use copy_var to save a lower pass */
228          nir_ssa_def *value = nir_load_array_var_imm(&b, in_vars[j], idx);
229          nir_store_var(&b, out_vars[oj], value,
230                        (1u << value->num_components) - 1);
231          ++oj;
232       }
233       nir_emit_vertex(&b, 0);
234       if (emulate_edgeflags) {
235          nir_ssa_def *edge_value = nir_channel(&b, nir_load_array_var_imm(&b, edge_var, idx), 0);
236          nir_if *edge_if = nir_push_if(&b, nir_fneu(&b, edge_value, nir_imm_float(&b, 1.0)));
237          nir_end_primitive(&b, 0);
238          nir_pop_if(&b, edge_if);
239       }
240       if (i >= end_vert)
241          break;
242    }
243
244    nir_end_primitive(&b, 0);
245    nir_shader_gather_info(nir, nir_shader_get_entrypoint(nir));
246    nir_validate_shader(nir, "in nir_create_passthrough_gs");
247
248    return nir;
249 }