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