1 /*
2 * Copyright © 2013 Marek Olšák <[email protected]>
3 * Copyright © 2022 Valve Corporation
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25 /**
26 * This eliminates the built-in shader outputs which are either not written
27 * at all or not used by the next stage. It also eliminates unused elements
28 * of gl_TexCoord inputs, which reduces the overall varying usage.
29 * The varyings handled here are the primary and secondary color, the fog,
30 * and the texture coordinates (gl_TexCoord).
31 *
32 * This pass is necessary, because the Mesa GLSL linker cannot eliminate
33 * built-in varyings like it eliminates user-defined varyings, because
34 * the built-in varyings have pre-assigned locations. Also, the elimination
35 * of unused gl_TexCoord elements requires its own lowering pass anyway.
36 *
37 * It's implemented by replacing all occurrences of dead varyings with
38 * temporary variables, which creates dead code. It is recommended to run
39 * a dead-code elimination pass after this.
40 *
41 * If any texture coordinate slots can be eliminated, the gl_TexCoord array is
42 * broken down into separate vec4 variables with locations equal to
43 * VARYING_SLOT_TEX0 + i.
44 */
45
46 #include "gl_nir_link_varyings.h"
47 #include "gl_nir_linker.h"
48 #include "glsl_types.h"
49 #include "linker_util.h"
50 #include "nir_builder.h"
51
52 #include "main/consts_exts.h"
53 #include "main/shader_types.h"
54 #include "util/u_string.h"
55
56 struct varying_info {
57 bool lower_texcoord_array;
58 nir_variable *texcoord_array;
59 unsigned texcoord_usage; /* bitmask */
60
61 bool find_frag_outputs; /* false if it's looking for varyings */
62
63 nir_variable *color[2];
64 nir_variable *backcolor[2];
65 unsigned color_usage; /* bitmask */
66 unsigned tfeedback_color_usage; /* bitmask */
67
68 nir_variable *fog;
69 bool has_fog;
70 bool tfeedback_has_fog;
71
72 nir_variable_mode mode;
73 };
74
75 static void
initialise_varying_info(struct varying_info * info,nir_variable_mode mode,bool find_frag_outputs)76 initialise_varying_info(struct varying_info *info, nir_variable_mode mode,
77 bool find_frag_outputs)
78 {
79 info->find_frag_outputs = find_frag_outputs;
80 info->lower_texcoord_array = true;
81 info->texcoord_array = NULL;
82 info->texcoord_usage = 0;
83 info->color_usage = 0;
84 info->tfeedback_color_usage = 0;
85 info->fog = NULL;
86 info->has_fog = false;
87 info->tfeedback_has_fog = false;
88 info->mode = mode;
89
90 memset(info->color, 0, sizeof(info->color));
91 memset(info->backcolor, 0, sizeof(info->backcolor));
92 }
93
94 static void
gather_info_on_varying_deref(struct varying_info * info,nir_deref_instr * deref)95 gather_info_on_varying_deref(struct varying_info *info, nir_deref_instr *deref)
96 {
97 nir_variable *var = nir_deref_instr_get_variable(deref);
98
99 if (!glsl_type_is_array(var->type) || !is_gl_identifier(var->name))
100 return;
101
102 if (!info->find_frag_outputs && var->data.location == VARYING_SLOT_TEX0) {
103 info->texcoord_array = var;
104
105 assert(deref->deref_type == nir_deref_type_array);
106 if (nir_src_is_const(deref->arr.index)) {
107 info->texcoord_usage |= 1 << nir_src_as_uint(deref->arr.index);
108 } else {
109 /* There is variable indexing, we can't lower the texcoord array. */
110 info->texcoord_usage |= (1 << glsl_array_size(var->type)) - 1;
111 info->lower_texcoord_array = false;
112 }
113
114 return;
115 }
116 }
117
118 /**
119 * This obtains detailed information about built-in varyings from shader code.
120 */
121 static void
get_varying_info(struct varying_info * info,nir_shader * shader,unsigned num_tfeedback_decls,struct xfb_decl * tfeedback_decls)122 get_varying_info(struct varying_info *info, nir_shader *shader,
123 unsigned num_tfeedback_decls, struct xfb_decl *tfeedback_decls)
124 {
125 /* Handle the transform feedback varyings. */
126 for (unsigned i = 0; i < num_tfeedback_decls; i++) {
127 if (!xfb_decl_is_varying(&tfeedback_decls[i]))
128 continue;
129
130 unsigned location = tfeedback_decls[i].location;
131
132 switch (location) {
133 case VARYING_SLOT_COL0:
134 case VARYING_SLOT_BFC0:
135 info->tfeedback_color_usage |= 1;
136 break;
137 case VARYING_SLOT_COL1:
138 case VARYING_SLOT_BFC1:
139 info->tfeedback_color_usage |= 2;
140 break;
141 case VARYING_SLOT_FOGC:
142 info->tfeedback_has_fog = true;
143 break;
144 default:
145 if (location >= VARYING_SLOT_TEX0 &&
146 location <= VARYING_SLOT_TEX7) {
147 info->lower_texcoord_array = false;
148 }
149 }
150 }
151
152 /* Process frag shader vars */
153 nir_foreach_variable_with_modes(var, shader, info->mode) {
154 /* Nothing to do here for fragment outputs. */
155 if (info->find_frag_outputs)
156 break;
157
158 /* Handle colors and fog. */
159 switch (var->data.location) {
160 case VARYING_SLOT_COL0:
161 info->color[0] = var;
162 info->color_usage |= 1;
163 break;
164 case VARYING_SLOT_COL1:
165 info->color[1] = var;
166 info->color_usage |= 2;
167 break;
168 case VARYING_SLOT_BFC0:
169 info->backcolor[0] = var;
170 info->color_usage |= 1;
171 break;
172 case VARYING_SLOT_BFC1:
173 info->backcolor[1] = var;
174 info->color_usage |= 2;
175 break;
176 case VARYING_SLOT_FOGC:
177 info->fog = var;
178 info->has_fog = true;
179 break;
180 }
181 }
182
183 /* Process the shader. */
184 assert(shader->info.stage != MESA_SHADER_COMPUTE);
185 nir_function_impl *impl = nir_shader_get_entrypoint(shader);
186
187 /* assert that functions have been inlined before packing is called */
188 nir_foreach_function(f, shader) {
189 assert(f->impl == impl);
190 }
191
192 nir_foreach_block(block, impl) {
193 nir_foreach_instr(instr, block) {
194 if (instr->type != nir_instr_type_intrinsic)
195 continue;
196
197 nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
198
199 /* Copies should have been lowered by nir_split_var_copies() before
200 * calling this pass.
201 */
202 assert(intrin->intrinsic != nir_intrinsic_copy_deref);
203
204 if (intrin->intrinsic != nir_intrinsic_load_deref &&
205 intrin->intrinsic != nir_intrinsic_store_deref)
206 continue;
207
208 nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]);
209 if (!nir_deref_mode_is(deref, info->mode))
210 continue;
211
212 gather_info_on_varying_deref(info, deref);
213 }
214 }
215
216 if (!info->texcoord_array) {
217 info->lower_texcoord_array = false;
218 }
219 }
220
221 struct replace_varyings_data {
222 const struct gl_constants *consts;
223
224 struct gl_shader_program *prog;
225 struct gl_linked_shader *shader;
226 const struct varying_info *info;
227
228 nir_variable *new_texcoord[MAX_TEXTURE_COORD_UNITS];
229 nir_variable *new_color[2];
230 nir_variable *new_backcolor[2];
231 nir_variable *new_fog;
232 };
233
234 static nir_variable *
create_new_var(nir_shader * shader,char * name,nir_variable_mode mode,const struct glsl_type * type)235 create_new_var(nir_shader *shader, char *name, nir_variable_mode mode,
236 const struct glsl_type *type)
237 {
238 nir_variable *var = rzalloc(shader, nir_variable);
239 var->name = ralloc_strdup(var, name);
240 var->data.mode = mode;
241 var->type = type;
242
243 nir_shader_add_variable(shader, var);
244 return var;
245 }
246
247 static void
replace_varying(struct replace_varyings_data * rv_data,nir_variable * var)248 replace_varying(struct replace_varyings_data *rv_data, nir_variable *var)
249 {
250 /* Remove the gl_TexCoord array. */
251 if (rv_data->info->lower_texcoord_array &&
252 var == rv_data->info->texcoord_array) {
253 var->data.mode = nir_var_shader_temp;
254 }
255
256 /* Lower set-but-unused color and fog outputs to shader temps. */
257 for (int i = 0; i < 2; i++) {
258 if (var == rv_data->info->color[i] && rv_data->new_color[i]) {
259 var->data.mode = nir_var_shader_temp;
260 }
261
262 if (var == rv_data->info->backcolor[i] && rv_data->new_backcolor[i]) {
263 var->data.mode = nir_var_shader_temp;
264 }
265 }
266
267 if (var == rv_data->info->fog && rv_data->new_fog) {
268 var->data.mode = nir_var_shader_temp;
269 }
270 }
271
272 static void
rewrite_varying_deref(nir_builder * b,struct replace_varyings_data * rv_data,nir_deref_instr * deref)273 rewrite_varying_deref(nir_builder *b, struct replace_varyings_data *rv_data,
274 nir_deref_instr *deref)
275 {
276 if (deref->deref_type != nir_deref_type_array)
277 return;
278
279 nir_variable *var = nir_deref_instr_get_variable(deref);
280 const struct varying_info *info = rv_data->info;
281 b->cursor = nir_before_instr(&deref->instr);
282
283 /* Replace an array dereference gl_TexCoord[i] with a single
284 * variable dereference representing gl_TexCoord[i].
285 */
286 if (info->lower_texcoord_array && info->texcoord_array == var) {
287 /* gl_TexCoord[i] occurrence */
288 unsigned i = nir_src_as_uint(deref->arr.index);
289 nir_deref_instr *new_deref =
290 nir_build_deref_var(b, rv_data->new_texcoord[i]);
291 nir_def_rewrite_uses(&deref->def, &new_deref->def);
292 return;
293 }
294 }
295
296 static void
prepare_array(struct replace_varyings_data * rv_data,nir_shader * shader,nir_variable ** new_var,int max_elements,unsigned start_location,const char * var_name,const char * mode_str,unsigned usage,unsigned external_usage)297 prepare_array(struct replace_varyings_data *rv_data,
298 nir_shader *shader, nir_variable **new_var,
299 int max_elements, unsigned start_location,
300 const char *var_name, const char *mode_str,
301 unsigned usage, unsigned external_usage)
302 {
303 for (int i = max_elements - 1; i >= 0; i--) {
304 if (usage & (1 << i)) {
305 char name[32];
306
307 if (!(external_usage & (1 << i))) {
308 /* This varying is unused in the next stage. Declare
309 * a temporary instead of an output. */
310 snprintf(name, 32, "gl_%s_%s%i_dummy", mode_str, var_name, i);
311 new_var[i] = create_new_var(shader, name, nir_var_shader_temp,
312 glsl_vec4_type());
313 } else {
314 snprintf(name, 32, "gl_%s_%s%i", mode_str, var_name, i);
315 new_var[i] = create_new_var(shader, name, rv_data->info->mode,
316 glsl_vec4_type());
317 new_var[i]->data.location = start_location + i;
318 new_var[i]->data.explicit_location = true;
319 }
320 }
321 }
322 }
323
324 /**
325 * This replaces unused varyings with temporary variables.
326 *
327 * If "ir" is the producer, the "external" usage should come from
328 * the consumer. It also works the other way around. If either one is
329 * missing, set the "external" usage to a full mask.
330 */
331 static void
replace_varyings(const struct gl_constants * consts,struct gl_linked_shader * shader,struct gl_shader_program * prog,const struct varying_info * info,unsigned external_texcoord_usage,unsigned external_color_usage,bool external_has_fog)332 replace_varyings(const struct gl_constants *consts,
333 struct gl_linked_shader *shader,
334 struct gl_shader_program *prog,
335 const struct varying_info *info,
336 unsigned external_texcoord_usage,
337 unsigned external_color_usage, bool external_has_fog)
338 {
339 struct replace_varyings_data rv_data;
340 rv_data.shader = shader;
341 rv_data.info = info;
342 rv_data.consts = consts;
343 rv_data.prog = prog;
344
345 memset(rv_data.new_texcoord, 0, sizeof(rv_data.new_texcoord));
346 memset(rv_data.new_color, 0, sizeof(rv_data.new_color));
347 memset(rv_data.new_backcolor, 0, sizeof(rv_data.new_backcolor));
348 rv_data.new_fog = NULL;
349
350 const char *mode_str = info->mode == nir_var_shader_in ? "in" : "out";
351
352 /* Handle texcoord outputs.
353 *
354 * We're going to break down the gl_TexCoord array into separate
355 * variables. First, add declarations of the new variables all
356 * occurrences of gl_TexCoord will be replaced with.
357 */
358 if (info->lower_texcoord_array) {
359 prepare_array(&rv_data, shader->Program->nir, rv_data.new_texcoord,
360 ARRAY_SIZE(rv_data.new_texcoord),
361 VARYING_SLOT_TEX0, "TexCoord", mode_str,
362 info->texcoord_usage, external_texcoord_usage);
363 }
364
365 /* Create dummy variables which will replace set-but-unused color and
366 * fog outputs.
367 */
368 external_color_usage |= info->tfeedback_color_usage;
369
370 for (int i = 0; i < 2; i++) {
371 char name[32];
372
373 if (!(external_color_usage & (1 << i))) {
374 if (info->color[i]) {
375 snprintf(name, 32, "gl_%s_FrontColor%i_dummy", mode_str, i);
376 rv_data.new_color[i] =
377 create_new_var(shader->Program->nir, name, nir_var_shader_temp,
378 glsl_vec4_type());
379 }
380
381 if (info->backcolor[i]) {
382 snprintf(name, 32, "gl_%s_BackColor%i_dummy", mode_str, i);
383 rv_data.new_backcolor[i] =
384 create_new_var(shader->Program->nir, name, nir_var_shader_temp,
385 glsl_vec4_type());
386 }
387 }
388 }
389
390 if (!external_has_fog && !info->tfeedback_has_fog && info->fog) {
391 char name[32];
392
393 snprintf(name, 32, "gl_%s_FogFragCoord_dummy", mode_str);
394 rv_data.new_fog =
395 create_new_var(shader->Program->nir, name, nir_var_shader_temp,
396 glsl_float_type());
397 }
398
399 /* Now do the replacing. */
400 nir_foreach_variable_with_modes_safe(var, shader->Program->nir, info->mode) {
401 replace_varying(&rv_data, var);
402 }
403
404 nir_function_impl *impl = nir_shader_get_entrypoint(shader->Program->nir);
405 nir_builder b = nir_builder_create(impl);
406
407 /* assert that functions have been inlined before packing is called */
408 nir_foreach_function(f, shader->Program->nir) {
409 assert(f->impl == impl);
410 }
411
412 /* Rewrite the derefs to use the new vars */
413 nir_foreach_block(block, impl) {
414 nir_foreach_instr(instr, block) {
415 if (instr->type != nir_instr_type_intrinsic)
416 continue;
417
418 nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
419
420 /* Copies should have been lowered by nir_split_var_copies() before
421 * calling this pass.
422 */
423 assert(intrin->intrinsic != nir_intrinsic_copy_deref);
424
425 if (intrin->intrinsic != nir_intrinsic_load_deref &&
426 intrin->intrinsic != nir_intrinsic_store_deref)
427 continue;
428
429 nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]);
430 if (!nir_deref_mode_is(deref, info->mode))
431 continue;
432
433 rewrite_varying_deref(&b, &rv_data, deref);
434 }
435 }
436 }
437
438 static void
lower_texcoord_array(const struct gl_constants * consts,struct gl_linked_shader * shader,struct gl_shader_program * prog,const struct varying_info * info)439 lower_texcoord_array(const struct gl_constants *consts,
440 struct gl_linked_shader *shader,
441 struct gl_shader_program *prog,
442 const struct varying_info *info)
443 {
444 replace_varyings(consts, shader, prog, info,
445 (1 << MAX_TEXTURE_COORD_UNITS) - 1, 1 | 2, true);
446 }
447
448 void
gl_nir_opt_dead_builtin_varyings(const struct gl_constants * consts,gl_api api,struct gl_shader_program * prog,struct gl_linked_shader * producer,struct gl_linked_shader * consumer,unsigned num_tfeedback_decls,struct xfb_decl * tfeedback_decls)449 gl_nir_opt_dead_builtin_varyings(const struct gl_constants *consts, gl_api api,
450 struct gl_shader_program *prog,
451 struct gl_linked_shader *producer,
452 struct gl_linked_shader *consumer,
453 unsigned num_tfeedback_decls,
454 struct xfb_decl *tfeedback_decls)
455 {
456 /* Lowering of built-in varyings has no effect with the core context and
457 * GLES2, because they are not available there.
458 */
459 if (api == API_OPENGL_CORE ||
460 api == API_OPENGLES2) {
461 goto done;
462 }
463
464 /* Information about built-in varyings. */
465 struct varying_info producer_info;
466 struct varying_info consumer_info;
467 initialise_varying_info(&producer_info, nir_var_shader_out, false);
468 initialise_varying_info(&consumer_info, nir_var_shader_in, false);
469
470 if (producer) {
471 get_varying_info(&producer_info, producer->Program->nir,
472 num_tfeedback_decls, tfeedback_decls);
473
474 if (producer->Stage == MESA_SHADER_TESS_CTRL)
475 producer_info.lower_texcoord_array = false;
476
477 if (!consumer) {
478 /* At least eliminate unused gl_TexCoord elements. */
479 if (producer_info.lower_texcoord_array) {
480 lower_texcoord_array(consts, producer, prog, &producer_info);
481 }
482 goto done;
483 }
484 }
485
486 if (consumer) {
487 get_varying_info(&consumer_info, consumer->Program->nir,
488 num_tfeedback_decls, tfeedback_decls);
489
490 if (consumer->Stage != MESA_SHADER_FRAGMENT)
491 consumer_info.lower_texcoord_array = false;
492
493 if (!producer) {
494 /* At least eliminate unused gl_TexCoord elements. */
495 if (consumer_info.lower_texcoord_array) {
496 lower_texcoord_array(consts, consumer, prog, &consumer_info);
497 }
498 goto done;
499 }
500 }
501
502 /* Eliminate the outputs unused by the consumer. */
503 if (producer_info.lower_texcoord_array ||
504 producer_info.color_usage ||
505 producer_info.has_fog) {
506 replace_varyings(consts, producer, prog, &producer_info,
507 consumer_info.texcoord_usage,
508 consumer_info.color_usage,
509 consumer_info.has_fog);
510 }
511
512 /* The gl_TexCoord fragment shader inputs can be initialized
513 * by GL_COORD_REPLACE, so we can't eliminate them.
514 *
515 * This doesn't prevent elimination of the gl_TexCoord elements which
516 * are not read by the fragment shader. We want to eliminate those anyway.
517 */
518 if (consumer->Stage == MESA_SHADER_FRAGMENT) {
519 producer_info.texcoord_usage = (1 << MAX_TEXTURE_COORD_UNITS) - 1;
520 }
521
522 /* Eliminate the inputs uninitialized by the producer. */
523 if (consumer_info.lower_texcoord_array ||
524 consumer_info.color_usage ||
525 consumer_info.has_fog) {
526 replace_varyings(consts, consumer, prog, &consumer_info,
527 producer_info.texcoord_usage,
528 producer_info.color_usage,
529 producer_info.has_fog);
530 }
531
532 done:
533 if (producer)
534 nir_fixup_deref_modes(producer->Program->nir);
535
536 if (consumer)
537 nir_fixup_deref_modes(consumer->Program->nir);
538 }
539