1 /*-------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2018 The Khronos Group Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief VK_KHR_shader_float_controls2 tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #define _USE_MATH_DEFINES
25 
26 #include "vktSpvAsmFloatControlsTests.hpp"
27 #include "vktSpvAsmComputeShaderCase.hpp"
28 #include "vktSpvAsmGraphicsShaderTestUtil.hpp"
29 #include "vktTestGroupUtil.hpp"
30 #include "tcuFloat.hpp"
31 #include "tcuFloatFormat.hpp"
32 #include "tcuStringTemplate.hpp"
33 #include "deUniquePtr.hpp"
34 #include "deFloat16.h"
35 #include "vkQueryUtil.hpp"
36 #include "vkRefUtil.hpp"
37 #include "spirv/unified1/spirv.hpp11"
38 #include <cstring>
39 #include <vector>
40 #include <limits>
41 #include <cstdint>
42 #include <fenv.h>
43 #include <cmath>
44 #include <cassert>
45 
46 namespace vkt
47 {
48 namespace SpirVAssembly
49 {
50 
51 namespace
52 {
53 
54 using namespace std;
55 using namespace tcu;
56 
57 enum FloatType
58 {
59     FP16 = 0,
60     FP32,
61     FP64
62 };
63 
64 enum class BufferDataType
65 {
66     DATA_UNKNOWN = 0,
67     DATA_FP16    = 1,
68     DATA_FP32    = 2,
69     DATA_FP64    = 3,
70 };
71 
72 enum FloatUsage
73 {
74     // If the float type is 16bit, then the use of the type is supported by
75     // VK_KHR_16bit_storage.
76     FLOAT_STORAGE_ONLY = 0,
77     // Use of the float type goes beyond VK_KHR_16bit_storage.
78     FLOAT_ARITHMETIC
79 };
80 
81 enum FloatStatementUsageBits
82 {
83     B_STATEMENT_USAGE_ARGS_CONST_FLOAT     = (1 << 0),
84     B_STATEMENT_USAGE_ARGS_CONST_FP16      = (1 << 1),
85     B_STATEMENT_USAGE_ARGS_CONST_FP32      = (1 << 2),
86     B_STATEMENT_USAGE_ARGS_CONST_FP64      = (1 << 3),
87     B_STATEMENT_USAGE_TYPES_TYPE_FLOAT     = (1 << 4),
88     B_STATEMENT_USAGE_TYPES_TYPE_FP16      = (1 << 5),
89     B_STATEMENT_USAGE_TYPES_TYPE_FP32      = (1 << 6),
90     B_STATEMENT_USAGE_TYPES_TYPE_FP64      = (1 << 7),
91     B_STATEMENT_USAGE_CONSTS_TYPE_FLOAT    = (1 << 8),
92     B_STATEMENT_USAGE_CONSTS_TYPE_FP16     = (1 << 9),
93     B_STATEMENT_USAGE_CONSTS_TYPE_FP32     = (1 << 10),
94     B_STATEMENT_USAGE_CONSTS_TYPE_FP64     = (1 << 11),
95     B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT = (1 << 12),
96     B_STATEMENT_USAGE_COMMANDS_CONST_FP16  = (1 << 13),
97     B_STATEMENT_USAGE_COMMANDS_CONST_FP32  = (1 << 14),
98     B_STATEMENT_USAGE_COMMANDS_CONST_FP64  = (1 << 15),
99     B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT  = (1 << 16),
100     B_STATEMENT_USAGE_COMMANDS_TYPE_FP16   = (1 << 17),
101     B_STATEMENT_USAGE_COMMANDS_TYPE_FP32   = (1 << 18),
102     B_STATEMENT_USAGE_COMMANDS_TYPE_FP64   = (1 << 19),
103 };
104 
105 typedef uint32_t FloatStatementUsageFlags;
106 
107 using FP = spv::FPFastMathModeMask;
108 
109 typedef map<FP, string> BehaviorNameMap;
110 BehaviorNameMap behaviorToName = {{FP::MaskNone, "None"},
111                                   {FP::NotNaN, "NotNaN"},
112                                   {FP::NotInf, "NotInf"},
113                                   {FP::NSZ, "NSZ"},
114                                   {FP::AllowRecip, "AllowRecip"},
115                                   {FP::AllowContract, "AllowContract"},
116                                   {FP::AllowReassoc, "AllowReassoc"},
117                                   {FP::AllowTransform, "AllowTransform"}};
118 
119 const FP allBits =
120     FP::NotNaN | FP::NotInf | FP::NSZ | FP::AllowRecip | FP::AllowContract | FP::AllowReassoc | FP::AllowTransform;
121 const FP allBitsExceptTransform = ~FP::AllowTransform & allBits;
invert(FP bfb)122 FP invert(FP bfb)
123 {
124     // AllowTransform requires AllowReassoc and AllowContract to also be set
125     if ((bfb & (FP::AllowReassoc | FP::AllowContract)) != FP::MaskNone)
126         return ~bfb & allBitsExceptTransform;
127     else
128         return ~bfb & allBits;
129 }
130 
getBehaviourName(FP flagbits,const char * separator)131 string getBehaviourName(FP flagbits, const char *separator)
132 {
133     string behaviorName = "";
134     bool needOrInName   = false;
135     if (flagbits == FP::MaskNone)
136         behaviorName = "None";
137     else
138         for (auto it = behaviorToName.begin(); it != behaviorToName.end(); it++)
139         {
140             if ((it->first & flagbits) != FP::MaskNone)
141             {
142                 if (needOrInName)
143                     behaviorName += separator;
144                 behaviorName += it->second;
145                 needOrInName = true;
146             }
147         }
148     return behaviorName;
149 }
150 
151 // Codes for all float values used in tests as arguments and operation results
152 enum ValueId
153 {
154     V_UNUSED = 0, // used to mark arguments that are not used in operation
155     V_MINUS_INF,  //    or results of tests cases that should be skipped
156     V_MINUS_ONE,  // -1.0
157     V_MINUS_ZERO, // -0.0
158     V_ZERO,       //  0.0
159     V_HALF,       //  0.5
160     V_ONE,        //  1.0
161     V_TWO,
162     V_INF,
163     V_ZERO_POINT_ONE,
164     V_TWENTY_FIVE_POINT_EIGHT,
165     V_HUGE, // a large number that if doubled will result in infinity but that is not equal to the maximum
166     V_TINY, // a number that if squared will underflow to 0.
167     V_MINUS_TINY,
168     V_MAX,
169     V_NAN,
170 
171     // non comon results of some operation - corner cases
172     V_PI,
173     V_MINUS_PI,
174     V_PI_DIV_2,
175     V_MINUS_PI_DIV_2,
176     V_PI_DIV_4,
177     V_MINUS_PI_DIV_4,
178     V_3_PI_DIV_4,
179     V_MINUS_3_PI_DIV_4,
180     V_ONE_OR_NAN,
181     V_SIGN_NAN,           // Can be any of -1, -0, +0, +1
182     V_ZERO_OR_MINUS_ZERO, // both +0 and -0 are accepted
183     V_ZERO_OR_ONE,        // both +0 and 1 are accepted
184     V_TRIG_ONE,           // 1.0 trigonometric operations, including precision margin
185 };
186 
getValueName(ValueId value)187 string getValueName(ValueId value)
188 {
189     switch (value)
190     {
191     case V_UNUSED:
192         return "unused";
193     case V_MINUS_INF:
194         return "minusInf";
195     case V_MINUS_ONE:
196         return "minusOne";
197     case V_MINUS_ZERO:
198         return "minusZero";
199     case V_ZERO:
200         return "zero";
201     case V_HALF:
202         return "half";
203     case V_ONE:
204         return "one";
205     case V_TWO:
206         return "two";
207     case V_INF:
208         return "inf";
209     case V_ZERO_POINT_ONE:
210         return "zeroPtOne";
211     case V_TWENTY_FIVE_POINT_EIGHT:
212         return "twentyFivePtEight";
213     case V_HUGE:
214         return "huge";
215     case V_TINY:
216         return "tiny";
217     case V_MINUS_TINY:
218         return "minusTiny";
219     case V_MAX:
220         return "max";
221     case V_NAN:
222         return "nan";
223     case V_PI:
224         return "pi";
225     case V_MINUS_PI:
226         return "minusPi";
227     case V_PI_DIV_2:
228         return "piDiv2";
229     case V_MINUS_PI_DIV_2:
230         return "minusPiDiv2";
231     case V_PI_DIV_4:
232         return "piDiv4";
233     case V_MINUS_PI_DIV_4:
234         return "minusPiDiv4";
235     case V_3_PI_DIV_4:
236         return "3PiDiv4";
237     case V_MINUS_3_PI_DIV_4:
238         return "minus3PiDiv4";
239     case V_ONE_OR_NAN:
240         return "oneORnan";
241     case V_SIGN_NAN:
242         return "signNan";
243     case V_ZERO_OR_MINUS_ZERO:
244         return "zeroOrMinusZero";
245     case V_ZERO_OR_ONE:
246         return "zeroOrOne";
247     case V_TRIG_ONE:
248         return "trigOne";
249     }
250     assert(false);
251     return "";
252 }
253 
254 // Enum containing all tested operatios. Operations are defined in generic way so that
255 // they can be used to generate tests operating on arguments with different values of
256 // specified float type.
257 enum OperationId
258 {
259     // spir-v unary operations
260     OID_NEGATE = 0,
261     OID_COMPOSITE,
262     OID_COMPOSITE_INS,
263     OID_COPY,
264     OID_D_EXTRACT,
265     OID_D_INSERT,
266     OID_SHUFFLE,
267     OID_TRANSPOSE,
268     OID_CONV_FROM_FP16,
269     OID_CONV_FROM_FP32,
270     OID_CONV_FROM_FP64,
271     OID_RETURN_VAL,
272 
273     // spir-v binary operations
274     OID_ADD,
275     OID_SUB,
276     OID_MUL,
277     OID_DIV,
278     OID_REM,
279     OID_MOD,
280     OID_PHI,
281     OID_SELECT,
282     OID_DOT,
283     OID_VEC_MUL_S,
284     OID_VEC_MUL_M,
285     OID_MAT_MUL_S,
286     OID_MAT_MUL_V,
287     OID_MAT_MUL_M,
288     OID_OUT_PROD,
289     OID_ORD_EQ,
290     OID_UORD_EQ,
291     OID_ORD_NEQ,
292     OID_UORD_NEQ,
293     OID_ORD_LS,
294     OID_UORD_LS,
295     OID_ORD_GT,
296     OID_UORD_GT,
297     OID_ORD_LE,
298     OID_UORD_LE,
299     OID_ORD_GE,
300     OID_UORD_GE,
301 
302     // glsl unary operations
303     OID_ROUND,
304     OID_ROUND_EV,
305     OID_TRUNC,
306     OID_ABS,
307     OID_SIGN,
308     OID_FLOOR,
309     OID_CEIL,
310     OID_FRACT,
311     OID_RADIANS,
312     OID_DEGREES,
313     OID_SIN,
314     OID_COS,
315     OID_TAN,
316     OID_ASIN,
317     OID_ACOS,
318     OID_ATAN,
319     OID_SINH,
320     OID_COSH,
321     OID_TANH,
322     OID_ASINH,
323     OID_ACOSH,
324     OID_ATANH,
325     OID_EXP,
326     OID_LOG,
327     OID_EXP2,
328     OID_LOG2,
329     OID_SQRT,
330     OID_INV_SQRT,
331     OID_MODF,
332     OID_MODF_ST_WH, // Whole number part of modf
333     OID_MODF_ST_FR, // Fractional part of modf
334     OID_LDEXP,
335     OID_FREXP,
336     OID_FREXP_ST,
337     OID_LENGTH,
338     OID_NORMALIZE,
339     OID_REFLECT,
340     OID_REFRACT,
341     OID_MAT_DET,
342     OID_MAT_INV,
343 
344     // glsl binary operations
345     OID_ATAN2,
346     OID_POW,
347     OID_MIX,
348     OID_FMA,
349     OID_FMA2PT58,
350     OID_SZ_FMA,
351     OID_MIN,
352     OID_MAX,
353     OID_CLAMP,
354     OID_STEP,
355     OID_SSTEP,
356     OID_DIST,
357     OID_CROSS,
358     OID_FACE_FWD,
359     OID_NMIN,
360     OID_NMAX,
361     OID_NCLAMP,
362 
363     OID_ADD_SUB_REASSOCIABLE,
364 };
365 
366 // Function replacing all occurrences of substring with string passed in last parameter.
replace(string str,const string & from,const string & to)367 string replace(string str, const string &from, const string &to)
368 {
369     // to keep spir-v code clean and easier to read parts of it are processed
370     // with this method instead of StringTemplate; main usage of this method is the
371     // replacement of "float_" with "f16_", "f32_" or "f64_" depending on test case
372 
373     size_t start_pos = 0;
374     while ((start_pos = str.find(from, start_pos)) != std::string::npos)
375     {
376         str.replace(start_pos, from.length(), to);
377         start_pos += to.length();
378     }
379     return str;
380 }
381 
382 // Structure used to perform bits conversion int type <-> float type.
383 template <typename FLOAT_TYPE, typename UINT_TYPE>
384 struct RawConvert
385 {
386     union Value
387     {
388         FLOAT_TYPE fp;
389         UINT_TYPE ui;
390     };
391 };
392 
393 // Traits used to get int type that can store equivalent float type.
394 template <typename FLOAT_TYPE>
395 struct GetCoresponding
396 {
397     typedef uint16_t uint_type;
398 };
399 template <>
400 struct GetCoresponding<float>
401 {
402     typedef uint32_t uint_type;
403 };
404 template <>
405 struct GetCoresponding<double>
406 {
407     typedef uint64_t uint_type;
408 };
409 
410 // All values used for arguments and operation results are stored in single map.
411 // Each float type (fp16, fp32, fp64) has its own map that is used during
412 // test setup and during verification. TypeValuesBase is interface to that map.
413 class TypeValuesBase
414 {
415 public:
416     TypeValuesBase();
417     virtual ~TypeValuesBase() = default;
418 
419     virtual BufferSp constructInputBuffer(const ValueId *twoArguments) const                                     = 0;
420     virtual BufferSp constructOutputBuffer(ValueId result) const                                                 = 0;
421     virtual void fillInputData(const ValueId *twoArguments, vector<uint8_t> &bufferData, uint32_t &offset) const = 0;
422 };
423 
TypeValuesBase()424 TypeValuesBase::TypeValuesBase()
425 {
426 }
427 
428 typedef de::SharedPtr<TypeValuesBase> TypeValuesSP;
429 
430 template <typename FLOAT_TYPE>
431 class TypeValues : public TypeValuesBase
432 {
433 public:
434     TypeValues();
435 
436     BufferSp constructInputBuffer(const ValueId *twoArguments) const override;
437     BufferSp constructOutputBuffer(ValueId result) const override;
438     void fillInputData(const ValueId *twoArguments, vector<uint8_t> &bufferData, uint32_t &offset) const override;
439 
440     FLOAT_TYPE getValue(ValueId id) const;
441 
442     template <typename UINT_TYPE>
443     FLOAT_TYPE exactByteEquivalent(UINT_TYPE byteValue) const;
444 
445 private:
446     typedef map<ValueId, FLOAT_TYPE> ValueMap;
447     ValueMap m_valueIdToFloatType;
448 };
449 
450 template <typename FLOAT_TYPE>
constructInputBuffer(const ValueId * twoArguments) const451 BufferSp TypeValues<FLOAT_TYPE>::constructInputBuffer(const ValueId *twoArguments) const
452 {
453     std::vector<FLOAT_TYPE> inputData(2);
454     inputData[0] = m_valueIdToFloatType.at(twoArguments[0]);
455     inputData[1] = m_valueIdToFloatType.at(twoArguments[1]);
456     return BufferSp(new Buffer<FLOAT_TYPE>(inputData));
457 }
458 
459 template <typename FLOAT_TYPE>
constructOutputBuffer(ValueId result) const460 BufferSp TypeValues<FLOAT_TYPE>::constructOutputBuffer(ValueId result) const
461 {
462     // note: we are not doing maping here, ValueId is directly saved in
463     // float type in order to be able to retireve it during verification
464 
465     typedef typename GetCoresponding<FLOAT_TYPE>::uint_type uint_t;
466     uint_t value = static_cast<uint_t>(result);
467 
468     // For FP16 we increase the buffer size to hold an unsigned integer, as
469     // we can be in the no 16bit_storage case.
470     const uint_t outputSize = sizeof(FLOAT_TYPE) == 2u ? 2u : 1u;
471     std::vector<FLOAT_TYPE> outputData(outputSize, exactByteEquivalent<uint_t>(value));
472     return BufferSp(new Buffer<FLOAT_TYPE>(outputData));
473 }
474 
475 template <typename FLOAT_TYPE>
fillInputData(const ValueId * twoArguments,vector<uint8_t> & bufferData,uint32_t & offset) const476 void TypeValues<FLOAT_TYPE>::fillInputData(const ValueId *twoArguments, vector<uint8_t> &bufferData,
477                                            uint32_t &offset) const
478 {
479     uint32_t typeSize = sizeof(FLOAT_TYPE);
480 
481     FLOAT_TYPE argA = getValue(twoArguments[0]);
482     deMemcpy(&bufferData[offset], &argA, typeSize);
483     offset += typeSize;
484 
485     FLOAT_TYPE argB = getValue(twoArguments[1]);
486     deMemcpy(&bufferData[offset], &argB, typeSize);
487     offset += typeSize;
488 }
489 
490 template <typename FLOAT_TYPE>
getValue(ValueId id) const491 FLOAT_TYPE TypeValues<FLOAT_TYPE>::getValue(ValueId id) const
492 {
493     return m_valueIdToFloatType.at(id);
494 }
495 
496 template <typename FLOAT_TYPE>
497 template <typename UINT_TYPE>
exactByteEquivalent(UINT_TYPE byteValue) const498 FLOAT_TYPE TypeValues<FLOAT_TYPE>::exactByteEquivalent(UINT_TYPE byteValue) const
499 {
500     typename RawConvert<FLOAT_TYPE, UINT_TYPE>::Value value;
501     value.ui = byteValue;
502     return value.fp;
503 }
504 
505 template <>
TypeValues()506 TypeValues<deFloat16>::TypeValues() : TypeValuesBase()
507 {
508     // NOTE: when updating entries in m_valueIdToFloatType make sure to
509     // update also valueIdToSnippetArgMap defined in updateSpirvSnippets()
510     ValueMap &vm                  = m_valueIdToFloatType;
511     vm[V_UNUSED]                  = deFloat32To16(0.0f);
512     vm[V_MINUS_INF]               = 0xfc00;
513     vm[V_MINUS_ONE]               = deFloat32To16(-1.0f);
514     vm[V_MINUS_ZERO]              = 0x8000;
515     vm[V_ZERO]                    = 0x0000;
516     vm[V_HALF]                    = deFloat32To16(0.5f);
517     vm[V_ONE]                     = deFloat32To16(1.0f);
518     vm[V_TWO]                     = deFloat32To16(2.0f);
519     vm[V_ZERO_POINT_ONE]          = deFloat32To16(0.1f);
520     vm[V_TWENTY_FIVE_POINT_EIGHT] = deFloat32To16(25.8f);
521     vm[V_HUGE]                    = 0x7bfd;
522     vm[V_TINY]                    = 0x0400;
523     vm[V_MINUS_TINY]              = 0x8400;
524     vm[V_MAX]                     = 0x7bff;
525     vm[V_INF]                     = 0x7c00;
526     vm[V_NAN]                     = 0x7cf0;
527 
528     vm[V_PI]               = deFloat32To16((float)M_PI);
529     vm[V_MINUS_PI]         = deFloat32To16(-(float)M_PI);
530     vm[V_PI_DIV_2]         = deFloat32To16((float)M_PI_2);
531     vm[V_MINUS_PI_DIV_2]   = deFloat32To16(-(float)M_PI_2);
532     vm[V_PI_DIV_4]         = deFloat32To16((float)M_PI_4);
533     vm[V_MINUS_PI_DIV_4]   = deFloat32To16(-(float)M_PI_4);
534     vm[V_3_PI_DIV_4]       = deFloat32To16((float)(3 * M_PI_4));
535     vm[V_MINUS_3_PI_DIV_4] = deFloat32To16(-(float)(3 * M_PI_4));
536 }
537 
538 template <>
TypeValues()539 TypeValues<float>::TypeValues() : TypeValuesBase()
540 {
541     // NOTE: when updating entries in m_valueIdToFloatType make sure to
542     // update also valueIdToSnippetArgMap defined in updateSpirvSnippets()
543     ValueMap &vm                  = m_valueIdToFloatType;
544     vm[V_UNUSED]                  = 0.0f;
545     vm[V_MINUS_INF]               = -std::numeric_limits<float>::infinity();
546     vm[V_MINUS_ONE]               = -1.0f;
547     vm[V_MINUS_ZERO]              = -0.0f;
548     vm[V_ZERO]                    = 0.0f;
549     vm[V_HALF]                    = 0.5f;
550     vm[V_ONE]                     = 1.0f;
551     vm[V_TWO]                     = 2.0f;
552     vm[V_ZERO_POINT_ONE]          = 0.1f;
553     vm[V_TWENTY_FIVE_POINT_EIGHT] = 25.8f;
554     vm[V_HUGE]                    = 3.40282306073709652508e+38;
555     vm[V_TINY]                    = 1.17549435082228750797e-38;
556     vm[V_MINUS_TINY]              = -1.17549435082228750797e-38;
557     vm[V_MAX]                     = std::numeric_limits<float>::max();
558     vm[V_INF]                     = std::numeric_limits<float>::infinity();
559     vm[V_NAN]                     = std::numeric_limits<float>::quiet_NaN();
560 
561     vm[V_PI]               = static_cast<float>(M_PI);
562     vm[V_MINUS_PI]         = -static_cast<float>(M_PI);
563     vm[V_PI_DIV_2]         = static_cast<float>(M_PI_2);
564     vm[V_MINUS_PI_DIV_2]   = -static_cast<float>(M_PI_2);
565     vm[V_PI_DIV_4]         = static_cast<float>(M_PI_4);
566     vm[V_MINUS_PI_DIV_4]   = -static_cast<float>(M_PI_4);
567     vm[V_3_PI_DIV_4]       = static_cast<float>(3 * M_PI_4);
568     vm[V_MINUS_3_PI_DIV_4] = -static_cast<float>(3 * M_PI_4);
569 }
570 
571 template <>
TypeValues()572 TypeValues<double>::TypeValues() : TypeValuesBase()
573 {
574     // NOTE: when updating entries in m_valueIdToFloatType make sure to
575     // update also valueIdToSnippetArgMap defined in updateSpirvSnippets()
576     ValueMap &vm                  = m_valueIdToFloatType;
577     vm[V_UNUSED]                  = 0.0;
578     vm[V_MINUS_INF]               = -std::numeric_limits<double>::infinity();
579     vm[V_MINUS_ONE]               = -1.0;
580     vm[V_MINUS_ZERO]              = -0.0;
581     vm[V_ZERO]                    = 0.0;
582     vm[V_HALF]                    = 0.5;
583     vm[V_ONE]                     = 1.0;
584     vm[V_TWO]                     = 2.0;
585     vm[V_ZERO_POINT_ONE]          = 0.1;
586     vm[V_TWENTY_FIVE_POINT_EIGHT] = 25.8;
587     vm[V_HUGE]                    = 1.79769313486231530898e+308;
588     vm[V_TINY]                    = 2.22507385850720138309e-308;
589     vm[V_MINUS_TINY]              = -2.22507385850720138309e-308;
590     vm[V_MAX]                     = std::numeric_limits<double>::max();
591     vm[V_INF]                     = std::numeric_limits<double>::infinity();
592     vm[V_NAN]                     = std::numeric_limits<double>::quiet_NaN();
593 
594     vm[V_PI]               = M_PI;
595     vm[V_MINUS_PI]         = -M_PI;
596     vm[V_PI_DIV_2]         = M_PI_2;
597     vm[V_MINUS_PI_DIV_2]   = -M_PI_2;
598     vm[V_PI_DIV_4]         = M_PI_4;
599     vm[V_MINUS_PI_DIV_4]   = -M_PI_4;
600     vm[V_3_PI_DIV_4]       = 3 * M_PI_4;
601     vm[V_MINUS_3_PI_DIV_4] = -3 * M_PI_4;
602 }
603 
604 // Each float type (fp16, fp32, fp64) has specific set of SPIR-V snippets
605 // that was extracted to separate template specialization. Those snippets
606 // are used to compose final test shaders. With this approach
607 // parameterization can be done just once per type and reused for many tests.
608 class TypeSnippetsBase
609 {
610 public:
611     virtual ~TypeSnippetsBase() = default;
612 
613 protected:
614     void updateSpirvSnippets();
615 
616 public: // Type specific data:
617     // Number of bits consumed by float type
618     string bitWidth;
619     // Minimum positive normal
620     string epsilon;
621     string capabilities;
622     string extensions;
623     string capabilitiesFp16Without16BitStorage;
624     string extensionsFp16Without16BitStorage;
625     string arrayStride;
626 
627     bool loadStoreRequiresShaderFloat16;
628 
629 public: // Type specific spir-v snippets:
630     // Common annotations
631     string typeAnnotationsSnippet;
632 
633     // Definitions of all types commonly used by operation tests
634     string typeDefinitionsSnippet;
635 
636     // Definitions of all types commonly used by settings tests
637     string minTypeDefinitionsSnippet;
638 
639     // Definitions of all constants commonly used by tests
640     string constantsDefinitionsSnippet;
641 
642     // Map that stores instructions that generate arguments of specified value.
643     // Every test that uses generated inputod will select up to two items from this map
644     typedef map<ValueId, string> SnippetMap;
645     SnippetMap valueIdToSnippetArgMap;
646 
647     // Spir-v snippets that read argument from SSBO
648     string argumentsFromInputSnippet;
649     string multiArgumentsFromInputSnippet;
650 
651     // SSBO with stage input/output definitions
652     string inputAnnotationsSnippet;
653     string inputDefinitionsSnippet;
654     string outputAnnotationsSnippet;
655     string multiOutputAnnotationsSnippet;
656     string outputDefinitionsSnippet;
657     string multiOutputDefinitionsSnippet;
658 
659     // Varying is required to pass result from vertex stage to fragment stage,
660     // one of requirements was to not use SSBO writes in vertex stage so we
661     // need to do that in fragment stage; we also cant pass operation result
662     // directly because of interpolation, to avoid it we do a bitcast to uint
663     string varyingsTypesSnippet;
664     string inputVaryingsSnippet;
665     string outputVaryingsSnippet;
666     string storeVertexResultSnippet;
667     string loadVertexResultSnippet;
668 
669     string storeResultsSnippet;
670     string multiStoreResultsSnippet;
671 
672     string argumentsFromInputFp16Snippet;
673     string storeResultsFp16Snippet;
674     string multiArgumentsFromInputFp16Snippet;
675     string multiOutputAnnotationsFp16Snippet;
676     string multiStoreResultsFp16Snippet;
677     string multiOutputDefinitionsFp16Snippet;
678     string inputDefinitionsFp16Snippet;
679     string outputDefinitionsFp16Snippet;
680     string typeAnnotationsFp16Snippet;
681     string typeDefinitionsFp16Snippet;
682 };
683 
updateSpirvSnippets()684 void TypeSnippetsBase::updateSpirvSnippets()
685 {
686     // annotations to types that are commonly used by tests
687     const string typeAnnotationsTemplate = "OpDecorate %type_float_arr_1 ArrayStride " + arrayStride +
688                                            "\n"
689                                            "OpDecorate %type_float_arr_2 ArrayStride " +
690                                            arrayStride + "\n";
691 
692     // definition off all types that are commonly used by tests
693     const string typeDefinitionsTemplate = "%type_float             = OpTypeFloat " + bitWidth +
694                                            "\n"
695                                            "%type_float_uptr        = OpTypePointer Uniform %type_float\n"
696                                            "%type_float_fptr        = OpTypePointer Function %type_float\n"
697                                            "%type_float_vec2        = OpTypeVector %type_float 2\n"
698                                            "%type_float_vec3        = OpTypeVector %type_float 3\n"
699                                            "%type_float_vec4        = OpTypeVector %type_float 4\n"
700                                            "%type_float_vec4_iptr   = OpTypePointer Input %type_float_vec4\n"
701                                            "%type_float_vec4_optr   = OpTypePointer Output %type_float_vec4\n"
702                                            "%type_float_mat2x2      = OpTypeMatrix %type_float_vec2 2\n"
703                                            "%type_float_arr_1       = OpTypeArray %type_float %c_i32_1\n"
704                                            "%type_float_arr_2       = OpTypeArray %type_float %c_i32_2\n";
705 
706     // minimal type definition set that is used by settings tests
707     const string minTypeDefinitionsTemplate = "%type_float             = OpTypeFloat " + bitWidth +
708                                               "\n"
709                                               "%type_float_uptr        = OpTypePointer Uniform %type_float\n"
710                                               "%type_float_arr_2       = OpTypeArray %type_float %c_i32_2\n";
711 
712     // definition off all constants that are used by tests
713     const string constantsDefinitionsTemplate = "%c_float_n1             = OpConstant %type_float -1\n"
714                                                 "%c_float_n2pt58         = OpConstant %type_float -2.58\n"
715                                                 "%c_float_0              = OpConstant %type_float 0.0\n"
716                                                 "%c_float_0_5            = OpConstant %type_float 0.5\n"
717                                                 "%c_float_1              = OpConstant %type_float 1\n"
718                                                 "%c_float_2              = OpConstant %type_float 2\n"
719                                                 "%c_float_3              = OpConstant %type_float 3\n"
720                                                 "%c_float_4              = OpConstant %type_float 4\n"
721                                                 "%c_float_5              = OpConstant %type_float 5\n"
722                                                 "%c_float_6              = OpConstant %type_float 6\n"
723                                                 "%c_float_eps            = OpConstant %type_float " +
724                                                 epsilon + "\n";
725 
726     // when arguments are read from SSBO this snipped is placed in main function
727     const string argumentsFromInputTemplate =
728         "%arg1loc                = OpAccessChain %type_float_uptr %ssbo_in %c_i32_0 %c_i32_0\n"
729         "%arg1                   = OpLoad %type_float %arg1loc\n"
730         "%arg2loc                = OpAccessChain %type_float_uptr %ssbo_in %c_i32_0 %c_i32_1\n"
731         "%arg2                   = OpLoad %type_float %arg2loc\n";
732 
733     const string multiArgumentsFromInputTemplate =
734         "%arg1_float_loc         = OpAccessChain %type_float_uptr %ssbo_in %c_i32_${attr} %c_i32_0\n"
735         "%arg2_float_loc         = OpAccessChain %type_float_uptr %ssbo_in %c_i32_${attr} %c_i32_1\n"
736         "%arg1_float             = OpLoad %type_float %arg1_float_loc\n"
737         "%arg2_float             = OpLoad %type_float %arg2_float_loc\n";
738 
739     // when tested shader stage reads from SSBO it has to have this snippet
740     inputAnnotationsSnippet = "OpMemberDecorate %SSBO_in 0 Offset 0\n"
741                               "OpDecorate %SSBO_in BufferBlock\n"
742                               "OpDecorate %ssbo_in DescriptorSet 0\n"
743                               "OpDecorate %ssbo_in Binding 0\n"
744                               "OpDecorate %ssbo_in NonWritable\n";
745 
746     const string inputDefinitionsTemplate = "%SSBO_in              = OpTypeStruct %type_float_arr_2\n"
747                                             "%up_SSBO_in           = OpTypePointer Uniform %SSBO_in\n"
748                                             "%ssbo_in              = OpVariable %up_SSBO_in Uniform\n";
749 
750     outputAnnotationsSnippet = "OpMemberDecorate %SSBO_out 0 Offset 0\n"
751                                "OpDecorate %SSBO_out BufferBlock\n"
752                                "OpDecorate %ssbo_out DescriptorSet 0\n"
753                                "OpDecorate %ssbo_out Binding 1\n";
754 
755     const string multiOutputAnnotationsTemplate = "OpMemberDecorate %SSBO_float_out 0 Offset 0\n"
756                                                   "OpDecorate %type_float_arr_2 ArrayStride " +
757                                                   arrayStride +
758                                                   "\n"
759                                                   "OpDecorate %SSBO_float_out BufferBlock\n"
760                                                   "OpDecorate %ssbo_float_out DescriptorSet 0\n";
761 
762     const string outputDefinitionsTemplate = "%SSBO_out             = OpTypeStruct %type_float_arr_1\n"
763                                              "%up_SSBO_out          = OpTypePointer Uniform %SSBO_out\n"
764                                              "%ssbo_out             = OpVariable %up_SSBO_out Uniform\n";
765 
766     const string multiOutputDefinitionsTemplate = "%SSBO_float_out         = OpTypeStruct %type_float\n"
767                                                   "%up_SSBO_float_out      = OpTypePointer Uniform %SSBO_float_out\n"
768                                                   "%ssbo_float_out         = OpVariable %up_SSBO_float_out Uniform\n";
769 
770     // this snippet is used by compute and fragment stage but not by vertex stage
771     const string storeResultsTemplate =
772         "%outloc               = OpAccessChain %type_float_uptr %ssbo_out %c_i32_0 %c_i32_0\n"
773         "OpStore %outloc %result\n";
774 
775     const string multiStoreResultsTemplate = "%outloc" + bitWidth +
776                                              "             = OpAccessChain %type_float_uptr %ssbo_float_out %c_i32_0\n"
777                                              "                        OpStore %outloc" +
778                                              bitWidth + " %result" + bitWidth + "\n";
779 
780     const string typeToken = "_float";
781     const string typeName  = "_f" + bitWidth;
782 
783     typeAnnotationsSnippet         = replace(typeAnnotationsTemplate, typeToken, typeName);
784     typeDefinitionsSnippet         = replace(typeDefinitionsTemplate, typeToken, typeName);
785     minTypeDefinitionsSnippet      = replace(minTypeDefinitionsTemplate, typeToken, typeName);
786     constantsDefinitionsSnippet    = replace(constantsDefinitionsTemplate, typeToken, typeName);
787     argumentsFromInputSnippet      = replace(argumentsFromInputTemplate, typeToken, typeName);
788     multiArgumentsFromInputSnippet = replace(multiArgumentsFromInputTemplate, typeToken, typeName);
789     inputDefinitionsSnippet        = replace(inputDefinitionsTemplate, typeToken, typeName);
790     multiOutputAnnotationsSnippet  = replace(multiOutputAnnotationsTemplate, typeToken, typeName);
791     outputDefinitionsSnippet       = replace(outputDefinitionsTemplate, typeToken, typeName);
792     multiOutputDefinitionsSnippet  = replace(multiOutputDefinitionsTemplate, typeToken, typeName);
793     storeResultsSnippet            = replace(storeResultsTemplate, typeToken, typeName);
794     multiStoreResultsSnippet       = replace(multiStoreResultsTemplate, typeToken, typeName);
795 
796     argumentsFromInputFp16Snippet      = "";
797     storeResultsFp16Snippet            = "";
798     multiArgumentsFromInputFp16Snippet = "";
799     multiOutputAnnotationsFp16Snippet  = "";
800     multiStoreResultsFp16Snippet       = "";
801     multiOutputDefinitionsFp16Snippet  = "";
802     inputDefinitionsFp16Snippet        = "";
803     typeAnnotationsFp16Snippet         = "";
804     outputDefinitionsFp16Snippet       = "";
805     typeDefinitionsFp16Snippet         = "";
806 
807     if (bitWidth.compare("16") == 0)
808     {
809         typeDefinitionsFp16Snippet = "%type_u32_uptr       = OpTypePointer Uniform %type_u32\n"
810                                      "%type_u32_arr_1      = OpTypeArray %type_u32 %c_i32_1\n";
811 
812         typeAnnotationsFp16Snippet  = "OpDecorate %type_u32_arr_1 ArrayStride 4\n";
813         const string inputToken     = "_f16_arr_2";
814         const string inputName      = "_u32_arr_1";
815         inputDefinitionsFp16Snippet = replace(inputDefinitionsSnippet, inputToken, inputName);
816 
817         argumentsFromInputFp16Snippet = "%argloc            = OpAccessChain %type_u32_uptr %ssbo_in %c_i32_0 %c_i32_0\n"
818                                         "%inval             = OpLoad %type_u32 %argloc\n"
819                                         "%arg               = OpBitcast %type_f16_vec2 %inval\n"
820                                         "%arg1              = OpCompositeExtract %type_f16 %arg 0\n"
821                                         "%arg2              = OpCompositeExtract %type_f16 %arg 1\n";
822 
823         const string outputToken     = "_f16_arr_1";
824         const string outputName      = "_u32_arr_1";
825         outputDefinitionsFp16Snippet = replace(outputDefinitionsSnippet, outputToken, outputName);
826 
827         storeResultsFp16Snippet = "%result_f16_vec2   = OpCompositeConstruct %type_f16_vec2 %result %c_f16_0\n"
828                                   "%result_u32 = OpBitcast %type_u32 %result_f16_vec2\n"
829                                   "%outloc            = OpAccessChain %type_u32_uptr %ssbo_out %c_i32_0 %c_i32_0\n"
830                                   "OpStore %outloc %result_u32\n";
831 
832         multiArgumentsFromInputFp16Snippet =
833             "%arg_u32_loc         = OpAccessChain %type_u32_uptr %ssbo_in %c_i32_${attr} %c_i32_0\n"
834             "%arg_u32             = OpLoad %type_u32 %arg_u32_loc\n"
835             "%arg_f16_vec2        = OpBitcast %type_f16_vec2 %arg_u32\n"
836             "%arg1_f16            = OpCompositeExtract %type_f16 %arg_f16_vec2 0\n"
837             "%arg2_f16            = OpCompositeExtract %type_f16 %arg_f16_vec2 1\n";
838 
839         multiOutputAnnotationsFp16Snippet = "OpMemberDecorate %SSBO_u32_out 0 Offset 0\n"
840                                             "OpDecorate %type_u32_arr_1 ArrayStride 4\n"
841                                             "OpDecorate %SSBO_u32_out BufferBlock\n"
842                                             "OpDecorate %ssbo_u32_out DescriptorSet 0\n";
843 
844         multiStoreResultsFp16Snippet = "%outloc_u32            = OpAccessChain %type_u32_uptr %ssbo_u32_out %c_i32_0\n"
845                                        "%result16_vec2 = OpCompositeConstruct %type_f16_vec2 %result16 %c_f16_0\n"
846                                        "%result_u32            = OpBitcast %type_u32 %result16_vec2\n"
847                                        "                        OpStore %outloc_u32 %result_u32\n";
848 
849         multiOutputDefinitionsFp16Snippet = "%c_f16_0              = OpConstant %type_f16 0.0\n"
850                                             "%SSBO_u32_out         = OpTypeStruct %type_u32\n"
851                                             "%up_SSBO_u32_out      = OpTypePointer Uniform %SSBO_u32_out\n"
852                                             "%ssbo_u32_out         = OpVariable %up_SSBO_u32_out Uniform\n";
853     }
854 
855     // NOTE: only values used as _generated_ arguments in test operations
856     // need to be in this map, arguments that are only used by tests, ??????? generated vs input
857     // that grab arguments from input, do need to be in this map
858     // NOTE: when updating entries in valueIdToSnippetArgMap make
859     // sure to update also m_valueIdToFloatType for all float width
860     SnippetMap &sm   = valueIdToSnippetArgMap;
861     sm[V_UNUSED]     = "OpFSub %type_float %c_float_0 %c_float_0\n";
862     sm[V_MINUS_INF]  = "OpFDiv %type_float %c_float_n1 %c_float_0\n";
863     sm[V_MINUS_ONE]  = "OpFAdd %type_float %c_float_n1 %c_float_0\n";
864     sm[V_MINUS_ZERO] = "OpFMul %type_float %c_float_n1 %c_float_0\n";
865     sm[V_ZERO]       = "OpFMul %type_float %c_float_0 %c_float_0\n";
866     sm[V_HALF]       = "OpFAdd %type_float %c_float_0_5 %c_float_0\n";
867     sm[V_ONE]        = "OpFAdd %type_float %c_float_1 %c_float_0\n";
868     sm[V_INF]        = "OpFDiv %type_float %c_float_1 %c_float_0\n"; // x / 0 == Inf
869     sm[V_NAN]        = "OpFDiv %type_float %c_float_0 %c_float_0\n"; // 0 / 0 == Nan
870 
871     map<ValueId, string>::iterator it;
872     for (it = sm.begin(); it != sm.end(); it++)
873         sm[it->first] = replace(it->second, typeToken, typeName);
874 }
875 
876 typedef de::SharedPtr<TypeSnippetsBase> TypeSnippetsSP;
877 
878 template <typename FLOAT_TYPE>
879 class TypeSnippets : public TypeSnippetsBase
880 {
881 public:
882     TypeSnippets();
883 };
884 
885 template <>
TypeSnippets()886 TypeSnippets<deFloat16>::TypeSnippets()
887 {
888     bitWidth = "16";
889     epsilon  = "6.104e-5"; // 2^-14 = 0x0400
890 
891     // NOTE: constants in SPIR-V cant be specified as exact fp16 - there is conversion from double to fp16
892     capabilities = "OpCapability StorageUniform16\n";
893     extensions   = "OpExtension \"SPV_KHR_16bit_storage\"\n";
894 
895     capabilitiesFp16Without16BitStorage = "OpCapability Float16\n";
896     extensionsFp16Without16BitStorage   = "";
897 
898     arrayStride = "2";
899 
900     varyingsTypesSnippet     = "%type_u32_iptr        = OpTypePointer Input %type_u32\n"
901                                "%type_u32_optr        = OpTypePointer Output %type_u32\n";
902     inputVaryingsSnippet     = "%BP_vertex_result    = OpVariable %type_u32_iptr Input\n";
903     outputVaryingsSnippet    = "%BP_vertex_result    = OpVariable %type_u32_optr Output\n";
904     storeVertexResultSnippet = "%tmp_vec2            = OpCompositeConstruct %type_f16_vec2 %result %c_f16_0\n"
905                                "%packed_result       = OpBitcast %type_u32 %tmp_vec2\n"
906                                "OpStore %BP_vertex_result %packed_result\n";
907     loadVertexResultSnippet  = "%packed_result       = OpLoad %type_u32 %BP_vertex_result\n"
908                                "%tmp_vec2            = OpBitcast %type_f16_vec2 %packed_result\n"
909                                "%result              = OpCompositeExtract %type_f16 %tmp_vec2 0\n";
910 
911     loadStoreRequiresShaderFloat16 = true;
912 
913     updateSpirvSnippets();
914 }
915 
916 template <>
TypeSnippets()917 TypeSnippets<float>::TypeSnippets()
918 {
919     bitWidth                            = "32";
920     epsilon                             = "1.175494351e-38";
921     capabilities                        = "";
922     extensions                          = "";
923     capabilitiesFp16Without16BitStorage = "";
924     extensionsFp16Without16BitStorage   = "";
925     arrayStride                         = "4";
926 
927     varyingsTypesSnippet     = "%type_u32_iptr        = OpTypePointer Input %type_u32\n"
928                                "%type_u32_optr        = OpTypePointer Output %type_u32\n";
929     inputVaryingsSnippet     = "%BP_vertex_result    = OpVariable %type_u32_iptr Input\n";
930     outputVaryingsSnippet    = "%BP_vertex_result    = OpVariable %type_u32_optr Output\n";
931     storeVertexResultSnippet = "%packed_result       = OpBitcast %type_u32 %result\n"
932                                "OpStore %BP_vertex_result %packed_result\n";
933     loadVertexResultSnippet  = "%packed_result       = OpLoad %type_u32 %BP_vertex_result\n"
934                                "%result              = OpBitcast %type_f32 %packed_result\n";
935 
936     loadStoreRequiresShaderFloat16 = false;
937 
938     updateSpirvSnippets();
939 }
940 
941 template <>
TypeSnippets()942 TypeSnippets<double>::TypeSnippets()
943 {
944     bitWidth                            = "64";
945     epsilon                             = "2.2250738585072014e-308"; // 0x0010000000000000
946     capabilities                        = "OpCapability Float64\n";
947     extensions                          = "";
948     capabilitiesFp16Without16BitStorage = "";
949     extensionsFp16Without16BitStorage   = "";
950     arrayStride                         = "8";
951 
952     varyingsTypesSnippet     = "%type_u32_vec2_iptr   = OpTypePointer Input %type_u32_vec2\n"
953                                "%type_u32_vec2_optr   = OpTypePointer Output %type_u32_vec2\n";
954     inputVaryingsSnippet     = "%BP_vertex_result     = OpVariable %type_u32_vec2_iptr Input\n";
955     outputVaryingsSnippet    = "%BP_vertex_result     = OpVariable %type_u32_vec2_optr Output\n";
956     storeVertexResultSnippet = "%packed_result        = OpBitcast %type_u32_vec2 %result\n"
957                                "OpStore %BP_vertex_result %packed_result\n";
958     loadVertexResultSnippet  = "%packed_result        = OpLoad %type_u32_vec2 %BP_vertex_result\n"
959                                "%result               = OpBitcast %type_f64 %packed_result\n";
960 
961     loadStoreRequiresShaderFloat16 = false;
962 
963     updateSpirvSnippets();
964 }
965 
966 // Operation structure holds data needed to test specified SPIR-V operation. This class contains
967 // additional annotations, additional types and aditional constants that should be properly included
968 // in SPIR-V code. Commands attribute in this structure contains code that performs tested operation
969 // on given arguments, in some cases verification is also performed there.
970 // All snipets stroed in this structure are generic and can be specialized for fp16, fp32 or fp64,
971 // thanks to that this data can be shared by many OperationTestCase instances (testing diferent
972 // float behaviors on diferent float widths).
973 struct Operation
974 {
975     // operation name is included in test case name
976     const char *name;
977 
978     // How extensively is the floating point type used?
979     FloatUsage floatUsage;
980 
981     // operation specific spir-v snippets that will be
982     // placed in proper places in final test shader
983     const char *annotations;
984     const char *types;
985     const char *constants;
986     const char *variables;
987     const char *functions;
988     const char *commands;
989     vector<string> IDsToDecorate;
990 
991     // conversion operations operate on one float type and produce float
992     // type with different bit width; restrictedInputType is used only when
993     // isInputTypeRestricted is set to true and it restricts usage of this
994     // operation to specified input type
995     bool isInputTypeRestricted;
996     FloatType restrictedInputType;
997 
998     // arguments for OpSpecConstant need to be specified also as constant
999     bool isSpecConstant;
1000 
1001     // set if c_float* constant is used in operation
1002     FloatStatementUsageFlags statementUsageFlags;
1003 
Operationvkt::SpirVAssembly::__anon898b99550111::Operation1004     Operation()
1005     {
1006     }
1007 
1008     // Minimal constructor - used by most of operations
Operationvkt::SpirVAssembly::__anon898b99550111::Operation1009     Operation(const char *_name, FloatUsage _floatUsage, const char *_commands,
1010               const FloatStatementUsageFlags _statementUsageFlags = 0, vector<string> _IDsToDecorate = {"result"})
1011         : name(_name)
1012         , floatUsage(_floatUsage)
1013         , annotations("")
1014         , types("")
1015         , constants("")
1016         , variables("")
1017         , functions("")
1018         , commands(_commands)
1019         , IDsToDecorate(_IDsToDecorate)
1020         , isInputTypeRestricted(false)
1021         , restrictedInputType(FP16) // not used as isInputTypeRestricted is false
1022         , isSpecConstant(false)
1023         , statementUsageFlags(_statementUsageFlags)
1024     {
1025     }
1026 
1027     // Conversion operations constructor (used also by conversions done in SpecConstantOp)
Operationvkt::SpirVAssembly::__anon898b99550111::Operation1028     Operation(const char *_name, FloatUsage _floatUsage, bool specConstant, FloatType _inputType,
1029               const char *_constants, const char *_commands, const FloatStatementUsageFlags _statementUsageFlags = 0,
1030               vector<string> _IDsToDecorate = {"result"})
1031         : name(_name)
1032         , floatUsage(_floatUsage)
1033         , annotations("")
1034         , types("")
1035         , constants(_constants)
1036         , variables("")
1037         , functions("")
1038         , commands(_commands)
1039         , IDsToDecorate(_IDsToDecorate)
1040         , isInputTypeRestricted(true)
1041         , restrictedInputType(_inputType)
1042         , isSpecConstant(specConstant)
1043         , statementUsageFlags(_statementUsageFlags)
1044     {
1045     }
1046 
1047     // Full constructor - used by few operations, that are more complex to test
Operationvkt::SpirVAssembly::__anon898b99550111::Operation1048     Operation(const char *_name, FloatUsage _floatUsage, const char *_annotations, const char *_types,
1049               const char *_constants, const char *_variables, const char *_functions, const char *_commands,
1050               const FloatStatementUsageFlags _statementUsageFlags = 0, vector<string> _IDsToDecorate = {"result"})
1051         : name(_name)
1052         , floatUsage(_floatUsage)
1053         , annotations(_annotations)
1054         , types(_types)
1055         , constants(_constants)
1056         , variables(_variables)
1057         , functions(_functions)
1058         , commands(_commands)
1059         , IDsToDecorate(_IDsToDecorate)
1060         , isInputTypeRestricted(false)
1061         , restrictedInputType(FP16) // not used as isInputTypeRestricted is false
1062         , isSpecConstant(false)
1063         , statementUsageFlags(_statementUsageFlags)
1064     {
1065     }
1066 
1067     // Full constructor - used by rounding override cases
Operationvkt::SpirVAssembly::__anon898b99550111::Operation1068     Operation(const char *_name, FloatUsage _floatUsage, FloatType _inputType, const char *_annotations,
1069               const char *_types, const char *_constants, const char *_commands,
1070               const FloatStatementUsageFlags _statementUsageFlags = 0, vector<string> _IDsToDecorate = {"result"})
1071         : name(_name)
1072         , floatUsage(_floatUsage)
1073         , annotations(_annotations)
1074         , types(_types)
1075         , constants(_constants)
1076         , variables("")
1077         , functions("")
1078         , commands(_commands)
1079         , IDsToDecorate(_IDsToDecorate)
1080         , isInputTypeRestricted(true)
1081         , restrictedInputType(_inputType)
1082         , isSpecConstant(false)
1083         , statementUsageFlags(_statementUsageFlags)
1084     {
1085     }
1086 };
1087 
1088 // Class storing input that will be passed to operation and expected
1089 // output that should be generated for specified behavior.
1090 class OperationTestCase
1091 {
1092 public:
OperationTestCase()1093     OperationTestCase()
1094     {
1095     }
1096 
OperationTestCase(const char * _baseName,FP _behaviorFlags,bool _useDecorationFlags,OperationId _operatinId,ValueId _input1,ValueId _input2,ValueId _expectedOutput,bool _fp16Without16BitStorage=false,bool _requireRte=false)1097     OperationTestCase(const char *_baseName, FP _behaviorFlags, bool _useDecorationFlags, OperationId _operatinId,
1098                       ValueId _input1, ValueId _input2, ValueId _expectedOutput, bool _fp16Without16BitStorage = false,
1099                       bool _requireRte = false)
1100         : baseName(_baseName)
1101         , useDecorationFlags(_useDecorationFlags)
1102         , operationId(_operatinId)
1103         , expectedOutput(_expectedOutput)
1104         , fp16Without16BitStorage(_fp16Without16BitStorage)
1105         , requireRte(_requireRte)
1106     {
1107         if (useDecorationFlags)
1108         {
1109             behaviorFlagsExecMode   = allBits;
1110             behaviorFlagsDecoration = _behaviorFlags;
1111         }
1112         else
1113         {
1114             behaviorFlagsExecMode   = _behaviorFlags;
1115             behaviorFlagsDecoration = FP::MaskNone;
1116         }
1117         input[0] = _input1;
1118         input[1] = _input2;
1119     }
1120 
1121 public:
1122     string baseName;
1123     FP behaviorFlagsExecMode;
1124     FP behaviorFlagsDecoration;
1125     bool useDecorationFlags;
1126     OperationId operationId;
1127     ValueId input[2];
1128     ValueId expectedOutput;
1129     bool fp16Without16BitStorage;
1130     bool requireRte;
1131 };
1132 
1133 struct OperationTestCaseInputs
1134 {
1135     OperationId operationId;
1136     ValueId operandFirst;
1137     ValueId operandSecond;
1138     ValueId result;
1139     FP testedFlagBits;
1140     bool requireRte = false;
1141 };
1142 
1143 // op1 is SPECIAL VALUE (SZ/INF/NAN)
1144 // op2 is 1
1145 // tested flagbits are NSZ,NotInf,NotNaN
1146 struct standardOperationTestCase
1147 {
1148     OperationId operationId;
1149     ValueId resultSZ;
1150     ValueId resultInf;
1151     ValueId resultNaN;
1152 };
1153 
1154 // Helper structure used to store specialized operation
1155 // data. This data is ready to be used during shader assembly.
1156 struct SpecializedOperation
1157 {
1158     string constants;
1159     string annotations;
1160     string types;
1161     string arguments;
1162     string variables;
1163     string functions;
1164     string commands;
1165 
1166     FloatType inFloatType;
1167     TypeSnippetsSP inTypeSnippets;
1168     TypeSnippetsSP outTypeSnippets;
1169     FloatStatementUsageFlags argumentsUsesFloatConstant;
1170 };
1171 
1172 class TypeTestResultsBase
1173 {
1174 public:
TypeTestResultsBase()1175     TypeTestResultsBase()
1176     {
1177         // tests common for all fp types
1178 
1179         // this array contains only special cases not conforming to standardOperationTestCase
1180         const OperationTestCaseInputs testCaseInputsArr[] = {
1181             {OID_NEGATE, V_ZERO, V_UNUSED, V_MINUS_ZERO, FP::NSZ},
1182             {OID_NEGATE, V_MINUS_INF, V_UNUSED, V_INF, FP::NotInf},
1183 
1184             {OID_ADD, V_MINUS_ZERO, V_MINUS_ZERO, V_MINUS_ZERO, FP::NSZ},
1185             {OID_ADD, V_ZERO, V_MINUS_ZERO, V_ZERO, FP::NSZ},
1186             {OID_ADD, V_MINUS_ONE, V_ONE, V_ZERO, FP::NSZ},
1187             {OID_ADD, V_HUGE, V_HUGE, V_INF, FP::NotInf, true},
1188             {OID_ADD, V_ZERO, V_MINUS_INF, V_MINUS_INF, FP::NotInf},
1189             {OID_ADD, V_ZERO, V_NAN, V_NAN, FP::NotNaN},
1190             {OID_ADD, V_INF, V_MINUS_INF, V_NAN, FP::NotNaN | FP::NotInf},
1191 
1192             {OID_SUB, V_MINUS_ZERO, V_ZERO, V_MINUS_ZERO, FP::NSZ},
1193             {OID_SUB, V_MINUS_ZERO, V_MINUS_ZERO, V_ZERO, FP::NSZ},
1194             {OID_SUB, V_ZERO, V_MINUS_INF, V_INF, FP::NotInf},
1195             {OID_SUB, V_ZERO, V_NAN, V_NAN, FP::NotNaN},
1196             {OID_SUB, V_INF, V_INF, V_NAN, FP::NotNaN | FP::NotInf},
1197 
1198             {OID_MUL, V_MINUS_ONE, V_ZERO, V_MINUS_ZERO, FP::NSZ},
1199             {OID_MUL, V_ZERO, V_MINUS_ZERO, V_MINUS_ZERO, FP::NSZ},
1200             {OID_MUL, V_TINY, V_MINUS_TINY, V_MINUS_ZERO, FP::NSZ},
1201             {OID_MUL, V_HUGE, V_HUGE, V_INF, FP::NotInf, true},
1202             {OID_MUL, V_ZERO, V_INF, V_NAN, FP::NotInf | FP::NotNaN},
1203             {OID_MUL, V_ZERO, V_NAN, V_NAN, FP::NotNaN},
1204 
1205             {OID_DIV, V_ONE, V_MINUS_INF, V_MINUS_ZERO, FP::NSZ | FP::NotInf},
1206             {OID_DIV, V_ZERO, V_INF, V_ZERO, FP::NotInf},
1207             {OID_DIV, V_INF, V_MINUS_ZERO, V_MINUS_INF, FP::NSZ | FP::NotInf},
1208             {OID_DIV, V_ZERO, V_NAN, V_NAN, FP::NotNaN},
1209             {OID_DIV, V_INF, V_INF, V_NAN, FP::NotInf | FP::NotNaN},
1210 
1211             {OID_DOT, V_MINUS_ZERO, V_MINUS_ZERO, V_ZERO, FP::NSZ},
1212 
1213             {OID_ABS, V_MINUS_INF, V_UNUSED, V_INF, FP::NotInf},
1214 
1215             {OID_SIGN, V_MINUS_INF, V_UNUSED, V_MINUS_ONE, FP::NotInf},
1216 
1217             {OID_FRACT, V_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1218             {OID_FRACT, V_MINUS_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1219 
1220             {OID_SQRT, V_MINUS_ONE, V_UNUSED, V_NAN, FP::NotNaN},
1221             {OID_SQRT, V_MINUS_INF, V_UNUSED, V_NAN, FP::NotNaN},
1222 
1223             {OID_INV_SQRT, V_ZERO, V_UNUSED, V_INF, FP::NotInf},
1224             {OID_INV_SQRT, V_MINUS_ZERO, V_UNUSED, V_MINUS_INF, FP::NSZ | FP::NotInf},
1225             {OID_INV_SQRT, V_MINUS_ONE, V_UNUSED, V_NAN, FP::NotNaN},
1226             {OID_INV_SQRT, V_MINUS_INF, V_UNUSED, V_NAN, FP::NotNaN},
1227 
1228             {OID_MODF_ST_WH, V_MINUS_INF, V_UNUSED, V_MINUS_INF, FP::NotInf},
1229             {OID_MODF_ST_FR, V_MINUS_INF, V_UNUSED, V_MINUS_ZERO, FP::NSZ | FP::NotInf},
1230 
1231             {OID_LENGTH, V_MINUS_INF, V_UNUSED, V_INF, FP::NotInf},
1232 
1233             {OID_NORMALIZE, V_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1234 
1235             {OID_REFLECT, V_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1236 
1237             {OID_REFRACT, V_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1238 
1239             {OID_MAT_INV, V_ZERO, V_UNUSED, V_MINUS_ZERO, FP::NSZ},
1240 
1241             {OID_MIX, V_NAN, V_ONE, V_NAN, FP::NotNaN},
1242             {OID_MIX, V_ONE, V_NAN, V_NAN, FP::NotNaN},
1243 
1244             {OID_FMA2PT58, V_ZERO_POINT_ONE, V_TWENTY_FIVE_POINT_EIGHT, V_ZERO,
1245              FP::AllowContract},                                                     // 0.1 * 25.8 - 2.58 == 0.0
1246             {OID_SZ_FMA, V_MINUS_ZERO, V_ZERO, V_ZERO, FP::AllowContract | FP::NSZ}, // -0.0 * 1 +  0.0 ==  0.0
1247             {OID_SZ_FMA, V_MINUS_ZERO, V_MINUS_ZERO, V_MINUS_ZERO,
1248              FP::AllowContract | FP::NSZ}, // -0.0 * 1 + -0.0 == -0.0
1249 
1250             {OID_MIN, V_MINUS_ZERO, V_ZERO, V_MINUS_ZERO, FP::NSZ},
1251             {OID_MIN, V_MINUS_INF, V_ONE, V_MINUS_INF, FP::NotInf},
1252 
1253             {OID_MAX, V_MINUS_ZERO, V_ZERO, V_ZERO, FP::NSZ},
1254             {OID_MAX, V_MINUS_INF, V_ONE, V_ONE, FP::NotInf},
1255 
1256             {OID_CLAMP, V_MINUS_ONE, V_MINUS_ZERO, V_MINUS_ZERO, FP::NSZ},
1257             {OID_CLAMP, V_MINUS_ZERO, V_ZERO, V_ZERO, FP::NSZ},
1258             {OID_CLAMP, V_ZERO, V_MINUS_ZERO, V_MINUS_ZERO, FP::NSZ},
1259             {OID_CLAMP, V_INF, V_ONE, V_ONE, FP::NotInf},
1260             {OID_CLAMP, V_ONE, V_INF, V_INF, FP::NotInf},
1261             {OID_CLAMP, V_ONE, V_MINUS_INF, V_MINUS_INF, FP::NotInf},
1262             {OID_CLAMP, V_NAN, V_ONE, V_ONE_OR_NAN, FP::NotNaN},
1263             {OID_CLAMP, V_ONE, V_NAN, V_ONE_OR_NAN, FP::NotNaN},
1264 
1265             {OID_CROSS, V_MINUS_ZERO, V_MINUS_ZERO, V_ZERO, FP::NSZ},
1266             {OID_CROSS, V_INF, V_ONE, V_UNUSED, FP::NotInf},
1267             {OID_CROSS, V_NAN, V_ONE, V_NAN, FP::NotNaN},
1268 
1269             {OID_NMIN, V_MINUS_ZERO, V_ZERO, V_MINUS_ZERO, FP::NSZ},
1270             {OID_NMIN, V_MINUS_INF, V_ONE, V_MINUS_INF, FP::NotInf},
1271 
1272             {OID_NMAX, V_MINUS_ZERO, V_ZERO, V_ZERO, FP::NSZ},
1273             {OID_NMAX, V_MINUS_INF, V_ONE, V_ONE, FP::NotInf},
1274 
1275             {OID_NCLAMP, V_MINUS_ONE, V_MINUS_ZERO, V_MINUS_ZERO, FP::NSZ},
1276             {OID_NCLAMP, V_MINUS_ZERO, V_ZERO, V_ZERO, FP::NSZ},
1277             {OID_NCLAMP, V_ZERO, V_MINUS_ZERO, V_MINUS_ZERO, FP::NSZ},
1278             {OID_NCLAMP, V_INF, V_ONE, V_ONE, FP::NotInf},
1279             {OID_NCLAMP, V_ONE, V_INF, V_INF, FP::NotInf},
1280             {OID_NCLAMP, V_ONE, V_MINUS_INF, V_MINUS_INF, FP::NotInf},
1281             {OID_NCLAMP, V_NAN, V_ONE, V_ONE, FP::NotNaN},
1282             {OID_NCLAMP, V_ONE, V_NAN, V_ONE, FP::NotNaN},
1283 
1284             // a + b + (-a)
1285             {OID_ADD_SUB_REASSOCIABLE, V_MAX, V_HUGE, V_INF, FP::AllowReassoc, true},
1286             // a + a + (-a)
1287             {OID_ADD_SUB_REASSOCIABLE, V_MAX, V_MAX, V_INF, FP::AllowReassoc | FP::NotInf, true},
1288         };
1289 
1290         testCaseInputs.insert(testCaseInputs.begin(), testCaseInputsArr,
1291                               testCaseInputsArr + DE_LENGTH_OF_ARRAY(testCaseInputsArr));
1292 
1293         const standardOperationTestCase stcArr[] = {
1294             {OID_NEGATE, V_ZERO, V_MINUS_INF, V_NAN},
1295             {OID_COMPOSITE, V_MINUS_ZERO, V_INF, V_NAN},
1296             {OID_COMPOSITE_INS, V_MINUS_ZERO, V_INF, V_NAN},
1297             {OID_COPY, V_MINUS_ZERO, V_INF, V_NAN},
1298             {OID_D_EXTRACT, V_MINUS_ZERO, V_INF, V_NAN},
1299             {OID_D_INSERT, V_MINUS_ZERO, V_INF, V_NAN},
1300             {OID_SHUFFLE, V_MINUS_ZERO, V_INF, V_NAN},
1301             {OID_TRANSPOSE, V_MINUS_ZERO, V_INF, V_NAN},
1302             {OID_RETURN_VAL, V_MINUS_ZERO, V_INF, V_NAN},
1303 
1304             {OID_ADD, V_ONE, V_INF, V_NAN},
1305             {OID_SUB, V_MINUS_ONE, V_INF, V_NAN},
1306             {OID_MUL, V_MINUS_ZERO, V_INF, V_NAN},
1307             {OID_DIV, V_MINUS_ZERO, V_INF, V_NAN},
1308             {OID_REM, V_UNUSED, V_UNUSED, V_NAN},
1309             {OID_MOD, V_UNUSED, V_UNUSED, V_NAN},
1310             {OID_PHI, V_MINUS_ZERO, V_ONE, V_NAN},
1311             {OID_SELECT, V_MINUS_ZERO, V_INF, V_NAN},
1312             {OID_DOT, V_MINUS_ZERO, V_INF, V_NAN},
1313             {OID_VEC_MUL_S, V_MINUS_ZERO, V_INF, V_NAN},
1314             {OID_VEC_MUL_M, V_MINUS_ZERO, V_INF, V_NAN},
1315             {OID_MAT_MUL_S, V_MINUS_ZERO, V_INF, V_NAN},
1316             {OID_MAT_MUL_V, V_MINUS_ZERO, V_INF, V_NAN},
1317             {OID_MAT_MUL_M, V_MINUS_ZERO, V_INF, V_NAN},
1318             {OID_OUT_PROD, V_MINUS_ZERO, V_INF, V_NAN},
1319             {OID_ORD_EQ, V_ZERO, V_ZERO, V_ZERO},
1320             {OID_UORD_EQ, V_ZERO, V_ZERO, V_ONE},
1321             {OID_ORD_NEQ, V_ONE, V_ONE, V_ZERO},
1322             {OID_UORD_NEQ, V_ONE, V_ONE, V_ONE},
1323             {OID_ORD_LS, V_ONE, V_ZERO, V_ZERO},
1324             {OID_UORD_LS, V_ONE, V_ZERO, V_ONE},
1325             {OID_ORD_GT, V_ZERO, V_ONE, V_ZERO},
1326             {OID_UORD_GT, V_ZERO, V_ONE, V_ONE},
1327             {OID_ORD_LE, V_ONE, V_ZERO, V_ZERO},
1328             {OID_UORD_LE, V_ONE, V_ZERO, V_ONE},
1329             {OID_ORD_GE, V_ZERO, V_ONE, V_ZERO},
1330             {OID_UORD_GE, V_ZERO, V_ONE, V_ONE},
1331             {OID_ROUND, V_MINUS_ZERO, V_INF, V_NAN},
1332             {OID_ROUND_EV, V_MINUS_ZERO, V_INF, V_NAN},
1333             {OID_TRUNC, V_MINUS_ZERO, V_INF, V_NAN},
1334             {OID_ABS, V_ZERO, V_INF, V_NAN},
1335             {OID_SIGN, V_ZERO_OR_MINUS_ZERO, V_ONE, V_SIGN_NAN},
1336             {OID_FLOOR, V_MINUS_ZERO, V_INF, V_NAN},
1337             {OID_CEIL, V_MINUS_ZERO, V_INF, V_NAN},
1338             {OID_FRACT, V_ZERO, V_UNUSED, V_NAN}, // fract(Inf) == NaN, so needs non-standard flags.
1339             {OID_SQRT, V_MINUS_ZERO, V_INF, V_NAN},
1340             {OID_INV_SQRT, V_UNUSED, V_ZERO, V_NAN}, // -0 needs NotInf, so handled as special case.
1341             {OID_MODF, V_MINUS_ZERO, V_UNUSED, V_NAN},
1342             {OID_MODF_ST_WH, V_MINUS_ZERO, V_INF, V_NAN},
1343             {OID_MODF_ST_FR, V_MINUS_ZERO, V_ZERO, V_NAN},
1344             {OID_LDEXP, V_MINUS_ZERO, V_INF, V_NAN},
1345             {OID_FREXP, V_MINUS_ZERO, V_UNUSED, V_UNUSED},
1346             {OID_FREXP_ST, V_MINUS_ZERO, V_UNUSED, V_UNUSED},
1347             {OID_LENGTH, V_ZERO, V_INF, V_NAN},
1348             {OID_NORMALIZE, V_MINUS_ZERO, V_UNUSED, V_NAN},
1349             {OID_REFLECT, V_MINUS_ZERO, V_UNUSED, V_NAN},
1350             {OID_REFRACT, V_MINUS_ZERO, V_UNUSED, V_NAN},
1351             {OID_MAT_DET, V_ZERO, V_UNUSED, V_NAN},
1352             {OID_MAT_INV, V_ZERO, V_UNUSED, V_NAN},
1353             {OID_FMA, V_MINUS_ONE, V_INF, V_NAN},
1354             {OID_MIN, V_MINUS_ZERO, V_ONE, V_ONE_OR_NAN},
1355             {OID_MAX, V_ONE, V_INF, V_ONE_OR_NAN},
1356             {OID_STEP, V_ONE, V_ZERO, V_UNUSED},
1357             {OID_SSTEP, V_HALF, V_UNUSED, V_UNUSED},
1358             {OID_DIST, V_ONE, V_INF, V_NAN},
1359             {OID_FACE_FWD, V_MINUS_ONE, V_MINUS_ONE, V_UNUSED},
1360             {OID_NMIN, V_MINUS_ZERO, V_ONE, V_ONE},
1361             {OID_NMAX, V_ONE, V_INF, V_ONE},
1362         };
1363 
1364         appendStandardCases(stcArr, DE_LENGTH_OF_ARRAY(stcArr));
1365     }
1366 
appendStandardCases(const standardOperationTestCase * cases,size_t count)1367     void appendStandardCases(const standardOperationTestCase *cases, size_t count)
1368     {
1369         for (size_t i = 0; i < count; i++)
1370         {
1371             testCaseInputs.push_back({cases[i].operationId, V_MINUS_ZERO, V_ONE, cases[i].resultSZ, FP::NSZ});
1372             testCaseInputs.push_back({cases[i].operationId, V_INF, V_ONE, cases[i].resultInf, FP::NotInf});
1373             testCaseInputs.push_back({cases[i].operationId, V_NAN, V_ONE, cases[i].resultNaN, FP::NotNaN});
1374         }
1375     }
1376 
~TypeTestResultsBase()1377     virtual ~TypeTestResultsBase()
1378     {
1379     }
1380 
1381     FloatType floatType() const;
1382 
1383 protected:
1384     FloatType m_floatType;
1385 
1386 public:
1387     vector<OperationTestCaseInputs> testCaseInputs;
1388 };
1389 
floatType() const1390 FloatType TypeTestResultsBase::floatType() const
1391 {
1392     return m_floatType;
1393 }
1394 
1395 typedef de::SharedPtr<TypeTestResultsBase> TypeTestResultsSP;
1396 
1397 template <typename FLOAT_TYPE>
1398 class TypeTestResults : public TypeTestResultsBase
1399 {
1400 public:
1401     TypeTestResults();
1402 };
1403 
1404 const OperationTestCaseInputs nonStc16and32Only[] = {
1405     {OID_SIN, V_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1406     {OID_SIN, V_MINUS_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1407     {OID_COS, V_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1408     {OID_COS, V_MINUS_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1409     {OID_TAN, V_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1410     {OID_TAN, V_MINUS_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1411 
1412     {OID_ASIN, V_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1413     {OID_ASIN, V_TWO, V_UNUSED, V_NAN, FP::NotNaN},
1414     {OID_ASIN, V_MINUS_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1415     {OID_ACOS, V_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1416     {OID_ACOS, V_TWO, V_UNUSED, V_NAN, FP::NotNaN},
1417     {OID_ACOS, V_MINUS_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1418 
1419     {OID_ATAN, V_MINUS_INF, V_UNUSED, V_MINUS_PI_DIV_2, FP::NotInf},
1420 
1421     {OID_SINH, V_MINUS_INF, V_UNUSED, V_MINUS_INF, FP::NotInf},
1422     {OID_COSH, V_MINUS_INF, V_UNUSED, V_INF, FP::NotInf},
1423     {OID_TANH, V_MINUS_INF, V_UNUSED, V_MINUS_ONE, FP::NotInf},
1424 
1425     {OID_ASINH, V_MINUS_INF, V_UNUSED, V_MINUS_INF, FP::NotInf},
1426 
1427     {OID_ACOSH, V_ZERO, V_UNUSED, V_NAN, FP::NotNaN},
1428     {OID_ACOSH, V_MINUS_ZERO, V_UNUSED, V_NAN, FP::NSZ | FP::NotNaN},
1429     {OID_ACOSH, V_HALF, V_UNUSED, V_NAN, FP::NotNaN},
1430     {OID_ACOSH, V_INF, V_UNUSED, V_INF, FP::NotInf},
1431     {OID_ACOSH, V_MINUS_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1432 
1433     {OID_ATANH, V_TWO, V_UNUSED, V_NAN, FP::NotNaN},
1434     {OID_ATANH, V_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1435     {OID_ATANH, V_MINUS_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1436 
1437     {OID_EXP, V_MINUS_INF, V_UNUSED, V_ZERO, FP::NotInf},
1438 
1439     {OID_LOG, V_ZERO, V_UNUSED, V_MINUS_INF, FP::NSZ | FP::NotInf},
1440     {OID_LOG, V_MINUS_ZERO, V_UNUSED, V_MINUS_INF, FP::NSZ | FP::NotInf},
1441     {OID_LOG, V_MINUS_ONE, V_UNUSED, V_NAN, FP::NotNaN},
1442     {OID_LOG, V_MINUS_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1443 
1444     {OID_EXP2, V_MINUS_INF, V_UNUSED, V_ZERO, FP::NotInf},
1445 
1446     {OID_LOG2, V_ZERO, V_UNUSED, V_MINUS_INF, FP::NSZ | FP::NotInf},
1447     {OID_LOG2, V_MINUS_ZERO, V_UNUSED, V_MINUS_INF, FP::NSZ | FP::NotInf},
1448     {OID_LOG2, V_MINUS_ONE, V_UNUSED, V_NAN, FP::NotNaN},
1449     {OID_LOG2, V_MINUS_INF, V_UNUSED, V_NAN, FP::NotInf | FP::NotNaN},
1450 
1451     {OID_ATAN2, V_ZERO, V_MINUS_ONE, V_PI, FP::NSZ},
1452     {OID_ATAN2, V_MINUS_ZERO, V_MINUS_ONE, V_MINUS_PI, FP::NSZ},
1453     // SPIR-V explicitly says that atan(0, 0) is undefined, so these next 2 tests would not be valid.
1454     // The expected behaviour given is the one from POSIX, OpenCL and IEEE-754.
1455     //{ OID_ATAN2, V_ZERO, V_MINUS_ZERO, V_PI, FP::NSZ },
1456     //{ OID_ATAN2, V_MINUS_ZERO, V_MINUS_ZERO, V_MINUS_PI, FP::NSZ },
1457     {OID_ATAN2, V_ZERO, V_MINUS_INF, V_PI, FP::NSZ | FP::NotInf},
1458     {OID_ATAN2, V_MINUS_ZERO, V_MINUS_INF, V_MINUS_PI, FP::NSZ | FP::NotInf},
1459     {OID_ATAN2, V_ONE, V_MINUS_INF, V_PI, FP::NSZ | FP::NotInf},
1460     {OID_ATAN2, V_MINUS_ONE, V_MINUS_INF, V_MINUS_PI, FP::NSZ | FP::NotInf},
1461     {OID_ATAN2, V_ONE, V_INF, V_ZERO_OR_MINUS_ZERO, FP::NotInf},
1462     {OID_ATAN2, V_MINUS_ONE, V_INF, V_ZERO_OR_MINUS_ZERO, FP::NotInf},
1463     {OID_ATAN2, V_INF, V_ONE, V_PI_DIV_2, FP::NotInf},
1464     {OID_ATAN2, V_MINUS_INF, V_ONE, V_MINUS_PI_DIV_2, FP::NotInf},
1465     {OID_ATAN2, V_INF, V_MINUS_INF, V_3_PI_DIV_4, FP::NotInf},
1466     {OID_ATAN2, V_MINUS_INF, V_MINUS_INF, V_MINUS_3_PI_DIV_4, FP::NotInf},
1467     {OID_ATAN2, V_INF, V_INF, V_PI_DIV_4, FP::NotInf},
1468     {OID_ATAN2, V_MINUS_INF, V_INF, V_MINUS_PI_DIV_4, FP::NotInf},
1469     {OID_ATAN2, V_NAN, V_ONE, V_NAN, FP::NotNaN},
1470     {OID_ATAN2, V_ONE, V_NAN, V_NAN, FP::NotNaN},
1471 };
1472 
1473 // Most of these operations are not accurate enough at 0 to resolve the difference between
1474 // +0 and -0 so the test is skipped. sin, cos and tan are also explicitly low precision for
1475 // large inputs, so are not tested at infinity.
1476 const standardOperationTestCase stc16and32only[] = {
1477     {OID_RADIANS, V_MINUS_ZERO, V_INF, V_NAN},
1478     {OID_DEGREES, V_MINUS_ZERO, V_INF, V_NAN},
1479     {OID_SIN, V_UNUSED, V_UNUSED, V_NAN},
1480     {OID_COS, V_TRIG_ONE, V_UNUSED, V_NAN},
1481     {OID_TAN, V_UNUSED, V_UNUSED, V_NAN},
1482     {OID_ASIN, V_UNUSED, V_UNUSED, V_NAN},
1483     {OID_ACOS, V_PI_DIV_2, V_UNUSED, V_NAN},
1484     {OID_ATAN, V_UNUSED, V_PI_DIV_2, V_NAN},
1485     {OID_SINH, V_UNUSED, V_INF, V_NAN},
1486     {OID_COSH, V_ONE, V_INF, V_NAN},
1487     {OID_TANH, V_UNUSED, V_ONE, V_NAN},
1488     {OID_ASINH, V_UNUSED, V_INF, V_NAN},
1489     {OID_ACOSH, V_UNUSED, V_INF, V_NAN},
1490     {OID_ATANH, V_UNUSED, V_UNUSED, V_NAN},
1491     {OID_EXP, V_ONE, V_INF, V_NAN},
1492     {OID_LOG, V_UNUSED, V_INF, V_NAN},
1493     {OID_EXP2, V_ONE, V_INF, V_NAN},
1494     {OID_LOG2, V_UNUSED, V_INF, V_NAN},
1495     // OID_ATAN2 -- All handled as special cases
1496     {OID_POW, V_UNUSED, V_INF, V_NAN},
1497 };
1498 
1499 template <>
TypeTestResults()1500 TypeTestResults<deFloat16>::TypeTestResults() : TypeTestResultsBase()
1501 {
1502     m_floatType = FP16;
1503 
1504     const standardOperationTestCase stcConvTo16[] = {
1505         {OID_CONV_FROM_FP32, V_MINUS_ZERO, V_INF, V_NAN},
1506         {OID_CONV_FROM_FP64, V_MINUS_ZERO, V_INF, V_NAN},
1507     };
1508 
1509     testCaseInputs.insert(testCaseInputs.end(), nonStc16and32Only,
1510                           nonStc16and32Only + DE_LENGTH_OF_ARRAY(nonStc16and32Only));
1511 
1512     appendStandardCases(stcConvTo16, DE_LENGTH_OF_ARRAY(stcConvTo16));
1513     appendStandardCases(stc16and32only, DE_LENGTH_OF_ARRAY(stc16and32only));
1514 }
1515 
1516 template <>
TypeTestResults()1517 TypeTestResults<float>::TypeTestResults() : TypeTestResultsBase()
1518 {
1519     m_floatType = FP32;
1520 
1521     const standardOperationTestCase stcConvTo32[] = {
1522         {OID_CONV_FROM_FP16, V_MINUS_ZERO, V_INF, V_NAN},
1523         {OID_CONV_FROM_FP64, V_MINUS_ZERO, V_INF, V_NAN},
1524     };
1525 
1526     testCaseInputs.insert(testCaseInputs.end(), nonStc16and32Only,
1527                           nonStc16and32Only + DE_LENGTH_OF_ARRAY(nonStc16and32Only));
1528 
1529     appendStandardCases(stcConvTo32, DE_LENGTH_OF_ARRAY(stcConvTo32));
1530     appendStandardCases(stc16and32only, DE_LENGTH_OF_ARRAY(stc16and32only));
1531 }
1532 
1533 template <>
TypeTestResults()1534 TypeTestResults<double>::TypeTestResults() : TypeTestResultsBase()
1535 {
1536     m_floatType = FP64;
1537 
1538     const standardOperationTestCase stcConvTo64[] = {
1539         {OID_CONV_FROM_FP16, V_MINUS_ZERO, V_INF, V_NAN},
1540         {OID_CONV_FROM_FP32, V_MINUS_ZERO, V_INF, V_NAN},
1541     };
1542 
1543     appendStandardCases(stcConvTo64, DE_LENGTH_OF_ARRAY(stcConvTo64));
1544 }
1545 
1546 // Class responsible for constructing list of test cases for specified
1547 // float type and specified way of preparation of arguments.
1548 // Arguments can be either read from input SSBO or generated via math
1549 // operations in spir-v code.
1550 class TestCasesBuilder
1551 {
1552 public:
1553     void init();
1554     void build(vector<OperationTestCase> &testCases, TypeTestResultsSP typeTestResults);
1555     const Operation &getOperation(OperationId id) const;
1556 
1557 private:
1558     void createUnaryTestCases(vector<OperationTestCase> &testCases, OperationId operationId,
1559                               ValueId denormPreserveResult, ValueId denormFTZResult,
1560                               bool fp16WithoutStorage = false) const;
1561 
1562 private:
1563     // Operations are shared betwean test cases so they are
1564     // passed to them as pointers to data stored in TestCasesBuilder.
1565     typedef OperationTestCase OTC;
1566     typedef Operation Op;
1567     map<int, Op> m_operations;
1568 };
1569 
init()1570 void TestCasesBuilder::init()
1571 {
1572     map<int, Op> &mo = m_operations;
1573 
1574     // predefine operations repeatedly used in tests; note that "_float"
1575     // in every operation command will be replaced with either "_f16",
1576     // "_f32" or "_f64" - StringTemplate is not used here because it
1577     // would make code less readable
1578     // m_operations contains generic operation definitions that can be
1579     // used for all float types
1580 
1581     mo[OID_NEGATE]    = Op("negate", FLOAT_ARITHMETIC, "%result             = OpFNegate %type_float %arg1\n",
1582                            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1583     mo[OID_COMPOSITE] = Op("composite", FLOAT_ARITHMETIC,
1584                            "%vec1               = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
1585                            "%result             = OpCompositeExtract %type_float %vec1 0\n",
1586                            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"vec1", "result"});
1587     mo[OID_COMPOSITE_INS] =
1588         Op("comp_ins", FLOAT_ARITHMETIC,
1589            "%vec1               = OpCompositeConstruct %type_float_vec2 %c_float_0 %c_float_0\n"
1590            "%vec2               = OpCompositeInsert %type_float_vec2 %arg1 %vec1 0\n"
1591            "%result             = OpCompositeExtract %type_float %vec2 0\n",
1592            B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"vec2", "result"});
1593     mo[OID_COPY]      = Op("copy", FLOAT_STORAGE_ONLY, "%result             = OpCopyObject %type_float %arg1\n",
1594                            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1595     mo[OID_D_EXTRACT] = Op("extract", FLOAT_ARITHMETIC,
1596                            "%vec1               = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
1597                            "%result             = OpVectorExtractDynamic %type_float %vec1 %c_i32_0\n",
1598                            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"vec1", "result"});
1599     mo[OID_D_INSERT]  = Op("insert", FLOAT_ARITHMETIC,
1600                            "%tmpVec             = OpCompositeConstruct %type_float_vec2 %c_float_2 %c_float_2\n"
1601                             "%vec1               = OpVectorInsertDynamic %type_float_vec2 %tmpVec %arg1 %c_i32_0\n"
1602                             "%result             = OpCompositeExtract %type_float %vec1 0\n",
1603                            B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT,
1604                            {"tmpVec", "vec1", "result"});
1605     mo[OID_SHUFFLE]   = Op(
1606         "shuffle", FLOAT_ARITHMETIC,
1607         "%tmpVec1            = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
1608           "%tmpVec2            = OpCompositeConstruct %type_float_vec2 %c_float_2 %c_float_2\n" // NOTE: its impossible to test shuffle with denorms flushed
1609         "%vec1               = OpVectorShuffle %type_float_vec2 %tmpVec1 %tmpVec2 0 2\n" //       to zero as this will be done by earlier operation
1610         "%result             = OpCompositeExtract %type_float %vec1 0\n", //       (this also applies to few other operations)
1611         B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"tmpVec1", "vec1", "result"});
1612     mo[OID_TRANSPOSE] = Op("transpose", FLOAT_ARITHMETIC,
1613                            "%col                = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
1614                            "%mat                = OpCompositeConstruct %type_float_mat2x2 %col %col\n"
1615                            "%tmat               = OpTranspose %type_float_mat2x2 %mat\n"
1616                            "%tcol               = OpCompositeExtract %type_float_vec2 %tmat 0\n"
1617                            "%result             = OpCompositeExtract %type_float %tcol 0\n",
1618                            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"col", "mat", "tmat", "tcol", "result"});
1619     mo[OID_RETURN_VAL] =
1620         Op("ret_val", FLOAT_ARITHMETIC, "", "%type_test_fun      = OpTypeFunction %type_float %type_float\n", "", "",
1621            "%test_fun = OpFunction %type_float None %type_test_fun\n"
1622            "%param = OpFunctionParameter %type_float\n"
1623            "%entry = OpLabel\n"
1624            "OpReturnValue %param\n"
1625            "OpFunctionEnd\n",
1626            "%result             = OpFunctionCall %type_float %test_fun %arg1\n",
1627            B_STATEMENT_USAGE_TYPES_TYPE_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"param", "entry", "result"});
1628 
1629     // conversion operations that are meant to be used only for single output type (defined by the second number in name)
1630     const char *convertSource = "%result             = OpFConvert %type_float %arg1\n";
1631     mo[OID_CONV_FROM_FP16] =
1632         Op("conv_from_fp16", FLOAT_STORAGE_ONLY, false, FP16, "", convertSource, B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1633     mo[OID_CONV_FROM_FP32] =
1634         Op("conv_from_fp32", FLOAT_STORAGE_ONLY, false, FP32, "", convertSource, B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1635     mo[OID_CONV_FROM_FP64] =
1636         Op("conv_from_fp64", FLOAT_STORAGE_ONLY, false, FP64, "", convertSource, B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1637 
1638     mo[OID_ADD] = Op("add", FLOAT_ARITHMETIC, "%result             = OpFAdd %type_float %arg1 %arg2\n",
1639                      B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1640     mo[OID_SUB] = Op("sub", FLOAT_ARITHMETIC, "%result             = OpFSub %type_float %arg1 %arg2\n",
1641                      B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1642     mo[OID_MUL] = Op("mul", FLOAT_ARITHMETIC, "%result             = OpFMul %type_float %arg1 %arg2\n",
1643                      B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1644     mo[OID_DIV] = Op("div", FLOAT_ARITHMETIC, "%result             = OpFDiv %type_float %arg1 %arg2\n",
1645                      B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1646     mo[OID_REM] = Op("rem", FLOAT_ARITHMETIC, "%result             = OpFRem %type_float %arg1 %arg2\n",
1647                      B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1648     mo[OID_MOD] = Op("mod", FLOAT_ARITHMETIC, "%result             = OpFMod %type_float %arg1 %arg2\n",
1649                      B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1650 
1651     mo[OID_ADD_SUB_REASSOCIABLE] = Op("add_sub_reassociable", FLOAT_ARITHMETIC,
1652                                       "%temp               = OpFAdd %type_float %arg1 %arg2\n"
1653                                       "%result             = OpFSub %type_float %temp %arg1\n",
1654                                       B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1655 
1656     mo[OID_PHI]       = Op("phi", FLOAT_ARITHMETIC,
1657                            "%comp               = OpFOrdGreaterThan %type_bool %arg1 %arg2\n"
1658                                  "                      OpSelectionMerge %comp_merge None\n"
1659                                  "                      OpBranchConditional %comp %true_branch %false_branch\n"
1660                                  "%true_branch        = OpLabel\n"
1661                                  "                      OpBranch %comp_merge\n"
1662                                  "%false_branch       = OpLabel\n"
1663                                  "                      OpBranch %comp_merge\n"
1664                                  "%comp_merge         = OpLabel\n"
1665                                  "%result             = OpPhi %type_float %arg2 %true_branch %arg1 %false_branch\n",
1666                            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"comp", "result"});
1667     mo[OID_SELECT]    = Op("select", FLOAT_ARITHMETIC,
1668                            "%always_true        = OpFOrdGreaterThan %type_bool %c_float_1 %c_float_0\n"
1669                               "%result             = OpSelect %type_float %always_true %arg1 %arg2\n",
1670                            B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1671     mo[OID_DOT]       = Op("dot", FLOAT_ARITHMETIC,
1672                            "%vec1               = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
1673                                  "%vec2               = OpCompositeConstruct %type_float_vec2 %arg2 %arg2\n"
1674                                  "%result             = OpDot %type_float %vec1 %vec2\n",
1675                            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"vec1", "vec2", "result"});
1676     mo[OID_VEC_MUL_S] = Op("vmuls", FLOAT_ARITHMETIC,
1677                            "%vec                = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
1678                            "%tmpVec             = OpVectorTimesScalar %type_float_vec2 %vec %arg2\n"
1679                            "%result             = OpCompositeExtract %type_float %tmpVec 0\n",
1680                            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"vec", "tmpVec", "result"});
1681     mo[OID_VEC_MUL_M] = Op("vmulm", FLOAT_ARITHMETIC,
1682                            "%col                = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
1683                            "%mat                = OpCompositeConstruct %type_float_mat2x2 %col %col\n"
1684                            "%vec                = OpCompositeConstruct %type_float_vec2 %arg2 %arg2\n"
1685                            "%tmpVec             = OpVectorTimesMatrix %type_float_vec2 %vec %mat\n"
1686                            "%result             = OpCompositeExtract %type_float %tmpVec 0\n",
1687                            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"col", "mat", "vec", "tmpVec", "result"});
1688     mo[OID_MAT_MUL_S] = Op("mmuls", FLOAT_ARITHMETIC,
1689                            "%col                = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
1690                            "%mat                = OpCompositeConstruct %type_float_mat2x2 %col %col\n"
1691                            "%mulMat             = OpMatrixTimesScalar %type_float_mat2x2 %mat %arg2\n"
1692                            "%extCol             = OpCompositeExtract %type_float_vec2 %mulMat 0\n"
1693                            "%result             = OpCompositeExtract %type_float %extCol 0\n",
1694                            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"col", "mat", "mulMat", "result"});
1695     mo[OID_MAT_MUL_V] = Op("mmulv", FLOAT_ARITHMETIC,
1696                            "%col                = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
1697                            "%mat                = OpCompositeConstruct %type_float_mat2x2 %col %col\n"
1698                            "%vec                = OpCompositeConstruct %type_float_vec2 %arg2 %arg2\n"
1699                            "%mulVec             = OpMatrixTimesVector %type_float_vec2 %mat %vec\n"
1700                            "%result             = OpCompositeExtract %type_float %mulVec 0\n",
1701                            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"col", "mat", "vec", "mulVec", "result"});
1702     mo[OID_MAT_MUL_M] = Op("mmulm", FLOAT_ARITHMETIC,
1703                            "%col1               = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
1704                            "%mat1               = OpCompositeConstruct %type_float_mat2x2 %col1 %col1\n"
1705                            "%col2               = OpCompositeConstruct %type_float_vec2 %arg2 %arg2\n"
1706                            "%mat2               = OpCompositeConstruct %type_float_mat2x2 %col2 %col2\n"
1707                            "%mulMat             = OpMatrixTimesMatrix %type_float_mat2x2 %mat1 %mat2\n"
1708                            "%extCol             = OpCompositeExtract %type_float_vec2 %mulMat 0\n"
1709                            "%result             = OpCompositeExtract %type_float %extCol 0\n",
1710                            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"col1", "mat1", "col2", "mat2", "mulMat", "result"});
1711     mo[OID_OUT_PROD]  = Op("out_prod", FLOAT_ARITHMETIC,
1712                            "%vec1               = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
1713                             "%vec2               = OpCompositeConstruct %type_float_vec2 %arg2 %arg2\n"
1714                             "%mulMat             = OpOuterProduct %type_float_mat2x2 %vec1 %vec2\n"
1715                             "%extCol             = OpCompositeExtract %type_float_vec2 %mulMat 0\n"
1716                             "%result             = OpCompositeExtract %type_float %extCol 0\n",
1717                            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"vec1", "vec2", "mulMat", "result"});
1718 
1719     // comparison operations
1720     mo[OID_ORD_EQ]   = Op("ord_eq", FLOAT_ARITHMETIC,
1721                           "%boolVal           = OpFOrdEqual %type_bool %arg1 %arg2\n"
1722                             "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
1723                           B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"boolVal"});
1724     mo[OID_UORD_EQ]  = Op("uord_eq", FLOAT_ARITHMETIC,
1725                           "%boolVal           = OpFUnordEqual %type_bool %arg1 %arg2\n"
1726                            "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
1727                           B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"boolVal"});
1728     mo[OID_ORD_NEQ]  = Op("ord_neq", FLOAT_ARITHMETIC,
1729                           "%boolVal           = OpFOrdNotEqual %type_bool %arg1 %arg2\n"
1730                            "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
1731                           B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"boolVal"});
1732     mo[OID_UORD_NEQ] = Op("uord_neq", FLOAT_ARITHMETIC,
1733                           "%boolVal           = OpFUnordNotEqual %type_bool %arg1 %arg2\n"
1734                           "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
1735                           B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"boolVal"});
1736     mo[OID_ORD_LS]   = Op("ord_ls", FLOAT_ARITHMETIC,
1737                           "%boolVal           = OpFOrdLessThan %type_bool %arg1 %arg2\n"
1738                             "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
1739                           B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"boolVal"});
1740     mo[OID_UORD_LS]  = Op("uord_ls", FLOAT_ARITHMETIC,
1741                           "%boolVal           = OpFUnordLessThan %type_bool %arg1 %arg2\n"
1742                            "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
1743                           B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"boolVal"});
1744     mo[OID_ORD_GT]   = Op("ord_gt", FLOAT_ARITHMETIC,
1745                           "%boolVal           = OpFOrdGreaterThan %type_bool %arg1 %arg2\n"
1746                             "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
1747                           B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"boolVal"});
1748     mo[OID_UORD_GT]  = Op("uord_gt", FLOAT_ARITHMETIC,
1749                           "%boolVal           = OpFUnordGreaterThan %type_bool %arg1 %arg2\n"
1750                            "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
1751                           B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"boolVal"});
1752     mo[OID_ORD_LE]   = Op("ord_le", FLOAT_ARITHMETIC,
1753                           "%boolVal           = OpFOrdLessThanEqual %type_bool %arg1 %arg2\n"
1754                             "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
1755                           B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"boolVal"});
1756     mo[OID_UORD_LE]  = Op("uord_le", FLOAT_ARITHMETIC,
1757                           "%boolVal           = OpFUnordLessThanEqual %type_bool %arg1 %arg2\n"
1758                            "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
1759                           B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"boolVal"});
1760     mo[OID_ORD_GE]   = Op("ord_ge", FLOAT_ARITHMETIC,
1761                           "%boolVal           = OpFOrdGreaterThanEqual %type_bool %arg1 %arg2\n"
1762                             "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
1763                           B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"boolVal"});
1764     mo[OID_UORD_GE]  = Op("uord_ge", FLOAT_ARITHMETIC,
1765                           "%boolVal           = OpFUnordGreaterThanEqual %type_bool %arg1 %arg2\n"
1766                            "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n",
1767                           B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"boolVal"});
1768 
1769     mo[OID_ATAN2] =
1770         Op("atan2", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Atan2 %arg1 %arg2\n",
1771            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1772     mo[OID_POW] = Op("pow", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Pow %arg1 %arg2\n",
1773                      B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1774     mo[OID_MIX] = Op("mix", FLOAT_ARITHMETIC,
1775                      "%result             = OpExtInst %type_float %std450 FMix %arg1 %arg2 %c_float_0_5\n",
1776                      B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1777     // OID_FMA is testing that operations don't get merged into fma, so they deliberately don't use fma here.
1778     // The fast-math mode for the Add determines whether these operations can be contracted, so the OpFMul is not decorated.
1779     mo[OID_FMA] = Op("fma", FLOAT_ARITHMETIC,
1780                      "%temp               = OpFMul %type_float %arg1 %arg2\n"
1781                      "%result             = OpFAdd %type_float %temp %c_float_n1\n",
1782                      B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1783     // OID_FMA2PT58 is testing that operations don't get merged into fma, so they deliberately don't use fma here.
1784     // The fast-math mode for the Add determines whether these operations can be contracted, so the OpFMul is not decorated.
1785     mo[OID_FMA2PT58] = Op("fma", FLOAT_ARITHMETIC,
1786                           "%temp               = OpFMul %type_float %arg1 %arg2\n"
1787                           "%result             = OpFAdd %type_float %temp %c_float_n2pt58\n",
1788                           B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1789     mo[OID_SZ_FMA]   = Op("sz_fma", FLOAT_ARITHMETIC,
1790                           "%result             = OpExtInst %type_float %std450 Fma %arg1 %c_float_1 %arg2\n",
1791                           B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1792     mo[OID_MIN] = Op("min", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 FMin %arg1 %arg2\n",
1793                      B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1794     mo[OID_MAX] = Op("max", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 FMax %arg1 %arg2\n",
1795                      B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1796     mo[OID_CLAMP] =
1797         Op("clamp", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 FClamp %arg1 %arg2 %arg2\n",
1798            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1799     mo[OID_STEP] =
1800         Op("step", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Step %arg1 %arg2\n",
1801            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1802     mo[OID_SSTEP] = Op("sstep", FLOAT_ARITHMETIC,
1803                        "%result             = OpExtInst %type_float %std450 SmoothStep %arg1 %arg2 %c_float_0_5\n",
1804                        B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1805     mo[OID_DIST] =
1806         Op("distance", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Distance %arg1 %arg2\n",
1807            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1808     mo[OID_CROSS]    = Op("cross", FLOAT_ARITHMETIC,
1809                           "%vec1               = OpCompositeConstruct %type_float_vec3 %arg1 %arg1 %arg1\n"
1810                              "%vec2               = OpCompositeConstruct %type_float_vec3 %arg2 %arg2 %arg2\n"
1811                              "%tmpVec             = OpExtInst %type_float_vec3 %std450 Cross %vec1 %vec2\n"
1812                              "%result             = OpCompositeExtract %type_float %tmpVec 0\n",
1813                           B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"vec1", "vec2", "tmpVec", "result"});
1814     mo[OID_FACE_FWD] = Op("face_fwd", FLOAT_ARITHMETIC,
1815                           "%result             = OpExtInst %type_float %std450 FaceForward %c_float_1 %arg1 %arg2\n",
1816                           B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1817     mo[OID_NMIN] =
1818         Op("nmin", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 NMin %arg1 %arg2\n",
1819            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1820     mo[OID_NMAX] =
1821         Op("nmax", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 NMax %arg1 %arg2\n",
1822            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1823     mo[OID_NCLAMP] =
1824         Op("nclamp", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 NClamp %arg2 %arg1 %arg2\n",
1825            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1826 
1827     mo[OID_ROUND] = Op("round", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Round %arg1\n",
1828                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1829     mo[OID_ROUND_EV] =
1830         Op("round_ev", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 RoundEven %arg1\n",
1831            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1832     mo[OID_TRUNC] = Op("trunc", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Trunc %arg1\n",
1833                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1834     mo[OID_ABS]   = Op("abs", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 FAbs %arg1\n",
1835                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1836     mo[OID_SIGN]  = Op("sign", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 FSign %arg1\n",
1837                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1838     mo[OID_FLOOR] = Op("floor", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Floor %arg1\n",
1839                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1840     mo[OID_CEIL]  = Op("ceil", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Ceil %arg1\n",
1841                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1842     mo[OID_FRACT] = Op("fract", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Fract %arg1\n",
1843                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1844     mo[OID_RADIANS] =
1845         Op("radians", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Radians %arg1\n",
1846            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1847     mo[OID_DEGREES] =
1848         Op("degrees", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Degrees %arg1\n",
1849            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1850     mo[OID_SIN]   = Op("sin", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Sin %arg1\n",
1851                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1852     mo[OID_COS]   = Op("cos", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Cos %arg1\n",
1853                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1854     mo[OID_TAN]   = Op("tan", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Tan %arg1\n",
1855                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1856     mo[OID_ASIN]  = Op("asin", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Asin %arg1\n",
1857                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1858     mo[OID_ACOS]  = Op("acos", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Acos %arg1\n",
1859                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1860     mo[OID_ATAN]  = Op("atan", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Atan %arg1\n",
1861                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1862     mo[OID_SINH]  = Op("sinh", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Sinh %arg1\n",
1863                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1864     mo[OID_COSH]  = Op("cosh", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Cosh %arg1\n",
1865                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1866     mo[OID_TANH]  = Op("tanh", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Tanh %arg1\n",
1867                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1868     mo[OID_ASINH] = Op("asinh", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Asinh %arg1\n",
1869                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1870     mo[OID_ACOSH] = Op("acosh", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Acosh %arg1\n",
1871                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1872     mo[OID_ATANH] = Op("atanh", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Atanh %arg1\n",
1873                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1874     mo[OID_EXP]   = Op("exp", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Exp %arg1\n",
1875                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1876     mo[OID_LOG]   = Op("log", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Log %arg1\n",
1877                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1878     mo[OID_EXP2]  = Op("exp2", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Exp2 %arg1\n",
1879                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1880     mo[OID_LOG2]  = Op("log2", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Log2 %arg1\n",
1881                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1882     mo[OID_SQRT]  = Op("sqrt", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Sqrt %arg1\n",
1883                        B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1884     mo[OID_INV_SQRT] =
1885         Op("inv_sqrt", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 InverseSqrt %arg1\n",
1886            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1887     mo[OID_MODF] =
1888         Op("modf", FLOAT_ARITHMETIC, "", "", "", "%tmpVarPtr          = OpVariable %type_float_fptr Function\n", "",
1889            "%result             = OpExtInst %type_float %std450 Modf %arg1 %tmpVarPtr\n",
1890            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1891     mo[OID_MODF_ST_WH] = Op("modf_st_wh", FLOAT_ARITHMETIC,
1892                             "OpMemberDecorate %struct_ff 0 Offset 0\n"
1893                             "OpMemberDecorate %struct_ff 1 Offset ${float_width}\n",
1894                             "%struct_ff          = OpTypeStruct %type_float %type_float\n"
1895                             "%struct_ff_fptr     = OpTypePointer Function %struct_ff\n",
1896                             "", "%tmpStructPtr       = OpVariable %struct_ff_fptr Function\n", "",
1897                             "%tmpStruct          = OpExtInst %struct_ff %std450 ModfStruct %arg1\n"
1898                             "                      OpStore %tmpStructPtr %tmpStruct\n"
1899                             "%tmpLoc             = OpAccessChain %type_float_fptr %tmpStructPtr %c_i32_1\n"
1900                             "%result             = OpLoad %type_float %tmpLoc\n",
1901                             B_STATEMENT_USAGE_TYPES_TYPE_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT,
1902                             {"tmpStruct", "tmpLoc", "result"});
1903     mo[OID_MODF_ST_FR] = Op("modf_st_fr", FLOAT_ARITHMETIC,
1904                             "OpMemberDecorate %struct_ff 0 Offset 0\n"
1905                             "OpMemberDecorate %struct_ff 1 Offset ${float_width}\n",
1906                             "%struct_ff          = OpTypeStruct %type_float %type_float\n"
1907                             "%struct_ff_fptr     = OpTypePointer Function %struct_ff\n",
1908                             "", "%tmpStructPtr       = OpVariable %struct_ff_fptr Function\n", "",
1909                             "%tmpStruct          = OpExtInst %struct_ff %std450 ModfStruct %arg1\n"
1910                             "                      OpStore %tmpStructPtr %tmpStruct\n"
1911                             "%tmpLoc             = OpAccessChain %type_float_fptr %tmpStructPtr %c_i32_0\n"
1912                             "%result             = OpLoad %type_float %tmpLoc\n",
1913                             B_STATEMENT_USAGE_TYPES_TYPE_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT,
1914                             {"tmpStruct", "tmpLoc", "result"});
1915     mo[OID_LDEXP] =
1916         Op("ldexp", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Ldexp %arg1 %c_i32_1\n",
1917            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1918     mo[OID_FREXP] =
1919         Op("frexp", FLOAT_ARITHMETIC, "", "", "", "%tmpVarPtr          = OpVariable %type_i32_fptr Function\n", "",
1920            "%result             = OpExtInst %type_float %std450 Frexp %arg1 %tmpVarPtr\n",
1921            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1922     mo[OID_FREXP_ST] = Op("frexp_st", FLOAT_ARITHMETIC,
1923                           "OpMemberDecorate %struct_fi 0 Offset 0\n"
1924                           "OpMemberDecorate %struct_fi 1 Offset ${float_width}\n",
1925                           "%struct_fi          = OpTypeStruct %type_float %type_i32\n"
1926                           "%struct_fi_fptr     = OpTypePointer Function %struct_fi\n",
1927                           "", "%tmpStructPtr       = OpVariable %struct_fi_fptr Function\n", "",
1928                           "%tmpStruct          = OpExtInst %struct_fi %std450 FrexpStruct %arg1\n"
1929                           "                      OpStore %tmpStructPtr %tmpStruct\n"
1930                           "%tmpLoc             = OpAccessChain %type_float_fptr %tmpStructPtr %c_i32_0\n"
1931                           "%result             = OpLoad %type_float %tmpLoc\n",
1932                           B_STATEMENT_USAGE_TYPES_TYPE_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT,
1933                           {"struct_fi", "tmpStruct", "tmpLoc", "result"});
1934     mo[OID_LENGTH] =
1935         Op("length", FLOAT_ARITHMETIC, "%result             = OpExtInst %type_float %std450 Length %arg1\n",
1936            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT);
1937     mo[OID_NORMALIZE] = Op("normalize", FLOAT_ARITHMETIC,
1938                            "%vec1               = OpCompositeConstruct %type_float_vec2 %arg1 %c_float_2\n"
1939                            "%tmpVec             = OpExtInst %type_float_vec2 %std450 Normalize %vec1\n"
1940                            "%result             = OpCompositeExtract %type_float %tmpVec 0\n",
1941                            B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT,
1942                            {"vec1", "tmpVec", "result"});
1943     mo[OID_REFLECT]   = Op("reflect", FLOAT_ARITHMETIC,
1944                            "%vec1               = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
1945                              "%vecN               = OpCompositeConstruct %type_float_vec2 %c_float_0 %c_float_n1\n"
1946                              "%tmpVec             = OpExtInst %type_float_vec2 %std450 Reflect %vec1 %vecN\n"
1947                              "%result             = OpCompositeExtract %type_float %tmpVec 0\n",
1948                            B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT,
1949                            {"vec1", "vecN", "tmpVec", "result"});
1950     mo[OID_REFRACT]   = Op("refract", FLOAT_ARITHMETIC,
1951                            "%vec1               = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
1952                              "%vecN               = OpCompositeConstruct %type_float_vec2 %c_float_0 %c_float_n1\n"
1953                              "%tmpVec             = OpExtInst %type_float_vec2 %std450 Refract %vec1 %vecN %c_float_0_5\n"
1954                              "%result             = OpCompositeExtract %type_float %tmpVec 0\n",
1955                            B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT,
1956                            {"vec1", "vecN", "tmpVec", "result"});
1957     mo[OID_MAT_DET]   = Op("mat_det", FLOAT_ARITHMETIC,
1958                            "%col                = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
1959                              "%mat                = OpCompositeConstruct %type_float_mat2x2 %col %col\n"
1960                              "%result             = OpExtInst %type_float %std450 Determinant %mat\n",
1961                            B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT, {"col", "mat", "result"});
1962     mo[OID_MAT_INV]   = Op("mat_inv", FLOAT_ARITHMETIC,
1963                            "%col1               = OpCompositeConstruct %type_float_vec2 %arg1 %c_float_1\n"
1964                              "%col2               = OpCompositeConstruct %type_float_vec2 %c_float_1 %c_float_1\n"
1965                              "%mat                = OpCompositeConstruct %type_float_mat2x2 %col1 %col2\n"
1966                              "%invMat             = OpExtInst %type_float_mat2x2 %std450 MatrixInverse %mat\n"
1967                              "%extCol             = OpCompositeExtract %type_float_vec2 %invMat 1\n"
1968                              "%result             = OpCompositeExtract %type_float %extCol 1\n",
1969                            B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_TYPE_FLOAT,
1970                            {"col1", "col2", "mat", "invMat", "result"});
1971 }
1972 
build(vector<OperationTestCase> & testCases,TypeTestResultsSP typeTestResults)1973 void TestCasesBuilder::build(vector<OperationTestCase> &testCases, TypeTestResultsSP typeTestResults)
1974 {
1975     bool isFP16 = typeTestResults->floatType() == FP16;
1976 
1977     for (auto it = typeTestResults->testCaseInputs.begin(); it != typeTestResults->testCaseInputs.end(); it++)
1978     {
1979         string OTCname = "op_testedWithout_" + getBehaviourName(it->testedFlagBits, "_OR_") + "_arg1_" +
1980                          getValueName(it->operandFirst) + "_arg2_" + getValueName(it->operandSecond) + "_res_" +
1981                          getValueName(it->result);
1982 
1983         testCases.push_back(OTC((OTCname + "_exec").c_str(), invert(it->testedFlagBits), false, it->operationId,
1984                                 it->operandFirst, it->operandSecond, it->result, isFP16, it->requireRte));
1985         testCases.push_back(OTC((OTCname + "_deco").c_str(), invert(it->testedFlagBits), true, it->operationId,
1986                                 it->operandFirst, it->operandSecond, it->result, isFP16, it->requireRte));
1987     }
1988 
1989     // test None, AllowTransform and AllowRecip gramatically
1990     testCases.push_back(
1991         OTC("op_None_exec_grammar_test", FP::MaskNone, false, OID_ADD, V_MAX, V_HUGE, V_INF, isFP16, true));
1992     testCases.push_back(OTC("op_AllowTransform_OR_AllowReassoc_OR_AllowContract_exec_grammar_test",
1993                             FP::AllowTransform | FP::AllowReassoc | FP::AllowContract, false, OID_ADD, V_MAX, V_HUGE,
1994                             V_INF, isFP16, true));
1995     // the test for AllowRecip gives the same result with or without the flag
1996     testCases.push_back(
1997         OTC("op_AllowRecip_exec_grammar_test", FP::AllowRecip, false, OID_DIV, V_ONE, V_TWO, V_HALF, isFP16));
1998 }
1999 
getOperation(OperationId id) const2000 const Operation &TestCasesBuilder::getOperation(OperationId id) const
2001 {
2002     return m_operations.at(id);
2003 }
2004 
2005 template <typename TYPE, typename FLOAT_TYPE>
valMatches(const TYPE & ret,ValueId expected)2006 bool valMatches(const TYPE &ret, ValueId expected)
2007 {
2008     TypeValues<FLOAT_TYPE> typeValues;
2009 
2010     if (expected == V_NAN && ret.isNaN())
2011         return true;
2012 
2013     typename RawConvert<FLOAT_TYPE, typename TYPE::StorageType>::Value val;
2014     val.fp = typeValues.getValue(expected);
2015     return ret.bits() == val.ui;
2016 }
2017 
2018 template <typename TYPE, typename FLOAT_TYPE>
isEither(const TYPE & returnedFloat,ValueId expected1,ValueId expected2,TestLog & log)2019 bool isEither(const TYPE &returnedFloat, ValueId expected1, ValueId expected2, TestLog &log)
2020 {
2021     TypeValues<FLOAT_TYPE> typeValues;
2022 
2023     if (valMatches<TYPE, FLOAT_TYPE>(returnedFloat, expected1) ||
2024         valMatches<TYPE, FLOAT_TYPE>(returnedFloat, expected2))
2025         return true;
2026 
2027     typename RawConvert<FLOAT_TYPE, typename TYPE::StorageType>::Value val1, val2;
2028     val1.fp = typeValues.getValue(expected1);
2029     val2.fp = typeValues.getValue(expected2);
2030 
2031     log << TestLog::Message << "Expected " << toHex(val1.ui) << " (" << val1.fp << ")"
2032         << " or " << toHex(val2.ui) << " (" << val2.fp << ")" << TestLog::EndMessage;
2033     return false;
2034 }
2035 
2036 template <typename TYPE>
isTrigULPResultCorrect(const TYPE & returnedFloat,ValueId expected,TestLog & log)2037 bool isTrigULPResultCorrect(const TYPE &returnedFloat, ValueId expected, TestLog &log)
2038 {
2039     // The trig ULP results are used for things like the inverse trig functions. The spec gives
2040     // precisions for these based on atan, so that precision is used here.
2041 
2042     // This functions doesn't give correct results for fp64 at present, but this is never used.
2043     assert(returnedFloat.MANTISSA_BITS == 23 || returnedFloat.MANTISSA_BITS == 10);
2044 
2045     FloatFormat fp32Format(-126, 127, 23, true, tcu::MAYBE, tcu::YES, tcu::MAYBE);
2046     FloatFormat fp16Format(-14, 15, 10, true, tcu::MAYBE);
2047 
2048     const FloatFormat *fmt = (returnedFloat.MANTISSA_BITS == 10) ? &fp16Format : &fp32Format;
2049 
2050     // The ULP range is based on the exact result, which we approximate using the double value.
2051     TypeValues<double> typeValues;
2052     const double ref = typeValues.getValue(expected);
2053     uint32_t ulp     = (returnedFloat.MANTISSA_BITS == 10) ? 5 : 4096;
2054 
2055     double precision = fmt->ulp(ref, ulp);
2056 
2057     if (deAbs(returnedFloat.asDouble() - ref) < precision)
2058         return true;
2059 
2060     log << TestLog::Message << "Expected result to be in range"
2061         << " (" << ref - precision << ", " << ref + precision << "), got " << returnedFloat.asDouble()
2062         << TestLog::EndMessage;
2063     return false;
2064 }
2065 
2066 template <typename TYPE>
isTrigAbsResultCorrect(const TYPE & returnedFloat,TestLog & log)2067 bool isTrigAbsResultCorrect(const TYPE &returnedFloat, TestLog &log)
2068 {
2069     // for cos(x) with x between -pi and pi, the precision error is 2^-11 for fp32 and 2^-7 for fp16.
2070     double precision      = returnedFloat.MANTISSA_BITS == 23 ? dePow(2, -11) : dePow(2, -7);
2071     const double expected = 1.0;
2072 
2073     if (deAbs(returnedFloat.asDouble() - expected) < precision)
2074         return true;
2075 
2076     log << TestLog::Message << "Expected result to be in range"
2077         << " (" << expected - precision << ", " << expected + precision << "), got " << returnedFloat.asDouble()
2078         << TestLog::EndMessage;
2079     return false;
2080 }
2081 
2082 // Function used to compare test result with expected output.
2083 // TYPE can be Float16, Float32 or Float64.
2084 // FLOAT_TYPE can be deFloat16, float, double.
2085 template <typename TYPE, typename FLOAT_TYPE>
compareBytes(vector<uint8_t> & expectedBytes,AllocationSp outputAlloc,TestLog & log)2086 bool compareBytes(vector<uint8_t> &expectedBytes, AllocationSp outputAlloc, TestLog &log)
2087 {
2088     const TYPE *returned = static_cast<const TYPE *>(outputAlloc->getHostPtr());
2089     const TYPE *fValueId = reinterpret_cast<const TYPE *>(&expectedBytes.front());
2090 
2091     // all test return single value
2092     // Fp16 nostorage tests get their values from a uint32_t value, but we create the
2093     // buffer with the same size for both cases: 4 bytes.
2094     if (sizeof(TYPE) == 2u)
2095         DE_ASSERT((expectedBytes.size() / sizeof(TYPE)) == 2);
2096     else
2097         DE_ASSERT((expectedBytes.size() / sizeof(TYPE)) == 1);
2098 
2099     // during test setup we do not store expected value but id that can be used to
2100     // retrieve actual value - this is done to handle special cases like multiple
2101     // allowed results or epsilon checks for some cases
2102     // note that this is workaround - this should be done by changing
2103     // ComputerShaderCase and GraphicsShaderCase so that additional arguments can
2104     // be passed to this verification callback
2105     typedef typename TYPE::StorageType SType;
2106     SType expectedInt       = fValueId[0].bits();
2107     ValueId expectedValueId = static_cast<ValueId>(expectedInt);
2108 
2109     // something went wrong, expected value cant be V_UNUSED,
2110     // if this is the case then test shouldn't be created at all
2111     DE_ASSERT(expectedValueId != V_UNUSED);
2112 
2113     TYPE returnedFloat = returned[0];
2114 
2115     log << TestLog::Message << "Calculated result: " << toHex(returnedFloat.bits()) << " (" << returnedFloat.asFloat()
2116         << ")" << TestLog::EndMessage;
2117 
2118     // handle multiple acceptable results cases
2119     if (expectedValueId == V_SIGN_NAN)
2120     {
2121         if (valMatches<TYPE, FLOAT_TYPE>(returnedFloat, V_MINUS_ONE) ||
2122             valMatches<TYPE, FLOAT_TYPE>(returnedFloat, V_MINUS_ZERO) ||
2123             valMatches<TYPE, FLOAT_TYPE>(returnedFloat, V_ZERO) || valMatches<TYPE, FLOAT_TYPE>(returnedFloat, V_ONE))
2124             return true;
2125 
2126         log << TestLog::Message << "Expected -1, -0, +0 or +1" << TestLog::EndMessage;
2127         return false;
2128     }
2129 
2130     if (expectedValueId == V_ZERO_OR_MINUS_ZERO)
2131         return isEither<TYPE, FLOAT_TYPE>(returnedFloat, V_ZERO, V_MINUS_ZERO, log);
2132 
2133     if (expectedValueId == V_ZERO_OR_ONE)
2134         return isEither<TYPE, FLOAT_TYPE>(returnedFloat, V_ZERO, V_ONE, log);
2135 
2136     if (expectedValueId == V_ONE_OR_NAN)
2137         return isEither<TYPE, FLOAT_TYPE>(returnedFloat, V_ONE, V_NAN, log);
2138 
2139     // handle trigonometric operations precision errors
2140     if (expectedValueId == V_TRIG_ONE)
2141         return isTrigAbsResultCorrect<TYPE>(returnedFloat, log);
2142 
2143     // handle cases with large ULP precision bounds.
2144     if (expectedValueId == V_PI || expectedValueId == V_MINUS_PI || expectedValueId == V_PI_DIV_2 ||
2145         expectedValueId == V_MINUS_PI_DIV_2 || expectedValueId == V_PI_DIV_4 || expectedValueId == V_MINUS_PI_DIV_4 ||
2146         expectedValueId == V_3_PI_DIV_4 || expectedValueId == V_MINUS_3_PI_DIV_4)
2147         return isTrigULPResultCorrect(returnedFloat, expectedValueId, log);
2148 
2149     if (valMatches<TYPE, FLOAT_TYPE>(returnedFloat, expectedValueId))
2150         return true;
2151 
2152     TypeValues<FLOAT_TYPE> typeValues;
2153 
2154     typename RawConvert<FLOAT_TYPE, SType>::Value value;
2155     value.fp = typeValues.getValue(expectedValueId);
2156 
2157     log << TestLog::Message << "Expected " << toHex(value.ui) << " (" << value.fp << ")" << TestLog::EndMessage;
2158     return false;
2159 }
2160 
2161 template <typename TYPE, typename FLOAT_TYPE>
checkFloats(const vector<Resource> &,const vector<AllocationSp> & outputAllocs,const vector<Resource> & expectedOutputs,TestLog & log)2162 bool checkFloats(const vector<Resource> &, const vector<AllocationSp> &outputAllocs,
2163                  const vector<Resource> &expectedOutputs, TestLog &log)
2164 {
2165     if (outputAllocs.size() != expectedOutputs.size())
2166         return false;
2167 
2168     for (uint32_t outputNdx = 0; outputNdx < outputAllocs.size(); ++outputNdx)
2169     {
2170         vector<uint8_t> expectedBytes;
2171         expectedOutputs[outputNdx].getBytes(expectedBytes);
2172 
2173         if (!compareBytes<TYPE, FLOAT_TYPE>(expectedBytes, outputAllocs[outputNdx], log))
2174             return false;
2175     }
2176 
2177     return true;
2178 }
2179 
2180 // Base class for ComputeTestGroupBuilder and GrephicstestGroupBuilder classes.
2181 // It contains all functionalities that are used by both child classes.
2182 class TestGroupBuilderBase
2183 {
2184 public:
2185     TestGroupBuilderBase();
2186     virtual ~TestGroupBuilderBase() = default;
2187 
2188     virtual void createOperationTests(TestCaseGroup *parentGroup, const char *groupName, FloatType floatType,
2189                                       bool argumentsFromInput) = 0;
2190 
2191 protected:
2192     typedef vector<OperationTestCase> TestCaseVect;
2193 
2194     // Structure containing all data required to create single operation test.
2195     struct OperationTestCaseInfo
2196     {
2197         FloatType outFloatType;
2198         bool argumentsFromInput;
2199         VkShaderStageFlagBits testedStage;
2200         const Operation &operation;
2201         const OperationTestCase &testCase;
2202     };
2203 
2204     void specializeOperation(const OperationTestCaseInfo &testCaseInfo,
2205                              SpecializedOperation &specializedOperation) const;
2206 
2207     void getBehaviorCapabilityAndExecutionModeDecoration(FP behaviorFlagsExecMode, FP behaviorFlagsDecoration,
2208                                                          bool useDecorationFlags, vector<string> IDsToDecorate,
2209                                                          const string inBitWidth, string &capability,
2210                                                          string &executionMode, string &decoration,
2211                                                          string &constant) const;
2212 
2213     void FillFloatControlsProperties(vk::VkPhysicalDeviceFloatControlsProperties &fc, const OperationTestCase &testCase,
2214                                      FloatType floatType) const;
2215 
2216 protected:
2217     struct TypeData
2218     {
2219         TypeValuesSP values;
2220         TypeSnippetsSP snippets;
2221         TypeTestResultsSP testResults;
2222     };
2223 
2224     // Type specific parameters are stored in this map.
2225     map<FloatType, TypeData> m_typeData;
2226 };
2227 
TestGroupBuilderBase()2228 TestGroupBuilderBase::TestGroupBuilderBase()
2229 {
2230     m_typeData[FP16]             = TypeData();
2231     m_typeData[FP16].values      = TypeValuesSP(new TypeValues<deFloat16>);
2232     m_typeData[FP16].snippets    = TypeSnippetsSP(new TypeSnippets<deFloat16>);
2233     m_typeData[FP16].testResults = TypeTestResultsSP(new TypeTestResults<deFloat16>);
2234     m_typeData[FP32]             = TypeData();
2235     m_typeData[FP32].values      = TypeValuesSP(new TypeValues<float>);
2236     m_typeData[FP32].snippets    = TypeSnippetsSP(new TypeSnippets<float>);
2237     m_typeData[FP32].testResults = TypeTestResultsSP(new TypeTestResults<float>);
2238     m_typeData[FP64]             = TypeData();
2239     m_typeData[FP64].values      = TypeValuesSP(new TypeValues<double>);
2240     m_typeData[FP64].snippets    = TypeSnippetsSP(new TypeSnippets<double>);
2241     m_typeData[FP64].testResults = TypeTestResultsSP(new TypeTestResults<double>);
2242 }
2243 
specializeOperation(const OperationTestCaseInfo & testCaseInfo,SpecializedOperation & specializedOperation) const2244 void TestGroupBuilderBase::specializeOperation(const OperationTestCaseInfo &testCaseInfo,
2245                                                SpecializedOperation &specializedOperation) const
2246 {
2247     const string typeToken  = "_float";
2248     const string widthToken = "${float_width}";
2249 
2250     FloatType outFloatType               = testCaseInfo.outFloatType;
2251     const Operation &operation           = testCaseInfo.operation;
2252     const TypeSnippetsSP outTypeSnippets = m_typeData.at(outFloatType).snippets;
2253     const bool inputRestricted           = operation.isInputTypeRestricted;
2254     FloatType inFloatType                = operation.restrictedInputType;
2255 
2256     // usually input type is same as output but this is not the case for conversion
2257     // operations; in those cases operation definitions have restricted input type
2258     inFloatType = inputRestricted ? inFloatType : outFloatType;
2259 
2260     TypeSnippetsSP inTypeSnippets = m_typeData.at(inFloatType).snippets;
2261 
2262     const string inTypePrefix  = string("_f") + inTypeSnippets->bitWidth;
2263     const string outTypePrefix = string("_f") + outTypeSnippets->bitWidth;
2264 
2265     std::string byteWidthToken = std::to_string(std::stoi(outTypeSnippets->bitWidth) / 8);
2266 
2267     specializedOperation.constants   = replace(operation.constants, typeToken, inTypePrefix);
2268     specializedOperation.annotations = replace(operation.annotations, widthToken, byteWidthToken);
2269     specializedOperation.types       = replace(operation.types, typeToken, outTypePrefix);
2270     specializedOperation.variables   = replace(operation.variables, typeToken, outTypePrefix);
2271     specializedOperation.functions   = replace(operation.functions, typeToken, outTypePrefix);
2272     specializedOperation.commands    = replace(operation.commands, typeToken, outTypePrefix);
2273 
2274     specializedOperation.inFloatType                = inFloatType;
2275     specializedOperation.inTypeSnippets             = inTypeSnippets;
2276     specializedOperation.outTypeSnippets            = outTypeSnippets;
2277     specializedOperation.argumentsUsesFloatConstant = 0;
2278 
2279     if (operation.isSpecConstant)
2280         return;
2281 
2282     // select way arguments are prepared
2283     if (testCaseInfo.argumentsFromInput)
2284     {
2285         // read arguments from input SSBO in main function
2286         specializedOperation.arguments = inTypeSnippets->argumentsFromInputSnippet;
2287 
2288         if (inFloatType == FP16 && testCaseInfo.testCase.fp16Without16BitStorage)
2289             specializedOperation.arguments = inTypeSnippets->argumentsFromInputFp16Snippet;
2290     }
2291     else
2292     {
2293         // generate proper values in main function
2294         const string arg1 = "%arg1                 = ";
2295         const string arg2 = "%arg2                 = ";
2296 
2297         const ValueId *inputArguments = testCaseInfo.testCase.input;
2298         if (inputArguments[0] != V_UNUSED)
2299         {
2300             specializedOperation.arguments = arg1 + inTypeSnippets->valueIdToSnippetArgMap.at(inputArguments[0]);
2301             specializedOperation.argumentsUsesFloatConstant |= B_STATEMENT_USAGE_ARGS_CONST_FLOAT;
2302         }
2303         if (inputArguments[1] != V_UNUSED)
2304         {
2305             specializedOperation.arguments += arg2 + inTypeSnippets->valueIdToSnippetArgMap.at(inputArguments[1]);
2306             specializedOperation.argumentsUsesFloatConstant |= B_STATEMENT_USAGE_ARGS_CONST_FLOAT;
2307         }
2308     }
2309 }
2310 
getBehaviorCapabilityAndExecutionModeDecoration(FP behaviorFlagsExecMode,FP behaviorFlagsDecoration,bool useDecorationFlags,vector<string> IDsToDecorate,const string inBitWidth,string & capability,string & executionMode,string & decoration,string & constant) const2311 void TestGroupBuilderBase::getBehaviorCapabilityAndExecutionModeDecoration(
2312     FP behaviorFlagsExecMode, FP behaviorFlagsDecoration, bool useDecorationFlags, vector<string> IDsToDecorate,
2313     const string inBitWidth, string &capability, string &executionMode, string &decoration, string &constant) const
2314 {
2315     capability += "OpCapability FloatControls2\n";
2316     constant += "%bc_u32_fp_exec_mode = OpConstant %type_u32 " + to_string((unsigned)behaviorFlagsExecMode) + "\n";
2317     executionMode += "OpExecutionModeId %main FPFastMathDefault %type_f" + inBitWidth + " %bc_u32_fp_exec_mode\n";
2318 
2319     if (useDecorationFlags)
2320         for (size_t i = 0; i < IDsToDecorate.size(); i++)
2321         {
2322             decoration.append("OpDecorate %").append(IDsToDecorate[i]).append(" FPFastMathMode ");
2323             decoration += getBehaviourName(behaviorFlagsDecoration, "|");
2324             decoration += "\n";
2325         }
2326 
2327     DE_ASSERT(!capability.empty() && !executionMode.empty());
2328 }
2329 
FillFloatControlsProperties(vk::VkPhysicalDeviceFloatControlsProperties & fc,const OperationTestCase & testCase,FloatType floatType) const2330 void TestGroupBuilderBase::FillFloatControlsProperties(vk::VkPhysicalDeviceFloatControlsProperties &fc,
2331                                                        const OperationTestCase &testCase, FloatType floatType) const
2332 {
2333     const FP SZInfNaNPreserveBits = (FP::NSZ | FP::NotInf | FP::NotNaN);
2334     bool requiresSZInfNaNPreserve;
2335     requiresSZInfNaNPreserve = ((testCase.behaviorFlagsExecMode & SZInfNaNPreserveBits) != SZInfNaNPreserveBits) ||
2336                                ((testCase.behaviorFlagsDecoration & SZInfNaNPreserveBits) != SZInfNaNPreserveBits);
2337 
2338     switch (floatType)
2339     {
2340     case FP16:
2341         fc.shaderSignedZeroInfNanPreserveFloat16 = requiresSZInfNaNPreserve;
2342         break;
2343     case FP32:
2344         fc.shaderSignedZeroInfNanPreserveFloat32 = requiresSZInfNaNPreserve;
2345         break;
2346     case FP64:
2347         fc.shaderSignedZeroInfNanPreserveFloat64 = requiresSZInfNaNPreserve;
2348         break;
2349     }
2350 
2351     switch (floatType)
2352     {
2353     case FP16:
2354         fc.shaderRoundingModeRTEFloat16 = testCase.requireRte;
2355         break;
2356     case FP32:
2357         fc.shaderRoundingModeRTEFloat32 = testCase.requireRte;
2358         break;
2359     case FP64:
2360         fc.shaderRoundingModeRTEFloat64 = testCase.requireRte;
2361         break;
2362     }
2363 }
2364 
2365 // ComputeTestGroupBuilder contains logic that creates compute shaders
2366 // for all test cases. As most tests in spirv-assembly it uses functionality
2367 // implemented in vktSpvAsmComputeShaderTestUtil.cpp.
2368 class ComputeTestGroupBuilder : public TestGroupBuilderBase
2369 {
2370 public:
2371     void init();
2372 
2373     void createOperationTests(TestCaseGroup *parentGroup, const char *groupName, FloatType floatType,
2374                               bool argumentsFromInput) override;
2375 
2376 protected:
2377     void fillShaderSpec(const OperationTestCaseInfo &testCaseInfo, ComputeShaderSpec &csSpec) const;
2378 
2379 private:
2380     StringTemplate m_operationShaderTemplate;
2381     TestCasesBuilder m_operationTestCaseBuilder;
2382 };
2383 
init()2384 void ComputeTestGroupBuilder::init()
2385 {
2386     m_operationTestCaseBuilder.init();
2387 
2388     // generic compute shader template with common code for all
2389     // float types and all possible operations listed in OperationId enum
2390     m_operationShaderTemplate.setString("OpCapability Shader\n"
2391                                         "${capabilities}"
2392 
2393                                         "OpExtension \"SPV_KHR_float_controls2\"\n"
2394                                         "${extensions}"
2395 
2396                                         "%std450            = OpExtInstImport \"GLSL.std.450\"\n"
2397                                         "OpMemoryModel Logical GLSL450\n"
2398                                         "OpEntryPoint GLCompute %main \"main\" %id\n"
2399                                         "OpExecutionMode %main LocalSize 1 1 1\n"
2400                                         "${execution_mode}"
2401 
2402                                         "OpDecorate %id BuiltIn GlobalInvocationId\n"
2403 
2404                                         "${decorations}"
2405 
2406                                         // some tests require additional annotations
2407                                         "${annotations}"
2408 
2409                                         "%type_void            = OpTypeVoid\n"
2410                                         "%type_voidf           = OpTypeFunction %type_void\n"
2411                                         "%type_bool            = OpTypeBool\n"
2412                                         "%type_u32             = OpTypeInt 32 0\n"
2413                                         "%type_i32             = OpTypeInt 32 1\n"
2414                                         "%type_i32_fptr        = OpTypePointer Function %type_i32\n"
2415                                         "%type_u32_vec2        = OpTypeVector %type_u32 2\n"
2416                                         "%type_u32_vec3        = OpTypeVector %type_u32 3\n"
2417                                         "%type_u32_vec3_ptr    = OpTypePointer Input %type_u32_vec3\n"
2418 
2419                                         "%c_i32_0              = OpConstant %type_i32 0\n"
2420                                         "%c_i32_1              = OpConstant %type_i32 1\n"
2421                                         "%c_i32_2              = OpConstant %type_i32 2\n"
2422 
2423                                         // if input float type has different width then output then
2424                                         // both types are defined here along with all types derived from
2425                                         // them that are commonly used by tests; some tests also define
2426                                         // their own types (those that are needed just by this single test)
2427                                         "${types}"
2428 
2429                                         // SSBO definitions
2430                                         "${io_definitions}"
2431 
2432                                         "%id                   = OpVariable %type_u32_vec3_ptr Input\n"
2433 
2434                                         // set of default constants per float type is placed here,
2435                                         // operation tests can also define additional constants.
2436                                         "${constants}"
2437                                         "${behaviorConstants}"
2438 
2439                                         // O_RETURN_VAL defines function here and becouse
2440                                         // of that this token needs to be directly before main function
2441                                         "${functions}"
2442 
2443                                         "%main                 = OpFunction %type_void None %type_voidf\n"
2444                                         "%label                = OpLabel\n"
2445 
2446                                         "${variables}"
2447 
2448                                         // depending on test case arguments are either read from input ssbo
2449                                         // or generated in spir-v code - in later case shader input is not used
2450                                         "${arguments}"
2451 
2452                                         // perform test commands
2453                                         "${commands}"
2454 
2455                                         // save result to SSBO
2456                                         "${save_result}"
2457 
2458                                         "OpReturn\n"
2459                                         "OpFunctionEnd\n");
2460 }
2461 
createOperationTests(TestCaseGroup * parentGroup,const char * groupName,FloatType floatType,bool argumentsFromInput)2462 void ComputeTestGroupBuilder::createOperationTests(TestCaseGroup *parentGroup, const char *groupName,
2463                                                    FloatType floatType, bool argumentsFromInput)
2464 {
2465     TestContext &testCtx = parentGroup->getTestContext();
2466     TestCaseGroup *group = new TestCaseGroup(testCtx, groupName, "");
2467     parentGroup->addChild(group);
2468 
2469     TestCaseVect testCases;
2470     m_operationTestCaseBuilder.build(testCases, m_typeData[floatType].testResults);
2471 
2472     for (auto &testCase : testCases)
2473     {
2474         // skip cases with undefined output
2475         if (testCase.expectedOutput == V_UNUSED)
2476             continue;
2477 
2478         OperationTestCaseInfo testCaseInfo = {floatType, argumentsFromInput, VK_SHADER_STAGE_COMPUTE_BIT,
2479                                               m_operationTestCaseBuilder.getOperation(testCase.operationId), testCase};
2480 
2481         ComputeShaderSpec csSpec;
2482 
2483         fillShaderSpec(testCaseInfo, csSpec);
2484 
2485         string testName = replace(testCase.baseName, "op", testCaseInfo.operation.name);
2486         group->addChild(new SpvAsmComputeShaderCase(testCtx, testName.c_str(), csSpec));
2487     }
2488 }
2489 
fillShaderSpec(const OperationTestCaseInfo & testCaseInfo,ComputeShaderSpec & csSpec) const2490 void ComputeTestGroupBuilder::fillShaderSpec(const OperationTestCaseInfo &testCaseInfo, ComputeShaderSpec &csSpec) const
2491 {
2492     // LUT storing functions used to verify test results
2493     const VerifyIOFunc checkFloatsLUT[] = {checkFloats<Float16, deFloat16>, checkFloats<Float32, float>,
2494                                            checkFloats<Float64, double>};
2495 
2496     const Operation &testOperation    = testCaseInfo.operation;
2497     const OperationTestCase &testCase = testCaseInfo.testCase;
2498     FloatType outFloatType            = testCaseInfo.outFloatType;
2499 
2500     SpecializedOperation specOpData;
2501     specializeOperation(testCaseInfo, specOpData);
2502 
2503     TypeSnippetsSP inTypeSnippets  = specOpData.inTypeSnippets;
2504     TypeSnippetsSP outTypeSnippets = specOpData.outTypeSnippets;
2505     FloatType inFloatType          = specOpData.inFloatType;
2506 
2507     bool outFp16WithoutStorage = (outFloatType == FP16) && testCase.fp16Without16BitStorage;
2508     bool inFp16WithoutStorage  = (inFloatType == FP16) && testCase.fp16Without16BitStorage;
2509 
2510     // UnpackHalf2x16 is a corner case - it returns two 32-bit floats but
2511     // internaly operates on fp16 and this type should be used by float controls
2512     string inFloatWidthForCaps = inTypeSnippets->bitWidth;
2513     string behaviorCapability;
2514     string behaviorExecutionMode;
2515     string behaviorDecorations;
2516     string behaviorConstants;
2517     getBehaviorCapabilityAndExecutionModeDecoration(testCase.behaviorFlagsExecMode, testCase.behaviorFlagsDecoration,
2518                                                     testCase.useDecorationFlags, testOperation.IDsToDecorate,
2519                                                     inFloatWidthForCaps, behaviorCapability, behaviorExecutionMode,
2520                                                     behaviorDecorations, behaviorConstants);
2521 
2522     string capabilities = behaviorCapability + outTypeSnippets->capabilities;
2523     string extensions   = outTypeSnippets->extensions;
2524     string annotations  = inTypeSnippets->inputAnnotationsSnippet + outTypeSnippets->outputAnnotationsSnippet +
2525                          outTypeSnippets->typeAnnotationsSnippet;
2526     string types         = outTypeSnippets->typeDefinitionsSnippet;
2527     string constants     = outTypeSnippets->constantsDefinitionsSnippet;
2528     string ioDefinitions = "";
2529 
2530     // Getting rid of 16bit_storage dependency imply replacing lots of snippets.
2531     {
2532         if (inFp16WithoutStorage)
2533             ioDefinitions = inTypeSnippets->inputDefinitionsFp16Snippet;
2534         else
2535             ioDefinitions = inTypeSnippets->inputDefinitionsSnippet;
2536 
2537         if (outFp16WithoutStorage)
2538         {
2539             extensions   = outTypeSnippets->extensionsFp16Without16BitStorage;
2540             capabilities = behaviorCapability + outTypeSnippets->capabilitiesFp16Without16BitStorage;
2541             types += outTypeSnippets->typeDefinitionsFp16Snippet;
2542             annotations += outTypeSnippets->typeAnnotationsFp16Snippet;
2543             ioDefinitions += outTypeSnippets->outputDefinitionsFp16Snippet;
2544         }
2545         else
2546             ioDefinitions += outTypeSnippets->outputDefinitionsSnippet;
2547     }
2548 
2549     bool outFp16TypeUsage = outTypeSnippets->loadStoreRequiresShaderFloat16;
2550     bool inFp16TypeUsage  = false;
2551 
2552     if (testOperation.isInputTypeRestricted)
2553     {
2554         annotations += inTypeSnippets->typeAnnotationsSnippet;
2555         types += inTypeSnippets->typeDefinitionsSnippet;
2556         constants += inTypeSnippets->constantsDefinitionsSnippet;
2557 
2558         if (inFp16WithoutStorage)
2559         {
2560             annotations += inTypeSnippets->typeAnnotationsFp16Snippet;
2561             types += inTypeSnippets->typeDefinitionsFp16Snippet;
2562             capabilities += inTypeSnippets->capabilitiesFp16Without16BitStorage;
2563             extensions += inTypeSnippets->extensionsFp16Without16BitStorage;
2564         }
2565         else
2566         {
2567             capabilities += inTypeSnippets->capabilities;
2568             extensions += inTypeSnippets->extensions;
2569         }
2570 
2571         inFp16TypeUsage = inTypeSnippets->loadStoreRequiresShaderFloat16;
2572     }
2573 
2574     map<string, string> specializations;
2575     specializations["behaviorConstants"] = behaviorConstants;
2576     specializations["decorations"]       = behaviorDecorations;
2577     specializations["annotations"]       = annotations + specOpData.annotations;
2578     specializations["types"]             = types + specOpData.types;
2579     specializations["io_definitions"]    = ioDefinitions;
2580     specializations["variables"]         = specOpData.variables;
2581     specializations["functions"]         = specOpData.functions;
2582     specializations["save_result"] =
2583         (outFp16WithoutStorage ? outTypeSnippets->storeResultsFp16Snippet : outTypeSnippets->storeResultsSnippet);
2584     specializations["arguments"] = specOpData.arguments;
2585     specializations["commands"]  = specOpData.commands;
2586 
2587     // Build constants. They are only needed sometimes.
2588     const FloatStatementUsageFlags argsAnyFloatConstMask =
2589         B_STATEMENT_USAGE_ARGS_CONST_FLOAT | B_STATEMENT_USAGE_ARGS_CONST_FP16 | B_STATEMENT_USAGE_ARGS_CONST_FP32 |
2590         B_STATEMENT_USAGE_ARGS_CONST_FP64;
2591     const bool argsUseFPConstants = (specOpData.argumentsUsesFloatConstant & argsAnyFloatConstMask) != 0;
2592     const FloatStatementUsageFlags commandsAnyFloatConstMask =
2593         B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_CONST_FP16 |
2594         B_STATEMENT_USAGE_COMMANDS_CONST_FP32 | B_STATEMENT_USAGE_COMMANDS_CONST_FP64;
2595     const bool commandsUseFPConstants = (testCaseInfo.operation.statementUsageFlags & commandsAnyFloatConstMask) != 0;
2596     const bool needConstants          = argsUseFPConstants || commandsUseFPConstants;
2597     const FloatStatementUsageFlags constsFloatTypeMask =
2598         B_STATEMENT_USAGE_CONSTS_TYPE_FLOAT | B_STATEMENT_USAGE_CONSTS_TYPE_FP16;
2599     const bool constsUsesFP16Type             = (testCaseInfo.operation.statementUsageFlags & constsFloatTypeMask) != 0;
2600     const bool loadStoreRequiresShaderFloat16 = inFp16TypeUsage || outFp16TypeUsage;
2601     const bool usesFP16Constants              = constsUsesFP16Type || (needConstants && loadStoreRequiresShaderFloat16);
2602 
2603     specializations["constants"] = "";
2604     if (needConstants || outFp16WithoutStorage)
2605     {
2606         specializations["constants"] = constants;
2607     }
2608     specializations["constants"] += specOpData.constants;
2609 
2610     // check which format features are needed
2611     bool float16FeatureRequired = (outFloatType == FP16) || (inFloatType == FP16);
2612     bool float64FeatureRequired = (outFloatType == FP64) || (inFloatType == FP64);
2613 
2614     // Determine required capabilities.
2615     bool float16CapabilityAlreadyAdded = inFp16WithoutStorage || outFp16WithoutStorage;
2616     if ((testOperation.floatUsage == FLOAT_ARITHMETIC && float16FeatureRequired && !float16CapabilityAlreadyAdded) ||
2617         usesFP16Constants)
2618     {
2619         capabilities += "OpCapability Float16\n";
2620     }
2621 
2622     if (testCase.requireRte)
2623     {
2624         extensions += "OpExtension \"SPV_KHR_float_controls\"\n";
2625         capabilities += "OpCapability RoundingModeRTE\n";
2626         behaviorExecutionMode += "OpExecutionMode %main RoundingModeRTE " + inTypeSnippets->bitWidth + "\n";
2627     }
2628 
2629     specializations["execution_mode"] = behaviorExecutionMode;
2630     specializations["extensions"]     = extensions;
2631     specializations["capabilities"]   = capabilities;
2632 
2633     // specialize shader
2634     const string shaderCode = m_operationShaderTemplate.specialize(specializations);
2635 
2636     // construct input and output buffers of proper types
2637     TypeValuesSP inTypeValues  = m_typeData.at(inFloatType).values;
2638     TypeValuesSP outTypeValues = m_typeData.at(outFloatType).values;
2639     BufferSp inBufferSp        = inTypeValues->constructInputBuffer(testCase.input);
2640     BufferSp outBufferSp       = outTypeValues->constructOutputBuffer(testCase.expectedOutput);
2641     csSpec.inputs.push_back(Resource(inBufferSp, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2642     csSpec.outputs.push_back(Resource(outBufferSp));
2643 
2644     csSpec.assembly      = shaderCode;
2645     csSpec.numWorkGroups = IVec3(1, 1, 1);
2646     csSpec.verifyIO      = checkFloatsLUT[outFloatType];
2647 
2648     csSpec.spirvVersion                                       = SPIRV_VERSION_1_2;
2649     csSpec.requestedVulkanFeatures.coreFeatures.shaderFloat64 = float64FeatureRequired;
2650     csSpec.requestedVulkanFeatures.ext16BitStorage.storageBuffer16BitAccess =
2651         float16FeatureRequired && !testCase.fp16Without16BitStorage;
2652     csSpec.requestedVulkanFeatures.extFloat16Int8.shaderFloat16 =
2653         float16CapabilityAlreadyAdded || usesFP16Constants ||
2654         (float16FeatureRequired && !testCase.fp16Without16BitStorage && testOperation.floatUsage == FLOAT_ARITHMETIC);
2655     csSpec.requestedVulkanFeatures.extFloatControls2.shaderFloatControls2 = true;
2656 
2657     // Float controls 2 still requires that the original float controls properties are supported
2658     FillFloatControlsProperties(csSpec.requestedVulkanFeatures.floatControlsProperties, testCase, inFloatType);
2659 }
2660 
getGraphicsShaderCode(vk::SourceCollections & dst,InstanceContext context)2661 void getGraphicsShaderCode(vk::SourceCollections &dst, InstanceContext context)
2662 {
2663     // this function is used only by GraphicsTestGroupBuilder but it couldn't
2664     // be implemented as a method because of how addFunctionCaseWithPrograms
2665     // was implemented
2666 
2667     SpirvVersion targetSpirvVersion = context.resources.spirvVersion;
2668     const uint32_t vulkanVersion    = dst.usedVulkanVersion;
2669 
2670     static const string vertexTemplate =
2671         "OpCapability Shader\n"
2672         "${vert_capabilities}"
2673 
2674         "OpExtension \"SPV_KHR_float_controls2\"\n"
2675         "${vert_extensions}"
2676 
2677         "%std450            = OpExtInstImport \"GLSL.std.450\"\n"
2678         "OpMemoryModel Logical GLSL450\n"
2679         "OpEntryPoint Vertex %main \"main\" %BP_stream %BP_position %BP_color %BP_gl_VertexIndex %BP_gl_InstanceIndex "
2680         "%BP_vertex_color %BP_vertex_result \n"
2681         "${vert_execution_mode}"
2682 
2683         "OpMemberDecorate %BP_gl_PerVertex 0 BuiltIn Position\n"
2684         "OpMemberDecorate %BP_gl_PerVertex 1 BuiltIn PointSize\n"
2685         "OpMemberDecorate %BP_gl_PerVertex 2 BuiltIn ClipDistance\n"
2686         "OpMemberDecorate %BP_gl_PerVertex 3 BuiltIn CullDistance\n"
2687         "OpDecorate %BP_gl_PerVertex Block\n"
2688         "OpDecorate %BP_position Location 0\n"
2689         "OpDecorate %BP_color Location 1\n"
2690         "OpDecorate %BP_vertex_color Location 1\n"
2691         "OpDecorate %BP_vertex_result Location 2\n"
2692         "OpDecorate %BP_vertex_result Flat\n"
2693         "OpDecorate %BP_gl_VertexIndex BuiltIn VertexIndex\n"
2694         "OpDecorate %BP_gl_InstanceIndex BuiltIn InstanceIndex\n"
2695         "${vert_decorations}"
2696 
2697         // some tests require additional annotations
2698         "${vert_annotations}"
2699 
2700         // types required by most of tests
2701         "%type_void            = OpTypeVoid\n"
2702         "%type_voidf           = OpTypeFunction %type_void\n"
2703         "%type_bool            = OpTypeBool\n"
2704         "%type_i32             = OpTypeInt 32 1\n"
2705         "%type_u32             = OpTypeInt 32 0\n"
2706         "%type_u32_vec2        = OpTypeVector %type_u32 2\n"
2707         "%type_i32_iptr        = OpTypePointer Input %type_i32\n"
2708         "%type_i32_optr        = OpTypePointer Output %type_i32\n"
2709         "%type_i32_fptr        = OpTypePointer Function %type_i32\n"
2710 
2711         // constants required by most of tests
2712         "%c_i32_0              = OpConstant %type_i32 0\n"
2713         "%c_i32_1              = OpConstant %type_i32 1\n"
2714         "%c_i32_2              = OpConstant %type_i32 2\n"
2715         "%c_u32_1              = OpConstant %type_u32 1\n"
2716 
2717         // if input float type has different width then output then
2718         // both types are defined here along with all types derived from
2719         // them that are commonly used by tests; some tests also define
2720         // their own types (those that are needed just by this single test)
2721         "${vert_types}"
2722 
2723         // SSBO is not universally supported for storing
2724         // data in vertex stages - it is onle read here
2725         "${vert_io_definitions}"
2726 
2727         "%BP_gl_PerVertex      = OpTypeStruct %type_f32_vec4 %type_f32 %type_f32_arr_1 %type_f32_arr_1\n"
2728         "%BP_gl_PerVertex_optr = OpTypePointer Output %BP_gl_PerVertex\n"
2729         "%BP_stream            = OpVariable %BP_gl_PerVertex_optr Output\n"
2730         "%BP_position          = OpVariable %type_f32_vec4_iptr Input\n"
2731         "%BP_color             = OpVariable %type_f32_vec4_iptr Input\n"
2732         "%BP_gl_VertexIndex    = OpVariable %type_i32_iptr Input\n"
2733         "%BP_gl_InstanceIndex  = OpVariable %type_i32_iptr Input\n"
2734         "%BP_vertex_color      = OpVariable %type_f32_vec4_optr Output\n"
2735 
2736         // set of default constants per float type is placed here,
2737         // operation tests can also define additional constants.
2738         "${vert_constants}"
2739         "${behaviorConstants}"
2740 
2741         // O_RETURN_VAL defines function here and because
2742         // of that this token needs to be directly before main function.
2743         "${vert_functions}"
2744 
2745         "%main                 = OpFunction %type_void None %type_voidf\n"
2746         "%label                = OpLabel\n"
2747 
2748         "${vert_variables}"
2749 
2750         "%position             = OpLoad %type_f32_vec4 %BP_position\n"
2751         "%gl_pos               = OpAccessChain %type_f32_vec4_optr %BP_stream %c_i32_0\n"
2752         "OpStore %gl_pos %position\n"
2753         "%color                = OpLoad %type_f32_vec4 %BP_color\n"
2754         "OpStore %BP_vertex_color %color\n"
2755 
2756         // this token is filled only when vertex stage is tested;
2757         // depending on test case arguments are either read from input ssbo
2758         // or generated in spir-v code - in later case ssbo is not used
2759         "${vert_arguments}"
2760 
2761         // when vertex shader is tested then test operations are performed
2762         // here and passed to fragment stage; if fragment stage ts tested
2763         // then ${comands} and ${vert_process_result} are rplaced with nop
2764         "${vert_commands}"
2765 
2766         "${vert_process_result}"
2767 
2768         "OpReturn\n"
2769         "OpFunctionEnd\n";
2770 
2771     static const string fragmentTemplate =
2772         "OpCapability Shader\n"
2773         "${frag_capabilities}"
2774 
2775         "OpExtension \"SPV_KHR_float_controls2\"\n"
2776         "${frag_extensions}"
2777 
2778         "%std450            = OpExtInstImport \"GLSL.std.450\"\n"
2779         "OpMemoryModel Logical GLSL450\n"
2780         "OpEntryPoint Fragment %main \"main\" %BP_vertex_color %BP_vertex_result %BP_fragColor %BP_gl_FragCoord \n"
2781         "OpExecutionMode %main OriginUpperLeft\n"
2782         "${frag_execution_mode}"
2783 
2784         "OpDecorate %BP_fragColor Location 0\n"
2785         "OpDecorate %BP_vertex_color Location 1\n"
2786         "OpDecorate %BP_vertex_result Location 2\n"
2787         "OpDecorate %BP_vertex_result Flat\n"
2788         "OpDecorate %BP_gl_FragCoord BuiltIn FragCoord\n"
2789         "${frag_decorations}"
2790 
2791         // some tests require additional annotations
2792         "${frag_annotations}"
2793 
2794         // types required by most of tests
2795         "%type_void            = OpTypeVoid\n"
2796         "%type_voidf           = OpTypeFunction %type_void\n"
2797         "%type_bool            = OpTypeBool\n"
2798         "%type_i32             = OpTypeInt 32 1\n"
2799         "%type_u32             = OpTypeInt 32 0\n"
2800         "%type_u32_vec2        = OpTypeVector %type_u32 2\n"
2801         "%type_i32_iptr        = OpTypePointer Input %type_i32\n"
2802         "%type_i32_optr        = OpTypePointer Output %type_i32\n"
2803         "%type_i32_fptr        = OpTypePointer Function %type_i32\n"
2804 
2805         // constants required by most of tests
2806         "%c_i32_0              = OpConstant %type_i32 0\n"
2807         "%c_i32_1              = OpConstant %type_i32 1\n"
2808         "%c_i32_2              = OpConstant %type_i32 2\n"
2809         "%c_u32_1              = OpConstant %type_u32 1\n"
2810 
2811         // if input float type has different width then output then
2812         // both types are defined here along with all types derived from
2813         // them that are commonly used by tests; some tests also define
2814         // their own types (those that are needed just by this single test)
2815         "${frag_types}"
2816 
2817         "%BP_gl_FragCoord      = OpVariable %type_f32_vec4_iptr Input\n"
2818         "%BP_vertex_color      = OpVariable %type_f32_vec4_iptr Input\n"
2819         "%BP_fragColor         = OpVariable %type_f32_vec4_optr Output\n"
2820 
2821         // SSBO definitions
2822         "${frag_io_definitions}"
2823 
2824         // set of default constants per float type is placed here,
2825         // operation tests can also define additional constants.
2826         "${frag_constants}"
2827         "${behaviorConstants}"
2828 
2829         // O_RETURN_VAL defines function here and because
2830         // of that this token needs to be directly before main function.
2831         "${frag_functions}"
2832 
2833         "%main                 = OpFunction %type_void None %type_voidf\n"
2834         "%label                = OpLabel\n"
2835 
2836         "${frag_variables}"
2837 
2838         // just pass vertex color - rendered image is not important in our case
2839         "%vertex_color         = OpLoad %type_f32_vec4 %BP_vertex_color\n"
2840         "OpStore %BP_fragColor %vertex_color\n"
2841 
2842         // this token is filled only when fragment stage is tested;
2843         // depending on test case arguments are either read from input ssbo or
2844         // generated in spir-v code - in later case ssbo is used only for output
2845         "${frag_arguments}"
2846 
2847         // when fragment shader is tested then test operations are performed
2848         // here and saved to ssbo; if vertex stage was tested then its
2849         // result is just saved to ssbo here
2850         "${frag_commands}"
2851         "${frag_process_result}"
2852 
2853         "OpReturn\n"
2854         "OpFunctionEnd\n";
2855 
2856     dst.spirvAsmSources.add("vert", DE_NULL) << StringTemplate(vertexTemplate).specialize(context.testCodeFragments)
2857                                              << SpirVAsmBuildOptions(vulkanVersion, targetSpirvVersion);
2858     dst.spirvAsmSources.add("frag", DE_NULL) << StringTemplate(fragmentTemplate).specialize(context.testCodeFragments)
2859                                              << SpirVAsmBuildOptions(vulkanVersion, targetSpirvVersion);
2860 }
2861 
2862 // GraphicsTestGroupBuilder iterates over all test cases and creates test for both
2863 // vertex and fragment stages. As in most spirv-assembly tests, tests here are also
2864 // executed using functionality defined in vktSpvAsmGraphicsShaderTestUtil.cpp but
2865 // because one of requirements during development was that SSBO wont be used in
2866 // vertex stage we couldn't use createTestForStage functions - we need a custom
2867 // version for both vertex and fragmen shaders at the same time. This was required
2868 // as we needed to pass result from vertex stage to fragment stage where it could
2869 // be saved to ssbo. To achieve that InstanceContext is created manually in
2870 // createInstanceContext method.
2871 class GraphicsTestGroupBuilder : public TestGroupBuilderBase
2872 {
2873 public:
2874     void init();
2875 
2876     void createOperationTests(TestCaseGroup *parentGroup, const char *groupName, FloatType floatType,
2877                               bool argumentsFromInput) override;
2878 
2879 protected:
2880     InstanceContext createInstanceContext(const OperationTestCaseInfo &testCaseInfo) const;
2881 
2882 private:
2883     TestCasesBuilder m_testCaseBuilder;
2884 };
2885 
init()2886 void GraphicsTestGroupBuilder::init()
2887 {
2888     m_testCaseBuilder.init();
2889 }
2890 
createOperationTests(TestCaseGroup * parentGroup,const char * groupName,FloatType floatType,bool argumentsFromInput)2891 void GraphicsTestGroupBuilder::createOperationTests(TestCaseGroup *parentGroup, const char *groupName,
2892                                                     FloatType floatType, bool argumentsFromInput)
2893 {
2894     TestContext &testCtx = parentGroup->getTestContext();
2895     TestCaseGroup *group = new TestCaseGroup(testCtx, groupName, "");
2896     parentGroup->addChild(group);
2897 
2898     // create test cases for vertex stage
2899     TestCaseVect testCases;
2900     m_testCaseBuilder.build(testCases, m_typeData[floatType].testResults);
2901 
2902     for (auto &testCase : testCases)
2903     {
2904         // skip cases with undefined output
2905         if (testCase.expectedOutput == V_UNUSED)
2906             continue;
2907 
2908         VkShaderStageFlagBits stages[] = {VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_STAGE_FRAGMENT_BIT};
2909         string stageNames[]            = {"_vert", "_frag"};
2910         for (int i = 0; i < DE_LENGTH_OF_ARRAY(stages); i++)
2911         {
2912             OperationTestCaseInfo testCaseInfo = {floatType, argumentsFromInput, stages[i],
2913                                                   m_testCaseBuilder.getOperation(testCase.operationId), testCase};
2914 
2915             InstanceContext ctxVertex = createInstanceContext(testCaseInfo);
2916             string testName           = replace(testCase.baseName, "op", testCaseInfo.operation.name);
2917             addFunctionCaseWithPrograms<InstanceContext>(group, testName + stageNames[i], getGraphicsShaderCode,
2918                                                          runAndVerifyDefaultPipeline, ctxVertex);
2919         }
2920     }
2921 }
2922 
createInstanceContext(const OperationTestCaseInfo & testCaseInfo) const2923 InstanceContext GraphicsTestGroupBuilder::createInstanceContext(const OperationTestCaseInfo &testCaseInfo) const
2924 {
2925     // LUT storing functions used to verify test results
2926     const VerifyIOFunc checkFloatsLUT[] = {checkFloats<Float16, deFloat16>, checkFloats<Float32, float>,
2927                                            checkFloats<Float64, double>};
2928 
2929     // 32-bit float types are always needed for standard operations on color
2930     // if tested operation does not require fp32 for either input or output
2931     // then this minimal type definitions must be appended to types section
2932     const string f32TypeMinimalRequired = "%type_f32             = OpTypeFloat 32\n"
2933                                           "%type_f32_arr_1       = OpTypeArray %type_f32 %c_i32_1\n"
2934                                           "%type_f32_iptr        = OpTypePointer Input %type_f32\n"
2935                                           "%type_f32_optr        = OpTypePointer Output %type_f32\n"
2936                                           "%type_f32_vec4        = OpTypeVector %type_f32 4\n"
2937                                           "%type_f32_vec4_iptr   = OpTypePointer Input %type_f32_vec4\n"
2938                                           "%type_f32_vec4_optr   = OpTypePointer Output %type_f32_vec4\n";
2939 
2940     const Operation &testOperation    = testCaseInfo.operation;
2941     const OperationTestCase &testCase = testCaseInfo.testCase;
2942     FloatType outFloatType            = testCaseInfo.outFloatType;
2943     VkShaderStageFlagBits testedStage = testCaseInfo.testedStage;
2944 
2945     DE_ASSERT((testedStage == VK_SHADER_STAGE_VERTEX_BIT) || (testedStage == VK_SHADER_STAGE_FRAGMENT_BIT));
2946 
2947     SpecializedOperation specOpData;
2948     specializeOperation(testCaseInfo, specOpData);
2949 
2950     TypeSnippetsSP inTypeSnippets  = specOpData.inTypeSnippets;
2951     TypeSnippetsSP outTypeSnippets = specOpData.outTypeSnippets;
2952     FloatType inFloatType          = specOpData.inFloatType;
2953 
2954     bool outFp16WithoutStorage = (outFloatType == FP16) && testCase.fp16Without16BitStorage;
2955     bool inFp16WithoutStorage  = (inFloatType == FP16) && testCase.fp16Without16BitStorage;
2956 
2957     // There may be several reasons why we need the shaderFloat16 Vulkan feature.
2958     bool needsShaderFloat16 = inFp16WithoutStorage || outFp16WithoutStorage;
2959     // There are some weird cases where we need the constants, but would otherwise drop them.
2960     bool needsSpecialConstants = false;
2961 
2962     // UnpackHalf2x16 is a corner case - it returns two 32-bit floats but
2963     // internaly operates on fp16 and this type should be used by float controls
2964     string inFloatWidthForCaps = inTypeSnippets->bitWidth;
2965     string behaviorCapability;
2966     string behaviorExecutionMode;
2967     string behaviorDecorations;
2968     string behaviorConstants;
2969     getBehaviorCapabilityAndExecutionModeDecoration(testCase.behaviorFlagsExecMode, testCase.behaviorFlagsDecoration,
2970                                                     testCase.useDecorationFlags, testOperation.IDsToDecorate,
2971                                                     inFloatWidthForCaps, behaviorCapability, behaviorExecutionMode,
2972                                                     behaviorDecorations, behaviorConstants);
2973 
2974     // check which format features are needed
2975     bool float16FeatureRequired = (inFloatType == FP16) || (outFloatType == FP16);
2976     bool float64FeatureRequired = (inFloatType == FP64) || (outFloatType == FP64);
2977 
2978     string vertExecutionMode;
2979     string fragExecutionMode;
2980     string vertCapabilities;
2981     string fragCapabilities;
2982     string vertExtensions;
2983     string fragExtensions;
2984     string vertAnnotations;
2985     string fragAnnotations;
2986     string vertTypes;
2987     string fragTypes;
2988     string vertConstants;
2989     string fragConstants;
2990     string vertFunctions;
2991     string fragFunctions;
2992     string vertIODefinitions;
2993     string fragIODefinitions;
2994     string vertArguments;
2995     string fragArguments;
2996     string vertVariables;
2997     string fragVariables;
2998     string vertCommands;
2999     string fragCommands;
3000     string vertProcessResult;
3001     string fragProcessResult;
3002 
3003     // check if operation should be executed in vertex stage
3004     if (testedStage == VK_SHADER_STAGE_VERTEX_BIT)
3005     {
3006         vertAnnotations = inTypeSnippets->inputAnnotationsSnippet + inTypeSnippets->typeAnnotationsSnippet;
3007         fragAnnotations = outTypeSnippets->outputAnnotationsSnippet + outTypeSnippets->typeAnnotationsSnippet;
3008         vertFunctions   = specOpData.functions;
3009 
3010         // check if input type is different from tested type (conversion operations)
3011         if (testOperation.isInputTypeRestricted)
3012         {
3013             vertCapabilities = inTypeSnippets->capabilities + outTypeSnippets->capabilities;
3014             fragCapabilities = outTypeSnippets->capabilities;
3015             vertExtensions   = inTypeSnippets->extensions + outTypeSnippets->extensions;
3016             fragExtensions   = outTypeSnippets->extensions;
3017             vertTypes        = inTypeSnippets->typeDefinitionsSnippet + outTypeSnippets->typeDefinitionsSnippet +
3018                         outTypeSnippets->varyingsTypesSnippet;
3019             if (inFp16WithoutStorage)
3020                 vertTypes += inTypeSnippets->typeDefinitionsFp16Snippet;
3021 
3022             fragTypes     = outTypeSnippets->typeDefinitionsSnippet + outTypeSnippets->varyingsTypesSnippet;
3023             vertConstants = inTypeSnippets->constantsDefinitionsSnippet + outTypeSnippets->constantsDefinitionsSnippet;
3024             fragConstants = outTypeSnippets->constantsDefinitionsSnippet;
3025         }
3026         else
3027         {
3028             // input and output types are the same (majority of operations)
3029 
3030             vertCapabilities = outTypeSnippets->capabilities;
3031             fragCapabilities = vertCapabilities;
3032             vertExtensions   = outTypeSnippets->extensions;
3033             fragExtensions   = vertExtensions;
3034             vertTypes        = outTypeSnippets->typeDefinitionsSnippet + outTypeSnippets->varyingsTypesSnippet;
3035             fragTypes        = vertTypes;
3036             vertConstants    = outTypeSnippets->constantsDefinitionsSnippet;
3037             fragConstants    = outTypeSnippets->constantsDefinitionsSnippet;
3038         }
3039 
3040         if (outFloatType != FP32)
3041         {
3042             fragTypes += f32TypeMinimalRequired;
3043             if (inFloatType != FP32)
3044                 vertTypes += f32TypeMinimalRequired;
3045         }
3046 
3047         vertAnnotations += specOpData.annotations;
3048         vertTypes += specOpData.types;
3049         vertConstants += specOpData.constants;
3050 
3051         vertExecutionMode = behaviorExecutionMode;
3052         fragExecutionMode = "";
3053         vertIODefinitions = inTypeSnippets->inputDefinitionsSnippet + outTypeSnippets->outputVaryingsSnippet;
3054         fragIODefinitions = outTypeSnippets->inputVaryingsSnippet + outTypeSnippets->outputDefinitionsSnippet;
3055         vertArguments     = specOpData.arguments;
3056         fragArguments     = "";
3057         vertVariables     = specOpData.variables;
3058         fragVariables     = "";
3059         vertCommands      = specOpData.commands;
3060         fragCommands      = "";
3061         vertProcessResult = outTypeSnippets->storeVertexResultSnippet;
3062         fragProcessResult = outTypeSnippets->loadVertexResultSnippet + outTypeSnippets->storeResultsSnippet;
3063 
3064         if (inFp16WithoutStorage)
3065         {
3066             vertAnnotations += inTypeSnippets->typeAnnotationsFp16Snippet;
3067             vertIODefinitions = inTypeSnippets->inputDefinitionsFp16Snippet + outTypeSnippets->outputVaryingsSnippet;
3068         }
3069 
3070         if (outFp16WithoutStorage)
3071         {
3072             vertTypes += outTypeSnippets->typeDefinitionsFp16Snippet;
3073             fragTypes += outTypeSnippets->typeDefinitionsFp16Snippet;
3074             fragAnnotations += outTypeSnippets->typeAnnotationsFp16Snippet;
3075             fragIODefinitions = outTypeSnippets->inputVaryingsSnippet + outTypeSnippets->outputDefinitionsFp16Snippet;
3076             fragProcessResult = outTypeSnippets->loadVertexResultSnippet + outTypeSnippets->storeResultsFp16Snippet;
3077         }
3078 
3079         needsShaderFloat16 |= outTypeSnippets->loadStoreRequiresShaderFloat16;
3080     }
3081     else // perform test in fragment stage - vertex stage is empty
3082     {
3083         fragFunctions = specOpData.functions;
3084         // check if input type is different from tested type
3085         if (testOperation.isInputTypeRestricted)
3086         {
3087             fragAnnotations = inTypeSnippets->inputAnnotationsSnippet + inTypeSnippets->typeAnnotationsSnippet +
3088                               outTypeSnippets->outputAnnotationsSnippet + outTypeSnippets->typeAnnotationsSnippet;
3089             fragCapabilities = (inFp16WithoutStorage ? inTypeSnippets->capabilitiesFp16Without16BitStorage :
3090                                                        inTypeSnippets->capabilities) +
3091                                (outFp16WithoutStorage ? outTypeSnippets->capabilitiesFp16Without16BitStorage :
3092                                                         outTypeSnippets->capabilities);
3093             fragExtensions = (inFp16WithoutStorage ? inTypeSnippets->extensionsFp16Without16BitStorage :
3094                                                      inTypeSnippets->extensions) +
3095                              (outFp16WithoutStorage ? outTypeSnippets->extensionsFp16Without16BitStorage :
3096                                                       outTypeSnippets->extensions);
3097             fragTypes     = inTypeSnippets->typeDefinitionsSnippet + outTypeSnippets->typeDefinitionsSnippet;
3098             fragConstants = inTypeSnippets->constantsDefinitionsSnippet + outTypeSnippets->constantsDefinitionsSnippet;
3099         }
3100         else
3101         {
3102             // input and output types are the same
3103 
3104             fragAnnotations = inTypeSnippets->inputAnnotationsSnippet + inTypeSnippets->typeAnnotationsSnippet +
3105                               outTypeSnippets->outputAnnotationsSnippet;
3106             fragCapabilities = (outFp16WithoutStorage ? outTypeSnippets->capabilitiesFp16Without16BitStorage :
3107                                                         outTypeSnippets->capabilities);
3108             fragExtensions   = (outFp16WithoutStorage ? outTypeSnippets->extensionsFp16Without16BitStorage :
3109                                                         outTypeSnippets->extensions);
3110             fragTypes        = outTypeSnippets->typeDefinitionsSnippet;
3111             fragConstants    = outTypeSnippets->constantsDefinitionsSnippet;
3112         }
3113 
3114         // varying is not used but it needs to be specified so lets use type_i32 for it
3115         string unusedVertVarying = "%BP_vertex_result     = OpVariable %type_i32_optr Output\n";
3116         string unusedFragVarying = "%BP_vertex_result     = OpVariable %type_i32_iptr Input\n";
3117 
3118         vertCapabilities = "";
3119         vertExtensions   = "";
3120         vertAnnotations  = "OpDecorate %type_f32_arr_1 ArrayStride 4\n";
3121         vertTypes        = f32TypeMinimalRequired;
3122         vertConstants    = "";
3123 
3124         if ((outFloatType != FP32) && (inFloatType != FP32))
3125             fragTypes += f32TypeMinimalRequired;
3126 
3127         fragAnnotations += specOpData.annotations;
3128         fragTypes += specOpData.types;
3129         fragConstants += specOpData.constants;
3130 
3131         vertExecutionMode = "";
3132         fragExecutionMode = behaviorExecutionMode;
3133         vertIODefinitions = unusedVertVarying;
3134         fragIODefinitions = unusedFragVarying;
3135 
3136         vertArguments     = "";
3137         fragArguments     = specOpData.arguments;
3138         vertVariables     = "";
3139         fragVariables     = specOpData.variables;
3140         vertCommands      = "";
3141         fragCommands      = specOpData.commands;
3142         vertProcessResult = "";
3143         fragProcessResult = outTypeSnippets->storeResultsSnippet;
3144 
3145         if (inFp16WithoutStorage)
3146         {
3147             fragAnnotations += inTypeSnippets->typeAnnotationsFp16Snippet;
3148             if (testOperation.isInputTypeRestricted)
3149             {
3150                 fragTypes += inTypeSnippets->typeDefinitionsFp16Snippet;
3151             }
3152             fragIODefinitions += inTypeSnippets->inputDefinitionsFp16Snippet;
3153         }
3154         else
3155             fragIODefinitions += inTypeSnippets->inputDefinitionsSnippet;
3156 
3157         if (outFp16WithoutStorage)
3158         {
3159             if (testOperation.isInputTypeRestricted)
3160             {
3161                 fragAnnotations += outTypeSnippets->typeAnnotationsFp16Snippet;
3162             }
3163             fragTypes += outTypeSnippets->typeDefinitionsFp16Snippet;
3164             fragIODefinitions += outTypeSnippets->outputDefinitionsFp16Snippet;
3165             fragProcessResult = outTypeSnippets->storeResultsFp16Snippet;
3166         }
3167         else
3168             fragIODefinitions += outTypeSnippets->outputDefinitionsSnippet;
3169 
3170         if (!testCaseInfo.argumentsFromInput)
3171         {
3172             switch (testCaseInfo.testCase.operationId)
3173             {
3174             case OID_CONV_FROM_FP32:
3175             case OID_CONV_FROM_FP64:
3176                 needsSpecialConstants = true;
3177                 break;
3178             default:
3179                 break;
3180             }
3181         }
3182     }
3183 
3184     // Another reason we need shaderFloat16 is the executable instructions uses fp16
3185     // in a way not supported by the 16bit storage extension.
3186     needsShaderFloat16 |= float16FeatureRequired && testOperation.floatUsage == FLOAT_ARITHMETIC;
3187 
3188     // Constants are only needed sometimes.  Drop them in the fp16 case if the code doesn't need
3189     // them, and if we don't otherwise need shaderFloat16.
3190     bool needsFP16Constants = needsShaderFloat16 || needsSpecialConstants || outFp16WithoutStorage;
3191 
3192     if (!needsFP16Constants && float16FeatureRequired)
3193     {
3194         // Check various code fragments
3195         const FloatStatementUsageFlags commandsFloatConstMask =
3196             B_STATEMENT_USAGE_COMMANDS_CONST_FLOAT | B_STATEMENT_USAGE_COMMANDS_CONST_FP16;
3197         const bool commandsUsesFloatConstant =
3198             (testCaseInfo.operation.statementUsageFlags & commandsFloatConstMask) != 0;
3199         const FloatStatementUsageFlags argumentsFloatConstMask =
3200             B_STATEMENT_USAGE_ARGS_CONST_FLOAT | B_STATEMENT_USAGE_ARGS_CONST_FP16;
3201         const bool argumentsUsesFloatConstant = (specOpData.argumentsUsesFloatConstant & argumentsFloatConstMask) != 0;
3202         bool hasFP16ConstsInCommandsOrArguments = commandsUsesFloatConstant || argumentsUsesFloatConstant;
3203 
3204         needsFP16Constants |= hasFP16ConstsInCommandsOrArguments;
3205 
3206         if (!needsFP16Constants)
3207         {
3208             vertConstants = "";
3209             fragConstants = "";
3210         }
3211     }
3212     needsShaderFloat16 |= needsFP16Constants;
3213 
3214     if (needsShaderFloat16)
3215     {
3216         vertCapabilities += "OpCapability Float16\n";
3217         fragCapabilities += "OpCapability Float16\n";
3218     }
3219 
3220     if (testCase.requireRte)
3221     {
3222         vertExtensions += "OpExtension \"SPV_KHR_float_controls\"\n";
3223         vertCapabilities += "OpCapability RoundingModeRTE\n";
3224         vertExecutionMode += "OpExecutionMode %main RoundingModeRTE " + inTypeSnippets->bitWidth + "\n";
3225 
3226         fragExtensions += "OpExtension \"SPV_KHR_float_controls\"\n";
3227         fragCapabilities += "OpCapability RoundingModeRTE\n";
3228         fragExecutionMode += "OpExecutionMode %main RoundingModeRTE " + inTypeSnippets->bitWidth + "\n";
3229     }
3230 
3231     map<string, string> specializations;
3232     if (testCaseInfo.testedStage == VK_SHADER_STAGE_VERTEX_BIT)
3233     {
3234         vertCapabilities += behaviorCapability;
3235 
3236         specializations["vert_decorations"] = behaviorDecorations;
3237         specializations["frag_decorations"] = "";
3238     }
3239     else
3240     {
3241         fragCapabilities += behaviorCapability;
3242 
3243         specializations["vert_decorations"] = "";
3244         specializations["frag_decorations"] = behaviorDecorations;
3245     }
3246     specializations["behaviorConstants"] = behaviorConstants;
3247     specializations["vert_capabilities"] = vertCapabilities, specializations["vert_extensions"] = vertExtensions;
3248     specializations["vert_execution_mode"] = vertExecutionMode;
3249     specializations["vert_annotations"]    = vertAnnotations;
3250     specializations["vert_types"]          = vertTypes;
3251     specializations["vert_constants"]      = vertConstants;
3252     specializations["vert_io_definitions"] = vertIODefinitions;
3253     specializations["vert_arguments"]      = vertArguments;
3254     specializations["vert_variables"]      = vertVariables;
3255     specializations["vert_functions"]      = vertFunctions;
3256     specializations["vert_commands"]       = vertCommands;
3257     specializations["vert_process_result"] = vertProcessResult;
3258     specializations["frag_capabilities"]   = fragCapabilities;
3259     specializations["frag_extensions"]     = fragExtensions;
3260     specializations["frag_execution_mode"] = fragExecutionMode;
3261     specializations["frag_annotations"]    = fragAnnotations;
3262     specializations["frag_types"]          = fragTypes;
3263     specializations["frag_constants"]      = fragConstants;
3264     specializations["frag_functions"]      = fragFunctions;
3265     specializations["frag_io_definitions"] = fragIODefinitions;
3266     specializations["frag_arguments"]      = fragArguments;
3267     specializations["frag_variables"]      = fragVariables;
3268     specializations["frag_commands"]       = fragCommands;
3269     specializations["frag_process_result"] = fragProcessResult;
3270 
3271     // colors are not used by the test - input is passed via uniform buffer
3272     RGBA defaultColors[4] = {RGBA::white(), RGBA::red(), RGBA::green(), RGBA::blue()};
3273 
3274     // construct input and output buffers of proper types
3275     TypeValuesSP inTypeValues  = m_typeData.at(inFloatType).values;
3276     TypeValuesSP outTypeValues = m_typeData.at(outFloatType).values;
3277     BufferSp inBufferSp        = inTypeValues->constructInputBuffer(testCase.input);
3278     BufferSp outBufferSp       = outTypeValues->constructOutputBuffer(testCase.expectedOutput);
3279 
3280     vkt::SpirVAssembly::GraphicsResources resources;
3281     resources.inputs.push_back(Resource(inBufferSp, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
3282     resources.outputs.push_back(Resource(outBufferSp, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
3283     resources.verifyIO = checkFloatsLUT[outFloatType];
3284 
3285     StageToSpecConstantMap noSpecConstants;
3286     PushConstants noPushConstants;
3287     GraphicsInterfaces noInterfaces;
3288 
3289     VulkanFeatures vulkanFeatures;
3290     vulkanFeatures.coreFeatures.shaderFloat64             = float64FeatureRequired;
3291     vulkanFeatures.coreFeatures.fragmentStoresAndAtomics  = true;
3292     vulkanFeatures.extFloatControls2.shaderFloatControls2 = true;
3293     vulkanFeatures.extFloat16Int8.shaderFloat16           = needsShaderFloat16;
3294     vulkanFeatures.ext16BitStorage.storageBuffer16BitAccess =
3295         float16FeatureRequired && !testCase.fp16Without16BitStorage;
3296 
3297     // Float controls 2 still requires that the original float controls properties are supported
3298     FillFloatControlsProperties(vulkanFeatures.floatControlsProperties, testCase, inFloatType);
3299 
3300     InstanceContext ctx(defaultColors, defaultColors, specializations, noSpecConstants, noPushConstants, resources,
3301                         noInterfaces, {}, vulkanFeatures, testedStage);
3302 
3303     ctx.moduleMap["vert"].push_back(std::make_pair("main", VK_SHADER_STAGE_VERTEX_BIT));
3304     ctx.moduleMap["frag"].push_back(std::make_pair("main", VK_SHADER_STAGE_FRAGMENT_BIT));
3305 
3306     ctx.requiredStages = static_cast<VkShaderStageFlagBits>(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
3307     ctx.failResult     = QP_TEST_RESULT_FAIL;
3308     ctx.failMessageTemplate = "Output doesn't match with expected";
3309 
3310     ctx.resources.spirvVersion = SPIRV_VERSION_1_2;
3311 
3312     return ctx;
3313 }
3314 
3315 } // namespace
3316 
createFloatControls2TestGroup(TestContext & testCtx,TestGroupBuilderBase * groupBuilder)3317 tcu::TestCaseGroup *createFloatControls2TestGroup(TestContext &testCtx, TestGroupBuilderBase *groupBuilder)
3318 {
3319     de::MovePtr<TestCaseGroup> group(
3320         new TestCaseGroup(testCtx, "float_controls2", "Tests for VK_KHR_shader_float_controls2 extension"));
3321 
3322     struct TestGroup
3323     {
3324         FloatType floatType;
3325         const char *groupName;
3326     };
3327     TestGroup testGroups[] = {
3328         {FP16, "fp16"},
3329         {FP32, "fp32"},
3330         {FP64, "fp64"},
3331     };
3332 
3333     for (int i = 0; i < DE_LENGTH_OF_ARRAY(testGroups); ++i)
3334     {
3335         const TestGroup &testGroup = testGroups[i];
3336         TestCaseGroup *typeGroup   = new TestCaseGroup(testCtx, testGroup.groupName, "");
3337         group->addChild(typeGroup);
3338 
3339         groupBuilder->createOperationTests(typeGroup, "input_args", testGroup.floatType, true);
3340     }
3341 
3342     return group.release();
3343 }
3344 
createFloatControls2ComputeGroup(TestContext & testCtx)3345 tcu::TestCaseGroup *createFloatControls2ComputeGroup(TestContext &testCtx)
3346 {
3347     ComputeTestGroupBuilder computeTestGroupBuilder;
3348     computeTestGroupBuilder.init();
3349 
3350     return createFloatControls2TestGroup(testCtx, &computeTestGroupBuilder);
3351 }
3352 
createFloatControls2GraphicsGroup(TestContext & testCtx)3353 tcu::TestCaseGroup *createFloatControls2GraphicsGroup(TestContext &testCtx)
3354 {
3355     GraphicsTestGroupBuilder graphicsTestGroupBuilder;
3356     graphicsTestGroupBuilder.init();
3357 
3358     return createFloatControls2TestGroup(testCtx, &graphicsTestGroupBuilder);
3359 }
3360 
3361 } // namespace SpirVAssembly
3362 } // namespace vkt
3363