1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2015 The Khronos Group Inc.
6 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
7 * Copyright (c) 2016 The Android Open Source Project
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 *
21 *//*!
22 * \file
23 * \brief Common built-in function tests.
24 *//*--------------------------------------------------------------------*/
25
26 #include "vktShaderCommonFunctionTests.hpp"
27 #include "vktShaderExecutor.hpp"
28 #include "vkQueryUtil.hpp"
29 #include "gluContextInfo.hpp"
30 #include "tcuTestLog.hpp"
31 #include "tcuFormatUtil.hpp"
32 #include "tcuFloat.hpp"
33 #include "tcuInterval.hpp"
34 #include "tcuFloatFormat.hpp"
35 #include "tcuVectorUtil.hpp"
36 #include "deRandom.hpp"
37 #include "deMath.h"
38 #include "deString.h"
39 #include "deArrayUtil.hpp"
40 #include "deSharedPtr.hpp"
41 #include <algorithm>
42
43 namespace vkt
44 {
45
46 namespace shaderexecutor
47 {
48
49
50 using std::vector;
51 using std::string;
52 using tcu::TestLog;
53
54 using tcu::Vec2;
55 using tcu::Vec3;
56 using tcu::Vec4;
57 using tcu::IVec2;
58 using tcu::IVec3;
59 using tcu::IVec4;
60
61 namespace
62 {
63
64 // Utilities
65
66 template<typename T, int Size>
67 struct VecArrayAccess
68 {
69 public:
VecArrayAccessvkt::shaderexecutor::__anonf42db1200111::VecArrayAccess70 VecArrayAccess (const void* ptr) : m_array((tcu::Vector<T, Size>*)ptr) {}
~VecArrayAccessvkt::shaderexecutor::__anonf42db1200111::VecArrayAccess71 ~VecArrayAccess (void) {}
72
operator []vkt::shaderexecutor::__anonf42db1200111::VecArrayAccess73 const tcu::Vector<T, Size>& operator[] (size_t offset) const { return m_array[offset]; }
operator []vkt::shaderexecutor::__anonf42db1200111::VecArrayAccess74 tcu::Vector<T, Size>& operator[] (size_t offset) { return m_array[offset]; }
75
76 private:
77 tcu::Vector<T, Size>* m_array;
78 };
79
80 template<typename T, int Size>
fillRandomVectors(de::Random & rnd,const tcu::Vector<T,Size> & minValue,const tcu::Vector<T,Size> & maxValue,void * dst,int numValues,int offset=0)81 static void fillRandomVectors (de::Random& rnd, const tcu::Vector<T, Size>& minValue, const tcu::Vector<T, Size>& maxValue, void* dst, int numValues, int offset = 0)
82 {
83 VecArrayAccess<T, Size> access(dst);
84 for (int ndx = 0; ndx < numValues; ndx++)
85 access[offset + ndx] = tcu::randomVector<T, Size>(rnd, minValue, maxValue);
86 }
87
88 template<typename T>
fillRandomScalars(de::Random & rnd,T minValue,T maxValue,void * dst,int numValues,int offset=0)89 static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0)
90 {
91 T* typedPtr = (T*)dst;
92 for (int ndx = 0; ndx < numValues; ndx++)
93 typedPtr[offset + ndx] = de::randomScalar<T>(rnd, minValue, maxValue);
94 }
95
getUlpDiff(float a,float b)96 inline deUint32 getUlpDiff (float a, float b)
97 {
98 const deUint32 aBits = tcu::Float32(a).bits();
99 const deUint32 bBits = tcu::Float32(b).bits();
100 return aBits > bBits ? aBits - bBits : bBits - aBits;
101 }
102
getUlpDiffIgnoreZeroSign(float a,float b)103 inline deUint32 getUlpDiffIgnoreZeroSign (float a, float b)
104 {
105 if (tcu::Float32(a).isZero())
106 return getUlpDiff(tcu::Float32::construct(tcu::Float32(b).sign(), 0, 0).asFloat(), b);
107 else if (tcu::Float32(b).isZero())
108 return getUlpDiff(a, tcu::Float32::construct(tcu::Float32(a).sign(), 0, 0).asFloat());
109 else
110 return getUlpDiff(a, b);
111 }
112
getMaxUlpDiffFromBits(int numAccurateBits,int numTotalBits)113 inline deUint64 getMaxUlpDiffFromBits (int numAccurateBits, int numTotalBits)
114 {
115 const int numGarbageBits = numTotalBits-numAccurateBits;
116 const deUint64 mask = (1ull<<numGarbageBits)-1ull;
117
118 return mask;
119 }
120
getNumMantissaBits(glu::DataType type)121 static int getNumMantissaBits (glu::DataType type)
122 {
123 DE_ASSERT(glu::isDataTypeFloatOrVec(type) || glu::isDataTypeDoubleOrDVec(type));
124 return (glu::isDataTypeFloatOrVec(type) ? 23 : 52);
125 }
126
getMinMantissaBits(glu::DataType type,glu::Precision precision)127 static int getMinMantissaBits (glu::DataType type, glu::Precision precision)
128 {
129 if (glu::isDataTypeDoubleOrDVec(type))
130 {
131 return tcu::Float64::MANTISSA_BITS;
132 }
133
134 // Float case.
135 const int bits[] =
136 {
137 7, // lowp
138 tcu::Float16::MANTISSA_BITS, // mediump
139 tcu::Float32::MANTISSA_BITS, // highp
140 };
141 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(bits) == glu::PRECISION_LAST);
142 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(bits)));
143 return bits[precision];
144 }
145
getExponentBits(glu::DataType type)146 static int getExponentBits (glu::DataType type)
147 {
148 DE_ASSERT(glu::isDataTypeFloatOrVec(type) || glu::isDataTypeDoubleOrDVec(type));
149 return (glu::isDataTypeFloatOrVec(type) ? static_cast<int>(tcu::Float32::EXPONENT_BITS) : static_cast<int>(tcu::Float64::EXPONENT_BITS));
150 }
151
getExponentMask(int exponentBits)152 static deUint32 getExponentMask (int exponentBits)
153 {
154 DE_ASSERT(exponentBits > 0);
155 return ((1u<<exponentBits) - 1u);
156 }
157
getComponentByteSize(glu::DataType type)158 static int getComponentByteSize (glu::DataType type)
159 {
160 const glu::DataType scalarType = glu::getDataTypeScalarType(type);
161
162 DE_ASSERT( scalarType == glu::TYPE_FLOAT ||
163 scalarType == glu::TYPE_FLOAT16 ||
164 scalarType == glu::TYPE_DOUBLE ||
165 scalarType == glu::TYPE_INT ||
166 scalarType == glu::TYPE_UINT ||
167 scalarType == glu::TYPE_INT8 ||
168 scalarType == glu::TYPE_UINT8 ||
169 scalarType == glu::TYPE_INT16 ||
170 scalarType == glu::TYPE_UINT16 ||
171 scalarType == glu::TYPE_BOOL );
172
173 switch (scalarType)
174 {
175 case glu::TYPE_INT8:
176 case glu::TYPE_UINT8:
177 return 1;
178 case glu::TYPE_INT16:
179 case glu::TYPE_UINT16:
180 case glu::TYPE_FLOAT16:
181 return 2;
182 case glu::TYPE_BOOL:
183 case glu::TYPE_INT:
184 case glu::TYPE_UINT:
185 case glu::TYPE_FLOAT:
186 return 4;
187 case glu::TYPE_DOUBLE:
188 return 8;
189 default:
190 DE_ASSERT(false); break;
191 }
192 // Unreachable.
193 return 0;
194 }
195
getScalarSizes(const vector<Symbol> & symbols)196 static vector<int> getScalarSizes (const vector<Symbol>& symbols)
197 {
198 vector<int> sizes(symbols.size());
199 for (int ndx = 0; ndx < (int)symbols.size(); ++ndx)
200 sizes[ndx] = symbols[ndx].varType.getScalarSize();
201 return sizes;
202 }
203
getComponentByteSizes(const vector<Symbol> & symbols)204 static vector<int> getComponentByteSizes (const vector<Symbol>& symbols)
205 {
206 vector<int> sizes;
207 sizes.reserve(symbols.size());
208 for (const auto& sym : symbols)
209 sizes.push_back(getComponentByteSize(sym.varType.getBasicType()));
210 return sizes;
211 }
212
computeTotalByteSize(const vector<Symbol> & symbols)213 static int computeTotalByteSize (const vector<Symbol>& symbols)
214 {
215 int totalSize = 0;
216 for (const auto& sym : symbols)
217 totalSize += getComponentByteSize(sym.varType.getBasicType()) * sym.varType.getScalarSize();
218 return totalSize;
219 }
220
getInputOutputPointers(const vector<Symbol> & symbols,vector<deUint8> & data,const int numValues)221 static vector<void*> getInputOutputPointers (const vector<Symbol>& symbols, vector<deUint8>& data, const int numValues)
222 {
223 vector<void*> pointers (symbols.size());
224 int curScalarOffset = 0;
225
226 for (int varNdx = 0; varNdx < (int)symbols.size(); ++varNdx)
227 {
228 const Symbol& var = symbols[varNdx];
229 const int scalarSize = var.varType.getScalarSize();
230 const auto componentBytes = getComponentByteSize(var.varType.getBasicType());
231
232 // Uses planar layout as input/output specs do not support strides.
233 pointers[varNdx] = &data[curScalarOffset];
234 curScalarOffset += scalarSize*numValues*componentBytes;
235 }
236
237 DE_ASSERT(curScalarOffset == (int)data.size());
238
239 return pointers;
240 }
241
checkTypeSupport(Context & context,glu::DataType dataType)242 void checkTypeSupport (Context& context, glu::DataType dataType)
243 {
244 if (glu::isDataTypeDoubleOrDVec(dataType))
245 {
246 const auto& vki = context.getInstanceInterface();
247 const auto physicalDevice = context.getPhysicalDevice();
248
249 const auto features = vk::getPhysicalDeviceFeatures(vki, physicalDevice);
250 if (!features.shaderFloat64)
251 TCU_THROW(NotSupportedError, "64-bit floats not supported by the implementation");
252 }
253 }
254
255 // \todo [2013-08-08 pyry] Make generic utility and move to glu?
256
257 struct HexFloat
258 {
259 const float value;
HexFloatvkt::shaderexecutor::__anonf42db1200111::HexFloat260 HexFloat (const float value_) : value(value_) {}
261 };
262
operator <<(std::ostream & str,const HexFloat & v)263 std::ostream& operator<< (std::ostream& str, const HexFloat& v)
264 {
265 return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
266 }
267
268 struct HexDouble
269 {
270 const double value;
HexDoublevkt::shaderexecutor::__anonf42db1200111::HexDouble271 HexDouble (const double value_) : value(value_) {}
272 };
273
operator <<(std::ostream & str,const HexDouble & v)274 std::ostream& operator<< (std::ostream& str, const HexDouble& v)
275 {
276 return str << v.value << " / " << tcu::toHex(tcu::Float64(v.value).bits());
277 }
278
279 struct HexBool
280 {
281 const deUint32 value;
HexBoolvkt::shaderexecutor::__anonf42db1200111::HexBool282 HexBool (const deUint32 value_) : value(value_) {}
283 };
284
operator <<(std::ostream & str,const HexBool & v)285 std::ostream& operator<< (std::ostream& str, const HexBool& v)
286 {
287 return str << (v.value ? "true" : "false") << " / " << tcu::toHex(v.value);
288 }
289
290 struct VarValue
291 {
292 const glu::VarType& type;
293 const void* value;
294
VarValuevkt::shaderexecutor::__anonf42db1200111::VarValue295 VarValue (const glu::VarType& type_, const void* value_) : type(type_), value(value_) {}
296 };
297
operator <<(std::ostream & str,const VarValue & varValue)298 std::ostream& operator<< (std::ostream& str, const VarValue& varValue)
299 {
300 DE_ASSERT(varValue.type.isBasicType());
301
302 const glu::DataType basicType = varValue.type.getBasicType();
303 const glu::DataType scalarType = glu::getDataTypeScalarType(basicType);
304 const int numComponents = glu::getDataTypeScalarSize(basicType);
305
306 if (numComponents > 1)
307 str << glu::getDataTypeName(basicType) << "(";
308
309 for (int compNdx = 0; compNdx < numComponents; compNdx++)
310 {
311 if (compNdx != 0)
312 str << ", ";
313
314 switch (scalarType)
315 {
316 case glu::TYPE_FLOAT: str << HexFloat(((const float*)varValue.value)[compNdx]); break;
317 case glu::TYPE_INT: str << ((const deInt32*)varValue.value)[compNdx]; break;
318 case glu::TYPE_UINT: str << tcu::toHex(((const deUint32*)varValue.value)[compNdx]); break;
319 case glu::TYPE_BOOL: str << HexBool(((const deUint32*)varValue.value)[compNdx]); break;
320 case glu::TYPE_DOUBLE: str << HexDouble(((const double*)varValue.value)[compNdx]); break;
321
322 default:
323 DE_ASSERT(false);
324 }
325 }
326
327 if (numComponents > 1)
328 str << ")";
329
330 return str;
331 }
332
getCommonFuncCaseName(glu::DataType baseType,glu::Precision precision)333 static std::string getCommonFuncCaseName (glu::DataType baseType, glu::Precision precision)
334 {
335 const bool isDouble = glu::isDataTypeDoubleOrDVec(baseType);
336 return string(glu::getDataTypeName(baseType)) + (isDouble ? "" : getPrecisionPostfix(precision)) + "_compute";
337 }
338
339 template<class TestClass>
addFunctionCases(tcu::TestCaseGroup * parent,const char * functionName,const std::vector<glu::DataType> & scalarTypes)340 static void addFunctionCases (tcu::TestCaseGroup* parent, const char* functionName, const std::vector<glu::DataType>& scalarTypes)
341 {
342 tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), functionName, functionName);
343 parent->addChild(group);
344
345 for (const auto scalarType : scalarTypes)
346 {
347 const bool isDouble = glu::isDataTypeDoubleOrDVec(scalarType);
348 const int lowestPrec = (isDouble ? glu::PRECISION_LAST : glu::PRECISION_MEDIUMP);
349 const int highestPrec = (isDouble ? glu::PRECISION_LAST : glu::PRECISION_HIGHP);
350
351 for (int vecSize = 1; vecSize <= 4; vecSize++)
352 {
353 for (int prec = lowestPrec; prec <= highestPrec; prec++)
354 {
355 group->addChild(new TestClass(parent->getTestContext(), glu::DataType(scalarType + vecSize - 1), glu::Precision(prec)));
356 }
357 }
358 }
359 }
360
361 // CommonFunctionCase
362
363 class CommonFunctionCase : public TestCase
364 {
365 public:
366 CommonFunctionCase (tcu::TestContext& testCtx, const char* name, const char* description);
367 ~CommonFunctionCase (void);
initPrograms(vk::SourceCollections & programCollection) const368 virtual void initPrograms (vk::SourceCollections& programCollection) const
369 {
370 generateSources(glu::SHADERTYPE_COMPUTE, m_spec, programCollection);
371 }
372
373 virtual TestInstance* createInstance (Context& context) const = 0;
374
375 protected:
376 CommonFunctionCase (const CommonFunctionCase&);
377 CommonFunctionCase& operator= (const CommonFunctionCase&);
378
379 ShaderSpec m_spec;
380 const int m_numValues;
381 };
382
CommonFunctionCase(tcu::TestContext & testCtx,const char * name,const char * description)383 CommonFunctionCase::CommonFunctionCase (tcu::TestContext& testCtx, const char* name, const char* description)
384 : TestCase (testCtx, name, description)
385 , m_numValues (100)
386 {
387 }
388
~CommonFunctionCase(void)389 CommonFunctionCase::~CommonFunctionCase (void)
390 {
391 }
392
393 // CommonFunctionTestInstance
394
395 class CommonFunctionTestInstance : public TestInstance
396 {
397 public:
CommonFunctionTestInstance(Context & context,const ShaderSpec & spec,int numValues,const char * name)398 CommonFunctionTestInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name)
399 : TestInstance (context)
400 , m_spec (spec)
401 , m_numValues (numValues)
402 , m_name (name)
403 , m_executor (createExecutor(context, glu::SHADERTYPE_COMPUTE, spec))
404 {
405 }
406 virtual tcu::TestStatus iterate (void);
407
408 protected:
409 virtual void getInputValues (int numValues, void* const* values) const = 0;
410 virtual bool compare (const void* const* inputs, const void* const* outputs) = 0;
411
412 const ShaderSpec m_spec;
413 const int m_numValues;
414
415 // \todo [2017-03-07 pyry] Hack used to generate seeds for test cases - get rid of this.
416 const char* m_name;
417
418 std::ostringstream m_failMsg; //!< Comparison failure help message.
419
420 de::UniquePtr<ShaderExecutor> m_executor;
421 };
422
iterate(void)423 tcu::TestStatus CommonFunctionTestInstance::iterate (void)
424 {
425 const int numInputBytes = computeTotalByteSize(m_spec.inputs);
426 const int numOutputBytes = computeTotalByteSize(m_spec.outputs);
427 vector<deUint8> inputData (numInputBytes * m_numValues);
428 vector<deUint8> outputData (numOutputBytes * m_numValues);
429 const vector<void*> inputPointers = getInputOutputPointers(m_spec.inputs, inputData, m_numValues);
430 const vector<void*> outputPointers = getInputOutputPointers(m_spec.outputs, outputData, m_numValues);
431
432 // Initialize input data.
433 getInputValues(m_numValues, &inputPointers[0]);
434
435 // Execute shader.
436 m_executor->execute(m_numValues, &inputPointers[0], &outputPointers[0]);
437
438 // Compare results.
439 {
440 const vector<int> inScalarSizes = getScalarSizes(m_spec.inputs);
441 const vector<int> outScalarSizes = getScalarSizes(m_spec.outputs);
442 const vector<int> inCompByteSizes = getComponentByteSizes(m_spec.inputs);
443 const vector<int> outCompByteSizes = getComponentByteSizes(m_spec.outputs);
444 vector<void*> curInputPtr (inputPointers.size());
445 vector<void*> curOutputPtr (outputPointers.size());
446 int numFailed = 0;
447 tcu::TestContext& testCtx = m_context.getTestContext();
448
449 for (int valNdx = 0; valNdx < m_numValues; valNdx++)
450 {
451 // Set up pointers for comparison.
452 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx)
453 curInputPtr[inNdx] = (deUint8*)inputPointers[inNdx] + inScalarSizes[inNdx]*inCompByteSizes[inNdx]*valNdx;
454
455 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx)
456 curOutputPtr[outNdx] = (deUint8*)outputPointers[outNdx] + outScalarSizes[outNdx]*outCompByteSizes[outNdx]*valNdx;
457
458 if (!compare(&curInputPtr[0], &curOutputPtr[0]))
459 {
460 // \todo [2013-08-08 pyry] We probably want to log reference value as well?
461
462 testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n " << m_failMsg.str() << TestLog::EndMessage;
463
464 testCtx.getLog() << TestLog::Message << " inputs:" << TestLog::EndMessage;
465 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++)
466 testCtx.getLog() << TestLog::Message << " " << m_spec.inputs[inNdx].name << " = "
467 << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx])
468 << TestLog::EndMessage;
469
470 testCtx.getLog() << TestLog::Message << " outputs:" << TestLog::EndMessage;
471 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++)
472 testCtx.getLog() << TestLog::Message << " " << m_spec.outputs[outNdx].name << " = "
473 << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx])
474 << TestLog::EndMessage;
475
476 m_failMsg.str("");
477 m_failMsg.clear();
478 numFailed += 1;
479 }
480 }
481
482 testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage;
483
484 if (numFailed == 0)
485 return tcu::TestStatus::pass("Pass");
486 else
487 return tcu::TestStatus::fail("Result comparison failed");
488 }
489 }
490
491 // Test cases
492
493 class AbsCaseInstance : public CommonFunctionTestInstance
494 {
495 public:
AbsCaseInstance(Context & context,const ShaderSpec & spec,int numValues,const char * name)496 AbsCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name)
497 : CommonFunctionTestInstance (context, spec, numValues, name)
498 {
499 }
500
getInputValues(int numValues,void * const * values) const501 void getInputValues (int numValues, void* const* values) const
502 {
503 const IVec2 intRanges[] =
504 {
505 IVec2(-(1<<7)+1, (1<<7)-1),
506 IVec2(-(1<<15)+1, (1<<15)-1),
507 IVec2(0x80000001, 0x7fffffff)
508 };
509
510 de::Random rnd (deStringHash(m_name) ^ 0x235facu);
511 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
512 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
513 const int scalarSize = glu::getDataTypeScalarSize(type);
514
515 DE_ASSERT(!glu::isDataTypeFloatOrVec(type));
516
517 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize);
518 }
519
compare(const void * const * inputs,const void * const * outputs)520 bool compare (const void* const* inputs, const void* const* outputs)
521 {
522 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
523 const int scalarSize = glu::getDataTypeScalarSize(type);
524
525 DE_ASSERT(!glu::isDataTypeFloatOrVec(type));
526
527 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
528 {
529 const int in0 = ((const int*)inputs[0])[compNdx];
530 const int out0 = ((const int*)outputs[0])[compNdx];
531 const int ref0 = de::abs(in0);
532
533 if (out0 != ref0)
534 {
535 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
536 return false;
537 }
538 }
539
540 return true;
541 }
542 };
543
544 class AbsCase : public CommonFunctionCase
545 {
546 public:
AbsCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision)547 AbsCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision)
548 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision).c_str(), "abs")
549 {
550 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
551 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
552 m_spec.source = "out0 = abs(in0);";
553 }
554
createInstance(Context & ctx) const555 TestInstance* createInstance (Context& ctx) const
556 {
557 return new AbsCaseInstance(ctx, m_spec, m_numValues, getName());
558 }
559 };
560
561 class SignCaseInstance : public CommonFunctionTestInstance
562 {
563 public:
SignCaseInstance(Context & context,const ShaderSpec & spec,int numValues,const char * name)564 SignCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name)
565 : CommonFunctionTestInstance (context, spec, numValues, name)
566 {
567 }
568
getInputValues(int numValues,void * const * values) const569 void getInputValues (int numValues, void* const* values) const
570 {
571 const IVec2 intRanges[] =
572 {
573 IVec2(-(1<<7), (1<<7)-1),
574 IVec2(-(1<<15), (1<<15)-1),
575 IVec2(0x80000000, 0x7fffffff)
576 };
577
578 de::Random rnd (deStringHash(m_name) ^ 0x324u);
579 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
580 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
581 const int scalarSize = glu::getDataTypeScalarSize(type);
582
583 DE_ASSERT(!glu::isDataTypeFloatOrVec(type));
584
585 std::fill((int*)values[0] + scalarSize*0, (int*)values[0] + scalarSize*1, +1);
586 std::fill((int*)values[0] + scalarSize*1, (int*)values[0] + scalarSize*2, -1);
587 std::fill((int*)values[0] + scalarSize*2, (int*)values[0] + scalarSize*3, 0);
588 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
589 }
590
compare(const void * const * inputs,const void * const * outputs)591 bool compare (const void* const* inputs, const void* const* outputs)
592 {
593 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
594 const int scalarSize = glu::getDataTypeScalarSize(type);
595
596 DE_ASSERT(!glu::isDataTypeFloatOrVec(type));
597
598 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
599 {
600 const int in0 = ((const int*)inputs[0])[compNdx];
601 const int out0 = ((const int*)outputs[0])[compNdx];
602 const int ref0 = in0 < 0 ? -1 :
603 in0 > 0 ? +1 : 0;
604
605 if (out0 != ref0)
606 {
607 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
608 return false;
609 }
610 }
611
612 return true;
613 }
614 };
615
616 class SignCase : public CommonFunctionCase
617 {
618 public:
SignCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision)619 SignCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision)
620 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision).c_str(), "sign")
621 {
622 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
623 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
624 m_spec.source = "out0 = sign(in0);";
625 }
626
createInstance(Context & ctx) const627 TestInstance* createInstance (Context& ctx) const
628 {
629 return new SignCaseInstance(ctx, m_spec, m_numValues, getName());
630 }
631 };
632
infNanRandomFloats(int numValues,void * const * values,const char * name,const ShaderSpec & spec)633 static void infNanRandomFloats(int numValues, void* const* values, const char *name, const ShaderSpec& spec)
634 {
635 constexpr deUint64 kOne = 1;
636 de::Random rnd (deStringHash(name) ^ 0xc2a39fu);
637 const glu::DataType type = spec.inputs[0].varType.getBasicType();
638 const glu::Precision precision = spec.inputs[0].varType.getPrecision();
639 const int scalarSize = glu::getDataTypeScalarSize(type);
640 const int minMantissaBits = getMinMantissaBits(type, precision);
641 const int numMantissaBits = getNumMantissaBits(type);
642 const deUint64 mantissaMask = ~getMaxUlpDiffFromBits(minMantissaBits, numMantissaBits) & ((kOne<<numMantissaBits)-kOne);
643 const int exponentBits = getExponentBits(type);
644 const deUint32 exponentMask = getExponentMask(exponentBits);
645 const bool isDouble = glu::isDataTypeDoubleOrDVec(type);
646 const deUint64 exponentBias = (isDouble ? static_cast<deUint64>(tcu::Float64::EXPONENT_BIAS) : static_cast<deUint64>(tcu::Float32::EXPONENT_BIAS));
647
648 int numInf = 0;
649 int numNan = 0;
650 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
651 {
652 // Roughly 25% chance of each of Inf and NaN
653 const bool isInf = rnd.getFloat() > 0.75f;
654 const bool isNan = !isInf && rnd.getFloat() > 0.66f;
655 const deUint64 m = rnd.getUint64() & mantissaMask;
656 const deUint64 e = static_cast<deUint64>(rnd.getUint32() & exponentMask);
657 const deUint64 sign = static_cast<deUint64>(rnd.getUint32() & 0x1u);
658 // Ensure the 'quiet' bit is set on NaNs (also ensures we don't generate inf by mistake)
659 const deUint64 mantissa = isInf ? 0 : (isNan ? ((kOne<<(numMantissaBits-1)) | m) : m);
660 const deUint64 exp = (isNan || isInf) ? exponentMask : std::min(e, exponentBias);
661 const deUint64 value = (sign << (numMantissaBits + exponentBits)) | (exp << numMantissaBits) | static_cast<deUint32>(mantissa);
662 if (isInf) numInf++;
663 if (isNan) numNan++;
664
665 if (isDouble)
666 {
667 DE_ASSERT(tcu::Float64(value).isInf() == isInf && tcu::Float64(value).isNaN() == isNan);
668 ((deUint64*)values[0])[valNdx] = value;
669 }
670 else
671 {
672 const auto value32 = static_cast<deUint32>(value);
673 DE_ASSERT(tcu::Float32(value32).isInf() == isInf && tcu::Float32(value32).isNaN() == isNan);
674 ((deUint32*)values[0])[valNdx] = value32;
675 }
676 }
677 // Check for minimal coverage of intended cases.
678 DE_ASSERT(0 < numInf);
679 DE_ASSERT(0 < numNan);
680 DE_ASSERT(numInf + numNan < numValues*scalarSize);
681 }
682
683 class IsnanCaseInstance : public CommonFunctionTestInstance
684 {
685 public:
IsnanCaseInstance(Context & context,const ShaderSpec & spec,int numValues,const char * name)686 IsnanCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name)
687 : CommonFunctionTestInstance (context, spec, numValues, name)
688 {
689 }
690
getInputValues(int numValues,void * const * values) const691 void getInputValues (int numValues, void* const* values) const
692 {
693 infNanRandomFloats(numValues, values, m_name, m_spec);
694 }
695
compare(const void * const * inputs,const void * const * outputs)696 bool compare (const void* const* inputs, const void* const* outputs)
697 {
698 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
699 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
700 const int scalarSize = glu::getDataTypeScalarSize(type);
701 const bool isDouble = glu::isDataTypeDoubleOrDVec(type);
702
703 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
704 {
705 const bool out0 = reinterpret_cast<const deUint32*>(outputs[0])[compNdx] != 0;
706 bool ok;
707 bool ref;
708
709 if (isDouble)
710 {
711 const double in0 = reinterpret_cast<const double*>(inputs[0])[compNdx];
712 ref = tcu::Float64(in0).isNaN();
713 ok = (out0 == ref);
714 }
715 else
716 {
717 const float in0 = reinterpret_cast<const float*>(inputs[0])[compNdx];
718 ref = tcu::Float32(in0).isNaN();
719
720 // NaN support only required for highp. Otherwise just check for false positives.
721 if (precision == glu::PRECISION_HIGHP)
722 ok = (out0 == ref);
723 else
724 ok = ref || !out0;
725 }
726
727 if (!ok)
728 {
729 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
730 return false;
731 }
732 }
733
734 return true;
735 }
736 };
737
738 class IsnanCase : public CommonFunctionCase
739 {
740 public:
IsnanCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision)741 IsnanCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision)
742 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision).c_str(), "isnan")
743 {
744 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType) || glu::isDataTypeDoubleOrDVec(baseType));
745
746 const int vecSize = glu::getDataTypeScalarSize(baseType);
747 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
748
749 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
750 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
751 m_spec.source = "out0 = isnan(in0);";
752 }
753
checkSupport(Context & context) const754 void checkSupport (Context& context) const
755 {
756 checkTypeSupport(context, m_spec.inputs[0].varType.getBasicType());
757 }
758
createInstance(Context & ctx) const759 TestInstance* createInstance (Context& ctx) const
760 {
761 return new IsnanCaseInstance(ctx, m_spec, m_numValues, getName());
762 }
763 };
764
765 class IsinfCaseInstance : public CommonFunctionTestInstance
766 {
767 public:
IsinfCaseInstance(Context & context,const ShaderSpec & spec,int numValues,const char * name)768 IsinfCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name)
769 : CommonFunctionTestInstance(context, spec, numValues, name)
770 {
771 }
772
getInputValues(int numValues,void * const * values) const773 void getInputValues (int numValues, void* const* values) const
774 {
775 infNanRandomFloats(numValues, values, m_name, m_spec);
776 }
777
compare(const void * const * inputs,const void * const * outputs)778 bool compare (const void* const* inputs, const void* const* outputs)
779 {
780 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
781 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
782 const int scalarSize = glu::getDataTypeScalarSize(type);
783 const bool isDouble = glu::isDataTypeDoubleOrDVec(type);
784
785 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
786 {
787 const bool out0 = reinterpret_cast<const deUint32*>(outputs[0])[compNdx] != 0;
788 bool ref;
789 bool ok;
790
791 if (isDouble)
792 {
793 const double in0 = reinterpret_cast<const double*>(inputs[0])[compNdx];
794 ref = tcu::Float64(in0).isInf();
795 ok = (out0 == ref);
796 }
797 else
798 {
799 const float in0 = reinterpret_cast<const float*>(inputs[0])[compNdx];
800 if (precision == glu::PRECISION_HIGHP)
801 {
802 // Only highp is required to support inf/nan
803 ref = tcu::Float32(in0).isInf();
804 ok = (out0 == ref);
805 }
806 else
807 {
808 // Inf support is optional, check that inputs that are not Inf in mediump don't result in true.
809 ref = tcu::Float16(in0).isInf();
810 ok = (out0 || !ref);
811 }
812 }
813
814 if (!ok)
815 {
816 m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
817 return false;
818 }
819 }
820
821 return true;
822 }
823 };
824
825 class IsinfCase : public CommonFunctionCase
826 {
827 public:
IsinfCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision)828 IsinfCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision)
829 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision).c_str(), "isinf")
830 {
831 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType) || glu::isDataTypeDoubleOrDVec(baseType));
832
833 const int vecSize = glu::getDataTypeScalarSize(baseType);
834 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
835
836 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
837 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
838 m_spec.source = "out0 = isinf(in0);";
839 }
840
checkSupport(Context & context) const841 void checkSupport (Context& context) const
842 {
843 checkTypeSupport(context, m_spec.inputs[0].varType.getBasicType());
844 }
845
createInstance(Context & ctx) const846 TestInstance* createInstance (Context& ctx) const
847 {
848 return new IsinfCaseInstance(ctx, m_spec, m_numValues, getName());
849 }
850 };
851
852 class FloatBitsToUintIntCaseInstance : public CommonFunctionTestInstance
853 {
854 public:
FloatBitsToUintIntCaseInstance(Context & context,const ShaderSpec & spec,int numValues,const char * name)855 FloatBitsToUintIntCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name)
856 : CommonFunctionTestInstance (context, spec, numValues, name)
857 {
858 }
859
getInputValues(int numValues,void * const * values) const860 void getInputValues (int numValues, void* const* values) const
861 {
862 const Vec2 ranges[] =
863 {
864 Vec2(-2.0f, 2.0f), // lowp
865 Vec2(-1e3f, 1e3f), // mediump
866 Vec2(-1e7f, 1e7f) // highp
867 };
868
869 de::Random rnd (deStringHash(m_name) ^ 0x2790au);
870 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
871 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
872 const int scalarSize = glu::getDataTypeScalarSize(type);
873
874 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
875 }
876
compare(const void * const * inputs,const void * const * outputs)877 bool compare (const void* const* inputs, const void* const* outputs)
878 {
879 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
880 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
881 const int scalarSize = glu::getDataTypeScalarSize(type);
882
883 const int minMantissaBits = getMinMantissaBits(type, precision);
884 const int numMantissaBits = getNumMantissaBits(type);
885 const int maxUlpDiff = static_cast<int>(getMaxUlpDiffFromBits(minMantissaBits, numMantissaBits));
886
887 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
888 {
889 const float in0 = ((const float*)inputs[0])[compNdx];
890 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
891 const deUint32 refOut0 = tcu::Float32(in0).bits();
892 const int ulpDiff = de::abs((int)out0 - (int)refOut0);
893
894 if (ulpDiff > maxUlpDiff)
895 {
896 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold "
897 << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
898 return false;
899 }
900 }
901
902 return true;
903 }
904 };
905
906 class FloatBitsToUintIntCase : public CommonFunctionCase
907 {
908 public:
FloatBitsToUintIntCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,bool outIsSigned)909 FloatBitsToUintIntCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, bool outIsSigned)
910 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision).c_str(), outIsSigned ? "floatBitsToInt" : "floatBitsToUint")
911 {
912 const int vecSize = glu::getDataTypeScalarSize(baseType);
913 const glu::DataType intType = outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT)
914 : (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT);
915
916 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
917 m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP)));
918 m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);";
919 }
920
createInstance(Context & ctx) const921 TestInstance* createInstance (Context& ctx) const
922 {
923 return new FloatBitsToUintIntCaseInstance(ctx, m_spec, m_numValues, getName());
924 }
925 };
926
927 class FloatBitsToIntCase : public FloatBitsToUintIntCase
928 {
929 public:
FloatBitsToIntCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision)930 FloatBitsToIntCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision)
931 : FloatBitsToUintIntCase (testCtx, baseType, precision, true)
932 {
933 }
934
935 };
936
937 class FloatBitsToUintCase : public FloatBitsToUintIntCase
938 {
939 public:
FloatBitsToUintCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision)940 FloatBitsToUintCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision)
941 : FloatBitsToUintIntCase (testCtx, baseType, precision, false)
942 {
943 }
944 };
945
946 class BitsToFloatCaseInstance : public CommonFunctionTestInstance
947 {
948 public:
BitsToFloatCaseInstance(Context & context,const ShaderSpec & spec,int numValues,const char * name)949 BitsToFloatCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name)
950 : CommonFunctionTestInstance (context, spec, numValues, name)
951 {
952 }
953
getInputValues(int numValues,void * const * values) const954 void getInputValues (int numValues, void* const* values) const
955 {
956 de::Random rnd (deStringHash(m_name) ^ 0xbbb225u);
957 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
958 const int scalarSize = glu::getDataTypeScalarSize(type);
959 const Vec2 range (-1e8f, +1e8f);
960
961 // \note Filled as floats.
962 fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize);
963 }
964
compare(const void * const * inputs,const void * const * outputs)965 bool compare (const void* const* inputs, const void* const* outputs)
966 {
967 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
968 const int scalarSize = glu::getDataTypeScalarSize(type);
969 const deUint32 maxUlpDiff = 0;
970
971 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
972 {
973 const float in0 = ((const float*)inputs[0])[compNdx];
974 const float out0 = ((const float*)outputs[0])[compNdx];
975 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(in0, out0);
976
977 if (ulpDiff > maxUlpDiff)
978 {
979 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits()) << " with ULP threshold "
980 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
981 return false;
982 }
983 }
984
985 return true;
986 }
987 };
988
989 class BitsToFloatCase : public CommonFunctionCase
990 {
991 public:
BitsToFloatCase(tcu::TestContext & testCtx,glu::DataType baseType)992 BitsToFloatCase (tcu::TestContext& testCtx, glu::DataType baseType)
993 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP).c_str(), glu::isDataTypeIntOrIVec(baseType) ? "intBitsToFloat" : "uintBitsToFloat")
994 {
995 const bool inIsSigned = glu::isDataTypeIntOrIVec(baseType);
996 const int vecSize = glu::getDataTypeScalarSize(baseType);
997 const glu::DataType floatType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
998
999 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1000 m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP)));
1001 m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);";
1002 }
1003
createInstance(Context & ctx) const1004 TestInstance* createInstance (Context& ctx) const
1005 {
1006 return new BitsToFloatCaseInstance(ctx, m_spec, m_numValues, getName());
1007 }
1008 };
1009
1010 } // anonymous
1011
ShaderCommonFunctionTests(tcu::TestContext & testCtx)1012 ShaderCommonFunctionTests::ShaderCommonFunctionTests (tcu::TestContext& testCtx)
1013 : tcu::TestCaseGroup (testCtx, "common", "Common function tests")
1014 {
1015 }
1016
~ShaderCommonFunctionTests(void)1017 ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void)
1018 {
1019 }
1020
init(void)1021 void ShaderCommonFunctionTests::init (void)
1022 {
1023 static const std::vector<glu::DataType> kIntOnly (1u, glu::TYPE_INT);
1024 static const std::vector<glu::DataType> kFloatOnly (1u, glu::TYPE_FLOAT);
1025 static const std::vector<glu::DataType> kFloatAndDouble {glu::TYPE_FLOAT, glu::TYPE_DOUBLE};
1026
1027 addFunctionCases<AbsCase> (this, "abs", kIntOnly);
1028 addFunctionCases<SignCase> (this, "sign", kIntOnly);
1029 addFunctionCases<IsnanCase> (this, "isnan", kFloatAndDouble);
1030 addFunctionCases<IsinfCase> (this, "isinf", kFloatAndDouble);
1031 addFunctionCases<FloatBitsToIntCase> (this, "floatbitstoint", kFloatOnly);
1032 addFunctionCases<FloatBitsToUintCase> (this, "floatbitstouint", kFloatOnly);
1033
1034 // (u)intBitsToFloat()
1035 {
1036 tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "intbitstofloat", "intBitsToFloat() Tests");
1037 tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat", "uintBitsToFloat() Tests");
1038
1039 addChild(intGroup);
1040 addChild(uintGroup);
1041
1042 for (int vecSize = 1; vecSize < 4; vecSize++)
1043 {
1044 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
1045 const glu::DataType uintType = vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT;
1046
1047 intGroup->addChild(new BitsToFloatCase(getTestContext(), intType));
1048 uintGroup->addChild(new BitsToFloatCase(getTestContext(), uintType));
1049 }
1050 }
1051 }
1052
1053 } // shaderexecutor
1054 } // vkt
1055