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