• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, “operationincludes operators, built-in
631              * functions, and constructors, andoperandincludes 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