1 /*
2 * Copyright © 2023 Google LLC
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
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include <gtest/gtest.h>
25 #include <gtest/gtest-spi.h>
26
27 #include "main/mtypes.h"
28 #include "standalone_scaffolding.h"
29 #include "ir.h"
30 #include "ir_optimization.h"
31 #include "nir.h"
32 #include "builtin_functions.h"
33 #include "nir.h"
34 #include "glsl_to_nir.h"
35 #include "nir_builder.h"
36 #include "program.h"
37
38 /* The printed-GLSL-IR tests use fmemopen so we can do stdio to memory (or you'd
39 * need equivalent tempfiles that you manage). Just disable this test on those
40 * platforms (aka Windows).
41 */
42 #ifdef HAVE_FMEMOPEN
43
44 namespace
45 {
46 class gl_nir_lower_mediump_test : public ::testing::Test
47 {
48 protected:
49 gl_nir_lower_mediump_test();
50 ~gl_nir_lower_mediump_test();
51
52 struct gl_shader *compile_shader(GLenum type, const char *source);
53 void compile(const char *source);
54
55 struct gl_context local_ctx;
56 struct gl_context *ctx;
57
find_op(nir_op op)58 nir_alu_instr *find_op(nir_op op)
59 {
60 if (!nir)
61 return NULL;
62
63 nir_foreach_function_impl(impl, nir)
64 {
65 nir_foreach_block(block, impl)
66 {
67 nir_foreach_instr(instr, block)
68 {
69 if (instr->type == nir_instr_type_alu)
70 {
71 nir_alu_instr *alu = nir_instr_as_alu(instr);
72 if (alu->op == op)
73 return alu;
74 }
75 }
76 }
77 }
78 return NULL;
79 }
80
op_dest_bits(nir_op op)81 uint32_t op_dest_bits(nir_op op)
82 {
83 nir_alu_instr *alu = find_op(op);
84 EXPECT_TRUE(alu != NULL);
85 return alu->def.bit_size;
86 }
87
get_fs_ir(void)88 char *get_fs_ir(void) {
89 char temp[4096];
90 FILE *ftemp = fmemopen(temp, sizeof(temp), "w");
91 _mesa_print_ir(ftemp, whole_program->_LinkedShaders[MESA_SHADER_FRAGMENT]->ir, NULL);
92 fclose(ftemp);
93 return strdup(temp);
94 }
95
96 /* Returns the common bit size of all src operands (failing if not matching). */
op_src_bits(nir_op op)97 uint32_t op_src_bits(nir_op op)
98 {
99 nir_alu_instr *alu = find_op(op);
100 EXPECT_TRUE(alu != NULL);
101
102 for (int i = 0; i < nir_op_infos[op].num_inputs; i++) {
103 EXPECT_EQ(alu->src[i].src.ssa->bit_size, alu->src[0].src.ssa->bit_size);
104 }
105 return alu->src[0].src.ssa->bit_size;
106 }
107
108 nir_shader *nir;
109 struct gl_shader_program *whole_program;
110 const char *source;
111 char *fs_ir;
112 };
113
gl_nir_lower_mediump_test()114 gl_nir_lower_mediump_test::gl_nir_lower_mediump_test()
115 : nir(NULL), source(NULL), fs_ir(NULL)
116 {
117 glsl_type_singleton_init_or_ref();
118 }
119
~gl_nir_lower_mediump_test()120 gl_nir_lower_mediump_test::~gl_nir_lower_mediump_test()
121 {
122 if (HasFailure())
123 {
124 if (source)
125 printf("\nSource for the failed test:\n%s\n", source);
126 if (fs_ir) {
127 printf("\nGLSL IR from the failed test:\n\n");
128 printf("%s", fs_ir);
129
130 }
131 if (nir) {
132 printf("\nNIR from the failed test:\n\n");
133 nir_print_shader(nir, stdout);
134 }
135 }
136
137 ralloc_free(nir);
138
139 free(fs_ir);
140
141 glsl_type_singleton_decref();
142 }
143
144 struct gl_shader *
compile_shader(GLenum type,const char * source)145 gl_nir_lower_mediump_test::compile_shader(GLenum type, const char *source)
146 {
147 struct gl_shader *shader = standalone_add_shader_source(ctx, whole_program, type, source);
148
149 _mesa_glsl_compile_shader(ctx, shader, false, false, true);
150
151 return shader;
152 }
153
154 void
compile(const char * source)155 gl_nir_lower_mediump_test::compile(const char *source)
156 {
157 ctx = &local_ctx;
158
159 /* Get better variable names from GLSL IR for debugging. */
160 ir_variable::temporaries_allocate_names = true;
161
162 initialize_context_to_defaults(ctx, API_OPENGLES2);
163 ctx->Version = 31;
164 for (int i = 0; i < MESA_SHADER_STAGES; i++) {
165 ctx->Const.ShaderCompilerOptions[i].LowerPrecisionFloat16 = true;
166 ctx->Const.ShaderCompilerOptions[i].LowerPrecisionInt16 = true;
167 }
168
169 _mesa_glsl_builtin_functions_init_or_ref();
170
171 whole_program = standalone_create_shader_program();
172 whole_program->IsES = true;
173
174 const char *vs_source = R"(#version 310 es
175 void main() {
176 gl_Position = vec4(0.0);
177 })";
178 compile_shader(GL_VERTEX_SHADER, vs_source);
179
180 compile_shader(GL_FRAGMENT_SHADER, source);
181
182 for (unsigned i = 0; i < whole_program->NumShaders; i++)
183 {
184 struct gl_shader *shader = whole_program->Shaders[i];
185 if (shader->CompileStatus != COMPILE_SUCCESS)
186 fprintf(stderr, "Compiler error: %s", shader->InfoLog);
187 ASSERT_EQ(shader->CompileStatus, COMPILE_SUCCESS);
188 }
189
190 link_shaders(ctx, whole_program);
191 if (whole_program->data->LinkStatus != LINKING_SUCCESS)
192 fprintf(stderr, "Linker error: %s", whole_program->data->InfoLog);
193 EXPECT_EQ(whole_program->data->LinkStatus, LINKING_SUCCESS);
194
195 /* Save off the GLSL IR now, since glsl_to_nir() frees it. */
196 fs_ir = get_fs_ir();
197
198 static const struct nir_shader_compiler_options compiler_options = {
199 .support_16bit_alu = true,
200 };
201
202 nir = glsl_to_nir(&ctx->Const, whole_program, MESA_SHADER_FRAGMENT, &compiler_options);
203
204 standalone_destroy_shader_program(whole_program);
205
206 /* nir_lower_mediump_vars happens after copy deref lowering. */
207 NIR_PASS(_, nir, nir_split_var_copies);
208 NIR_PASS(_, nir, nir_lower_var_copies);
209
210 /* Make the vars and i/o mediump like we'd expect, so people debugging aren't confused. */
211 NIR_PASS(_, nir, nir_lower_mediump_vars, nir_var_uniform | nir_var_function_temp | nir_var_shader_temp);
212 NIR_PASS(_, nir, nir_lower_mediump_io, nir_var_shader_out, ~0, false);
213
214 /* Clean up f2fmp(f2f32(x)) noise. */
215 NIR_PASS(_, nir, nir_opt_algebraic);
216 NIR_PASS(_, nir, nir_opt_algebraic_late);
217 NIR_PASS(_, nir, nir_copy_prop);
218 NIR_PASS(_, nir, nir_opt_dce);
219
220 /* Store the source for printing from later assertions. */
221 this->source = source;
222 }
223
224 // A predicate-formatter for asserting that two integers are mutually prime.
glsl_ir_contains(const char * glsl_ir_expr,const char * needle_expr,const char * glsl_ir,const char * needle)225 testing::AssertionResult glsl_ir_contains(const char *glsl_ir_expr,
226 const char *needle_expr,
227 const char *glsl_ir,
228 const char *needle)
229 {
230 /* If we didn't HAVE_FMEMOPEN, we won't have GLSL IR to look at. Just
231 * skip those parts of the tests on such platforms.
232 */
233 if (!glsl_ir)
234 return testing::AssertionSuccess();
235
236 if (strstr(glsl_ir, needle))
237 return testing::AssertionSuccess();
238
239 return testing::AssertionFailure() << " " << needle_expr << " not found in GLSL IR";
240 }
241 } // namespace
242
TEST_F(gl_nir_lower_mediump_test,float_simple_mul)243 TEST_F(gl_nir_lower_mediump_test, float_simple_mul)
244 {
245 ASSERT_NO_FATAL_FAILURE(compile(
246 R"(#version 310 es
247 uniform mediump float a, b;
248 out mediump float result;
249
250 void main()
251 {
252 result = a * b;
253 }
254 )"));
255
256 EXPECT_EQ(op_dest_bits(nir_op_fmul), 16);
257 }
258
TEST_F(gl_nir_lower_mediump_test,int_simple_mul)259 TEST_F(gl_nir_lower_mediump_test, int_simple_mul)
260 {
261 ASSERT_NO_FATAL_FAILURE(compile(
262 R"(#version 310 es
263 precision highp float;
264 precision mediump int;
265 uniform mediump int a, b;
266 out mediump int result;
267
268 void main()
269 {
270 result = a * b;
271 }
272 )"));
273
274 EXPECT_EQ(op_dest_bits(nir_op_imul), 16);
275 }
276
TEST_F(gl_nir_lower_mediump_test,int_default_precision_med)277 TEST_F(gl_nir_lower_mediump_test, int_default_precision_med)
278 {
279 ASSERT_NO_FATAL_FAILURE(compile(
280 R"(#version 310 es
281 precision highp float;
282 precision mediump int;
283 uniform int a, b;
284 out int result;
285
286 void main()
287 {
288 result = a * b;
289 }
290 )"));
291
292 EXPECT_EQ(op_dest_bits(nir_op_imul), 16);
293 }
294
TEST_F(gl_nir_lower_mediump_test,int_default_precision_high)295 TEST_F(gl_nir_lower_mediump_test, int_default_precision_high)
296 {
297 ASSERT_NO_FATAL_FAILURE(compile(
298 R"(#version 310 es
299 precision mediump float;
300 precision highp int;
301 uniform int a, b;
302 out int result;
303
304 void main()
305 {
306 result = a * b;
307 }
308 )"));
309
310 EXPECT_EQ(op_dest_bits(nir_op_imul), 32);
311 }
312
313 /* Test that a builtin with mediump args does mediump computation. */
TEST_F(gl_nir_lower_mediump_test,dot_builtin)314 TEST_F(gl_nir_lower_mediump_test, dot_builtin)
315 {
316 ASSERT_NO_FATAL_FAILURE(compile(
317 R"(#version 310 es
318 precision highp float;
319 precision highp int;
320 uniform mediump vec4 a, b;
321 out float result;
322
323 void main()
324 {
325 result = dot(a, b);
326 }
327 )"));
328
329 EXPECT_EQ(op_dest_bits(nir_op_fdot4), 16);
330 }
331
332 /* Test that a constant-index array deref is mediump */
TEST_F(gl_nir_lower_mediump_test,array_const_index)333 TEST_F(gl_nir_lower_mediump_test, array_const_index)
334 {
335 ASSERT_NO_FATAL_FAILURE(compile(
336 R"(#version 310 es
337 precision highp float;
338 precision highp int;
339 uniform mediump float a, b[2];
340 out float result;
341
342 void main()
343 {
344 result = a * b[1];
345 }
346 )"));
347
348 EXPECT_EQ(op_dest_bits(nir_op_fmul), 16);
349 }
350
351 /* Test that a variable-index array deref is mediump, even if the array index is highp */
TEST_F(gl_nir_lower_mediump_test,array_uniform_index)352 TEST_F(gl_nir_lower_mediump_test, array_uniform_index)
353 {
354 ASSERT_NO_FATAL_FAILURE(compile(
355 R"(#version 310 es
356 precision highp float;
357 uniform mediump float a, b[2];
358 uniform highp int i;
359 out float result;
360
361 void main()
362 {
363 result = a * b[i];
364 }
365 )"));
366
367 EXPECT_EQ(op_dest_bits(nir_op_fmul), 16);
368 }
369
370 /* Test that a variable-index array deref is highp, even if the array index is mediump */
TEST_F(gl_nir_lower_mediump_test,array_mediump_index)371 TEST_F(gl_nir_lower_mediump_test, array_mediump_index)
372 {
373 ASSERT_NO_FATAL_FAILURE(compile(
374 R"(#version 310 es
375 precision highp float;
376 uniform highp int b[2];
377 uniform mediump int a, i;
378 out highp int result;
379
380 void main()
381 {
382 result = a * b[i];
383 }
384 )"));
385
386 EXPECT_PRED_FORMAT2(glsl_ir_contains, fs_ir, "expression int * ");
387
388 EXPECT_EQ(op_dest_bits(nir_op_imul), 32);
389 }
390
TEST_F(gl_nir_lower_mediump_test,func_return)391 TEST_F(gl_nir_lower_mediump_test, func_return)
392 {
393 ASSERT_NO_FATAL_FAILURE(compile(
394 R"(#version 310 es
395 precision highp float; /* Make sure that default highp temps in function handling don't break our mediump return. */
396 uniform mediump float a;
397 uniform highp float b;
398 out float result;
399
400 mediump float func()
401 {
402 return b; /* Returning highp b here, but it should be the mediump return value qualifier that matters */
403 }
404
405 void main()
406 {
407 /* "If a function returns a value, then a call to that function may
408 * be used as an expression, whose type will be the type that was
409 * used to declare or define the function."
410 */
411 result = a * func();
412 }
413 )"));
414
415 EXPECT_PRED_FORMAT2(glsl_ir_contains, fs_ir, "expression float16_t * ");
416
417 EXPECT_EQ(op_dest_bits(nir_op_fmul), 16);
418 }
419
TEST_F(gl_nir_lower_mediump_test,func_args_in_mediump)420 TEST_F(gl_nir_lower_mediump_test, func_args_in_mediump)
421 {
422 ASSERT_NO_FATAL_FAILURE(compile(
423 R"(#version 310 es
424 precision highp float; /* Make sure that default highp temps in function handling don't break our mediump return. */
425 uniform highp float a, b;
426 out highp float result;
427
428 highp float func(mediump float x, mediump float y)
429 {
430 return x * y; /* should be mediump due to x and y, but propagating qualifiers from a,b by inlining could trick it. */
431 }
432
433 void main()
434 {
435 result = func(a, b);
436 }
437 )"));
438
439 EXPECT_PRED_FORMAT2(glsl_ir_contains, fs_ir, "expression float f162f (expression float16_t * (expression float16_t f2fmp (var_ref x) ) (expression float16_t f2fmp (var_ref y) ) )");
440
441 /* NIR optimization will notice that we downconvert the highp to mediump just
442 * to multiply and cast back up, and just multiply in highp instead.
443 */
444 EXPECT_EQ(op_dest_bits(nir_op_fmul), 32);
445 }
446
TEST_F(gl_nir_lower_mediump_test,func_args_inout_mediump)447 TEST_F(gl_nir_lower_mediump_test, func_args_inout_mediump)
448 {
449 ASSERT_NO_FATAL_FAILURE(compile(
450 R"(#version 310 es
451 precision highp float; /* Make sure that default highp temps in function handling don't break our mediump inout. */
452 uniform highp float a, b;
453 out float result;
454
455 void func(inout mediump float x, mediump float y)
456 {
457 x = x * y; /* should be mediump due to x and y, but propagating qualifiers from a,b by inlining could trick it. */
458 }
459
460 void main()
461 {
462 /* The spec says "function input and output is done through copies,
463 * and therefore qualifiers do not have to match." So we use a
464 * highp here for our mediump inout.
465 */
466 highp float x = a;
467 func(x, b);
468 result = x;
469 }
470 )"));
471
472 EXPECT_PRED_FORMAT2(glsl_ir_contains, fs_ir, "expression float16_t * ");
473
474 EXPECT_EQ(op_dest_bits(nir_op_fmul), 16);
475 }
476
TEST_F(gl_nir_lower_mediump_test,func_args_in_out_mediump)477 TEST_F(gl_nir_lower_mediump_test, func_args_in_out_mediump)
478 {
479 ASSERT_NO_FATAL_FAILURE(compile(
480 R"(#version 310 es
481 precision highp float; /* Make sure that default highp temps in function handling don't break our mediump inout. */
482 uniform highp float a, b;
483 out float result;
484
485 void func(mediump float x, mediump float y, out mediump float w)
486 {
487 w = x * y; /* should be mediump due to x and y, but propagating qualifiers from a,b by inlining could trick it. */
488 }
489
490 void main()
491 {
492 /* The spec says "function input and output is done through copies,
493 * and therefore qualifiers do not have to match." So we use a
494 * highp here for our mediump out.
495 */
496 highp float x;
497 func(a, b, x);
498 result = x;
499 }
500 )"));
501
502 EXPECT_PRED_FORMAT2(glsl_ir_contains, fs_ir, "expression float16_t * ");
503
504 EXPECT_EQ(op_dest_bits(nir_op_fmul), 16);
505 }
506
TEST_F(gl_nir_lower_mediump_test,func_args_inout_highp)507 TEST_F(gl_nir_lower_mediump_test, func_args_inout_highp)
508 {
509 ASSERT_NO_FATAL_FAILURE(compile(
510 R"(#version 310 es
511 precision mediump float; /* Make sure that default mediump temps in function handling don't break our highp inout. */
512 uniform mediump float a, b;
513 out float result;
514
515 void func(inout highp float x, highp float y)
516 {
517 x = x * y; /* should be highp due to x and y, but propagating qualifiers from a,b by inlining could trick it. */
518 }
519
520 void main()
521 {
522 mediump float x = a;
523 func(x, b);
524 result = x;
525 }
526 )"));
527
528 EXPECT_PRED_FORMAT2(glsl_ir_contains, fs_ir, "expression float * ");
529
530 EXPECT_EQ(op_dest_bits(nir_op_fmul), 32);
531 }
532
TEST_F(gl_nir_lower_mediump_test,if_mediump)533 TEST_F(gl_nir_lower_mediump_test, if_mediump)
534 {
535 ASSERT_NO_FATAL_FAILURE(compile(
536 R"(#version 310 es
537 precision highp float;
538 uniform mediump float a, b, c;
539 out float result;
540
541 void main()
542 {
543 if (a * b < c)
544 result = 1.0;
545 else
546 result = 0.0;
547 }
548 )"));
549
550 EXPECT_EQ(op_dest_bits(nir_op_fmul), 16);
551 EXPECT_EQ(op_src_bits(nir_op_flt), 16);
552 }
553
TEST_F(gl_nir_lower_mediump_test,mat_mul_mediump)554 TEST_F(gl_nir_lower_mediump_test, mat_mul_mediump)
555 {
556 ASSERT_NO_FATAL_FAILURE(compile(
557 R"(#version 310 es
558 precision highp float;
559 uniform mediump mat2 a;
560 uniform mediump vec2 b;
561 out highp vec2 result;
562
563 void main()
564 {
565 result = a * b;
566 }
567 )"));
568
569 EXPECT_EQ(op_dest_bits(nir_op_fmul), 16);
570 }
571
TEST_F(gl_nir_lower_mediump_test,struct_default_precision_lvalue)572 TEST_F(gl_nir_lower_mediump_test, struct_default_precision_lvalue)
573 {
574 ASSERT_NO_FATAL_FAILURE(compile(
575 R"(#version 310 es
576 precision highp float;
577 precision mediump int;
578 struct S {
579 float x, y;
580 int z, w;
581 };
582 uniform S a;
583 out mediump vec2 result;
584
585 void main()
586 {
587 /* I believe that structure members don't have a precision
588 * qualifier, so we expect the precision of these operations to come
589 * from the lvalue (which is higher precedence than the default
590 * precision).
591 */
592 mediump float resultf = a.x * a.y;
593 highp int resulti = a.z * a.w;
594 result = vec2(resultf, float(resulti));
595 }
596 )"));
597
598 /* GLSL fails to implement this correctly. */
599 EXPECT_NONFATAL_FAILURE(
600 EXPECT_PRED_FORMAT2(glsl_ir_contains, fs_ir,
601 "expression float16_t * (record_ref (var_ref a) x) (record_ref (var_ref a) y) "),
602 "not found in GLSL IR");
603 EXPECT_NONFATAL_FAILURE(
604 EXPECT_PRED_FORMAT2(glsl_ir_contains, fs_ir,
605 "expression int * (record_ref (var_ref a) z) (record_ref (var_ref a) w) "),
606 "not found in GLSL IR");
607
608 // Enable these checks once we fix the GLSL.
609 //EXPECT_EQ(op_dest_bits(nir_op_fmul), 16);
610 //EXPECT_EQ(op_dest_bits(nir_op_imul), 32);
611 }
612
TEST_F(gl_nir_lower_mediump_test,float_constructor)613 TEST_F(gl_nir_lower_mediump_test, float_constructor)
614 {
615 ASSERT_NO_FATAL_FAILURE(compile(
616 R"(#version 310 es
617 precision mediump float;
618 uniform highp uint a;
619 uniform mediump float b;
620 out mediump float result;
621
622 void main()
623 {
624 /* It's tricky to reconcile these two bits of spec: "Literal
625 * constants do not have precision qualifiers. Neither do Boolean
626 * variables. Neither do constructors."
627 *
628 * and
629 *
630 * "For this paragraph, “operation” includes operators, built-in
631 * functions, and constructors, and “operand” includes function
632 * arguments and constructor arguments."
633 *
634 * I take this to mean that the language doesn't let you put a
635 * precision qualifier on a constructor (or literal), but the
636 * constructor operation gets precision qualification inference
637 * based on its args like normal.
638 */
639 result = float(a) * b;
640 }
641 )"));
642
643 EXPECT_EQ(op_dest_bits(nir_op_fmul), 32);
644 }
645
TEST_F(gl_nir_lower_mediump_test,vec2_constructor)646 TEST_F(gl_nir_lower_mediump_test, vec2_constructor)
647 {
648 ASSERT_NO_FATAL_FAILURE(compile(
649 R"(#version 310 es
650 precision mediump float;
651 uniform highp float a, b;
652 uniform mediump float c;
653 out mediump vec2 result;
654
655 void main()
656 {
657 result = c * vec2(a, b);
658 }
659 )"));
660
661 EXPECT_EQ(op_dest_bits(nir_op_fmul), 32);
662 }
TEST_F(gl_nir_lower_mediump_test,vec4_of_float_constructor)663 TEST_F(gl_nir_lower_mediump_test, vec4_of_float_constructor)
664 {
665 ASSERT_NO_FATAL_FAILURE(compile(
666 R"(#version 310 es
667 precision mediump float;
668 uniform highp float a;
669 uniform mediump float b;
670 out mediump vec4 result;
671
672 void main()
673 {
674 result = b * vec4(a);
675 }
676 )"));
677
678 EXPECT_EQ(op_dest_bits(nir_op_fmul), 32);
679 }
680
TEST_F(gl_nir_lower_mediump_test,vec4_of_vec2_constructor)681 TEST_F(gl_nir_lower_mediump_test, vec4_of_vec2_constructor)
682 {
683 ASSERT_NO_FATAL_FAILURE(compile(
684 R"(#version 310 es
685 precision mediump float;
686 uniform highp vec2 a, b;
687 uniform mediump vec4 c;
688 out mediump vec4 result;
689
690 void main()
691 {
692 /* GLSL IR has to either have a temp for a*b, or clone the
693 * expression and let it get CSEed later. If it chooses temp, that
694 * may confuse us.
695 */
696 result = c + vec4(a * b, 0.0, 0.0);
697 }
698 )"));
699
700 EXPECT_EQ(op_dest_bits(nir_op_fmul), 32);
701 EXPECT_EQ(op_dest_bits(nir_op_fadd), 32);
702 }
703
TEST_F(gl_nir_lower_mediump_test,float_literal_mediump)704 TEST_F(gl_nir_lower_mediump_test, float_literal_mediump)
705 {
706 ASSERT_NO_FATAL_FAILURE(compile(
707 R"(#version 310 es
708 precision highp float;
709 uniform mediump float a;
710 out highp float result;
711
712 void main()
713 {
714 /* The literal is unqualified, so it shouldn't promote the expression to highp. */
715 result = a * 2.0;
716 }
717 )"));
718
719 EXPECT_EQ(op_dest_bits(nir_op_fmul), 16);
720 }
721
TEST_F(gl_nir_lower_mediump_test,float_const_highp)722 TEST_F(gl_nir_lower_mediump_test, float_const_highp)
723 {
724 ASSERT_NO_FATAL_FAILURE(compile(
725 R"(#version 310 es
726 precision highp float;
727 uniform mediump float a;
728 out highp float result;
729
730 void main()
731 {
732 highp float two = 2.0;
733 /* The constant is highp, so even with constant propagation the expression should be highp. */
734 result = a * two;
735 }
736 )"));
737
738 EXPECT_EQ(op_dest_bits(nir_op_fmul), 32);
739 }
740
TEST_F(gl_nir_lower_mediump_test,float_const_expr_mediump)741 TEST_F(gl_nir_lower_mediump_test, float_const_expr_mediump)
742 {
743 ASSERT_NO_FATAL_FAILURE(compile(
744 R"(#version 310 es
745 precision highp float;
746 uniform mediump float a;
747 out highp float result;
748
749 void main()
750 {
751 /* "Where the precision of a constant integral or constant floating
752 * point expression is not specified, evaluation is performed at
753 * highp. This rule does not affect the precision qualification of the
754 * expression."
755 * So the 5.0 is calculated at highp, but a * 5.0 is calculated at mediump.
756 */
757 result = a * (2.0 + 3.0);
758 }
759 )"));
760
761 EXPECT_EQ(op_dest_bits(nir_op_fmul), 16);
762 }
763
TEST_F(gl_nir_lower_mediump_test,unpackUnorm4x8)764 TEST_F(gl_nir_lower_mediump_test, unpackUnorm4x8)
765 {
766 ASSERT_NO_FATAL_FAILURE(compile(
767 R"(#version 310 es
768 precision highp float;
769 uniform highp uint a;
770 uniform mediump float b;
771 out highp float result;
772
773 void main()
774 {
775 result = unpackUnorm4x8(a).x * b;
776 }
777 )"));
778
779 /* XXX: GLSL doesn't lower this one correctly, currently. It returns highp despite the prototype being mediump. */
780 EXPECT_NONFATAL_FAILURE(
781 EXPECT_PRED_FORMAT2(glsl_ir_contains, fs_ir, "expression f16vec4 unpackUnorm4x8 (var_ref a"),
782 "not found in GLSL IR");
783 EXPECT_PRED_FORMAT2(glsl_ir_contains, fs_ir, "expression float16_t *");
784
785 /* XXX: NIR insists that nir_op_unpack_unorm_4x8 returns 32 bits per channel, too. */
786 EXPECT_NONFATAL_FAILURE(
787 EXPECT_EQ(op_dest_bits(nir_op_unpack_unorm_4x8), 16),
788 "op_dest_bits");
789 EXPECT_EQ(op_dest_bits(nir_op_fmul), 16);
790 }
791
TEST_F(gl_nir_lower_mediump_test,packUnorm4x8)792 TEST_F(gl_nir_lower_mediump_test, packUnorm4x8)
793 {
794 ASSERT_NO_FATAL_FAILURE(compile(
795 R"(#version 310 es
796 precision highp float;
797 uniform mediump vec4 a;
798 uniform mediump uint b;
799 out highp uint result;
800
801 void main()
802 {
803 result = packUnorm4x8(a) & b;
804 }
805 )"));
806
807 /* Test both the GLSL IR return value and an op using it with a mediump
808 * value, so we can be sure it's not just that we're assigning to highp.
809 */
810 EXPECT_PRED_FORMAT2(glsl_ir_contains, fs_ir, "expression uint packUnorm4x8 (var_ref a)");
811 EXPECT_PRED_FORMAT2(glsl_ir_contains, fs_ir, "expression uint &");
812
813 EXPECT_EQ(op_dest_bits(nir_op_pack_unorm_4x8), 32);
814 }
815
816 /* XXX: Add unit tests getting at precision of temporaries inside builtin function impls. */
817 /* XXX: Add unit tests getting at precision of any other temps internally generated by the compiler */
818 /* XXX: Add unit tests checking for default precision on user-declared function temps*/
819
820 #endif /* HAVE_FMEMOPEN */
821