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