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 "gluContextInfo.hpp"
29 #include "tcuTestLog.hpp"
30 #include "tcuFormatUtil.hpp"
31 #include "tcuFloat.hpp"
32 #include "tcuInterval.hpp"
33 #include "tcuFloatFormat.hpp"
34 #include "deRandom.hpp"
35 #include "deMath.h"
36 #include "deString.h"
37 #include "deArrayUtil.hpp"
38 #include "deSharedPtr.hpp"
39
40 namespace vkt
41 {
42
43 namespace shaderexecutor
44 {
45
46
47 using std::vector;
48 using std::string;
49 using tcu::TestLog;
50
51 using tcu::Vec2;
52 using tcu::Vec3;
53 using tcu::Vec4;
54 using tcu::IVec2;
55 using tcu::IVec3;
56 using tcu::IVec4;
57
58 namespace
59 {
60
61 // Utilities
62
63 template<typename T, int Size>
64 struct VecArrayAccess
65 {
66 public:
VecArrayAccessvkt::shaderexecutor::__anon2c2bd4020111::VecArrayAccess67 VecArrayAccess (const void* ptr) : m_array((tcu::Vector<T, Size>*)ptr) {}
~VecArrayAccessvkt::shaderexecutor::__anon2c2bd4020111::VecArrayAccess68 ~VecArrayAccess (void) {}
69
operator []vkt::shaderexecutor::__anon2c2bd4020111::VecArrayAccess70 const tcu::Vector<T, Size>& operator[] (size_t offset) const { return m_array[offset]; }
operator []vkt::shaderexecutor::__anon2c2bd4020111::VecArrayAccess71 tcu::Vector<T, Size>& operator[] (size_t offset) { return m_array[offset]; }
72
73 private:
74 tcu::Vector<T, Size>* m_array;
75 };
76
77 template<typename T> T randomScalar (de::Random& rnd, T minValue, T maxValue);
randomScalar(de::Random & rnd,float minValue,float maxValue)78 template<> inline float randomScalar (de::Random& rnd, float minValue, float maxValue) { return rnd.getFloat(minValue, maxValue); }
randomScalar(de::Random & rnd,deInt32 minValue,deInt32 maxValue)79 template<> inline deInt32 randomScalar (de::Random& rnd, deInt32 minValue, deInt32 maxValue) { return rnd.getInt(minValue, maxValue); }
80
81 template<typename T, int Size>
randomVector(de::Random & rnd,const tcu::Vector<T,Size> & minValue,const tcu::Vector<T,Size> & maxValue)82 inline tcu::Vector<T, Size> randomVector (de::Random& rnd, const tcu::Vector<T, Size>& minValue, const tcu::Vector<T, Size>& maxValue)
83 {
84 tcu::Vector<T, Size> res;
85 for (int ndx = 0; ndx < Size; ndx++)
86 res[ndx] = randomScalar<T>(rnd, minValue[ndx], maxValue[ndx]);
87 return res;
88 }
89
90 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)91 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)
92 {
93 VecArrayAccess<T, Size> access(dst);
94 for (int ndx = 0; ndx < numValues; ndx++)
95 access[offset + ndx] = randomVector<T, Size>(rnd, minValue, maxValue);
96 }
97
98 template<typename T>
fillRandomScalars(de::Random & rnd,T minValue,T maxValue,void * dst,int numValues,int offset=0)99 static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0)
100 {
101 T* typedPtr = (T*)dst;
102 for (int ndx = 0; ndx < numValues; ndx++)
103 typedPtr[offset + ndx] = randomScalar<T>(rnd, minValue, maxValue);
104 }
105
numBitsLostInOp(float input,float output)106 inline int numBitsLostInOp (float input, float output)
107 {
108 const int inExp = tcu::Float32(input).exponent();
109 const int outExp = tcu::Float32(output).exponent();
110
111 return de::max(0, inExp-outExp); // Lost due to mantissa shift.
112 }
113
getUlpDiff(float a,float b)114 inline deUint32 getUlpDiff (float a, float b)
115 {
116 const deUint32 aBits = tcu::Float32(a).bits();
117 const deUint32 bBits = tcu::Float32(b).bits();
118 return aBits > bBits ? aBits - bBits : bBits - aBits;
119 }
120
getUlpDiffIgnoreZeroSign(float a,float b)121 inline deUint32 getUlpDiffIgnoreZeroSign (float a, float b)
122 {
123 if (tcu::Float32(a).isZero())
124 return getUlpDiff(tcu::Float32::construct(tcu::Float32(b).sign(), 0, 0).asFloat(), b);
125 else if (tcu::Float32(b).isZero())
126 return getUlpDiff(a, tcu::Float32::construct(tcu::Float32(a).sign(), 0, 0).asFloat());
127 else
128 return getUlpDiff(a, b);
129 }
130
supportsSignedZero(glu::Precision precision)131 inline bool supportsSignedZero (glu::Precision precision)
132 {
133 // \note GLSL ES 3.1 doesn't really require support for -0, but we require it for highp
134 // as it is very widely supported.
135 return precision == glu::PRECISION_HIGHP;
136 }
137
getEpsFromMaxUlpDiff(float value,deUint32 ulpDiff)138 inline float getEpsFromMaxUlpDiff (float value, deUint32 ulpDiff)
139 {
140 const int exp = tcu::Float32(value).exponent();
141 return tcu::Float32::construct(+1, exp, (1u<<23) | ulpDiff).asFloat() - tcu::Float32::construct(+1, exp, 1u<<23).asFloat();
142 }
143
getMaxUlpDiffFromBits(int numAccurateBits)144 inline deUint32 getMaxUlpDiffFromBits (int numAccurateBits)
145 {
146 const int numGarbageBits = 23-numAccurateBits;
147 const deUint32 mask = (1u<<numGarbageBits)-1u;
148
149 return mask;
150 }
151
getEpsFromBits(float value,int numAccurateBits)152 inline float getEpsFromBits (float value, int numAccurateBits)
153 {
154 return getEpsFromMaxUlpDiff(value, getMaxUlpDiffFromBits(numAccurateBits));
155 }
156
getMinMantissaBits(glu::Precision precision)157 static int getMinMantissaBits (glu::Precision precision)
158 {
159 const int bits[] =
160 {
161 7, // lowp
162 10, // mediump
163 23 // highp
164 };
165 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(bits) == glu::PRECISION_LAST);
166 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(bits)));
167 return bits[precision];
168 }
169
getMaxNormalizedValueExponent(glu::Precision precision)170 static int getMaxNormalizedValueExponent (glu::Precision precision)
171 {
172 const int exponent[] =
173 {
174 0, // lowp
175 13, // mediump
176 127 // highp
177 };
178 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(exponent) == glu::PRECISION_LAST);
179 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(exponent)));
180 return exponent[precision];
181 }
182
getMinNormalizedValueExponent(glu::Precision precision)183 static int getMinNormalizedValueExponent (glu::Precision precision)
184 {
185 const int exponent[] =
186 {
187 -7, // lowp
188 -13, // mediump
189 -126 // highp
190 };
191 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(exponent) == glu::PRECISION_LAST);
192 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(exponent)));
193 return exponent[precision];
194 }
195
makeFloatRepresentable(float f,glu::Precision precision)196 static float makeFloatRepresentable (float f, glu::Precision precision)
197 {
198 if (precision == glu::PRECISION_HIGHP)
199 {
200 // \note: assuming f is not extended-precision
201 return f;
202 }
203 else
204 {
205 const int numMantissaBits = getMinMantissaBits(precision);
206 const int maxNormalizedValueExponent = getMaxNormalizedValueExponent(precision);
207 const int minNormalizedValueExponent = getMinNormalizedValueExponent(precision);
208 const deUint32 representableMantissaMask = ((deUint32(1) << numMantissaBits) - 1) << (23 - (deUint32)numMantissaBits);
209 const float largestRepresentableValue = tcu::Float32::constructBits(+1, maxNormalizedValueExponent, ((1u << numMantissaBits) - 1u) << (23u - (deUint32)numMantissaBits)).asFloat();
210 const bool zeroNotRepresentable = (precision == glu::PRECISION_LOWP);
211
212 // if zero is not required to be representable, use smallest positive non-subnormal value
213 const float zeroValue = (zeroNotRepresentable) ? (tcu::Float32::constructBits(+1, minNormalizedValueExponent, 1).asFloat()) : (0.0f);
214
215 const tcu::Float32 float32Representation (f);
216
217 if (float32Representation.exponent() < minNormalizedValueExponent)
218 {
219 // flush too small values to zero
220 return zeroValue;
221 }
222 else if (float32Representation.exponent() > maxNormalizedValueExponent)
223 {
224 // clamp too large values
225 return (float32Representation.sign() == +1) ? (largestRepresentableValue) : (-largestRepresentableValue);
226 }
227 else
228 {
229 // remove unrepresentable mantissa bits
230 const tcu::Float32 targetRepresentation(tcu::Float32::constructBits(float32Representation.sign(),
231 float32Representation.exponent(),
232 float32Representation.mantissaBits() & representableMantissaMask));
233
234 return targetRepresentation.asFloat();
235 }
236 }
237 }
238
getScalarSizes(const vector<Symbol> & symbols)239 static vector<int> getScalarSizes (const vector<Symbol>& symbols)
240 {
241 vector<int> sizes(symbols.size());
242 for (int ndx = 0; ndx < (int)symbols.size(); ++ndx)
243 sizes[ndx] = symbols[ndx].varType.getScalarSize();
244 return sizes;
245 }
246
computeTotalScalarSize(const vector<Symbol> & symbols)247 static int computeTotalScalarSize (const vector<Symbol>& symbols)
248 {
249 int totalSize = 0;
250 for (vector<Symbol>::const_iterator sym = symbols.begin(); sym != symbols.end(); ++sym)
251 totalSize += sym->varType.getScalarSize();
252 return totalSize;
253 }
254
getInputOutputPointers(const vector<Symbol> & symbols,vector<deUint32> & data,const int numValues)255 static vector<void*> getInputOutputPointers (const vector<Symbol>& symbols, vector<deUint32>& data, const int numValues)
256 {
257 vector<void*> pointers (symbols.size());
258 int curScalarOffset = 0;
259
260 for (int varNdx = 0; varNdx < (int)symbols.size(); ++varNdx)
261 {
262 const Symbol& var = symbols[varNdx];
263 const int scalarSize = var.varType.getScalarSize();
264
265 // Uses planar layout as input/output specs do not support strides.
266 pointers[varNdx] = &data[curScalarOffset];
267 curScalarOffset += scalarSize*numValues;
268 }
269
270 DE_ASSERT(curScalarOffset == (int)data.size());
271
272 return pointers;
273 }
274
275 // \todo [2013-08-08 pyry] Make generic utility and move to glu?
276
277 struct HexFloat
278 {
279 const float value;
HexFloatvkt::shaderexecutor::__anon2c2bd4020111::HexFloat280 HexFloat (const float value_) : value(value_) {}
281 };
282
operator <<(std::ostream & str,const HexFloat & v)283 std::ostream& operator<< (std::ostream& str, const HexFloat& v)
284 {
285 return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
286 }
287
288 struct HexBool
289 {
290 const deUint32 value;
HexBoolvkt::shaderexecutor::__anon2c2bd4020111::HexBool291 HexBool (const deUint32 value_) : value(value_) {}
292 };
293
operator <<(std::ostream & str,const HexBool & v)294 std::ostream& operator<< (std::ostream& str, const HexBool& v)
295 {
296 return str << (v.value ? "true" : "false") << " / " << tcu::toHex(v.value);
297 }
298
299 struct VarValue
300 {
301 const glu::VarType& type;
302 const void* value;
303
VarValuevkt::shaderexecutor::__anon2c2bd4020111::VarValue304 VarValue (const glu::VarType& type_, const void* value_) : type(type_), value(value_) {}
305 };
306
operator <<(std::ostream & str,const VarValue & varValue)307 std::ostream& operator<< (std::ostream& str, const VarValue& varValue)
308 {
309 DE_ASSERT(varValue.type.isBasicType());
310
311 const glu::DataType basicType = varValue.type.getBasicType();
312 const glu::DataType scalarType = glu::getDataTypeScalarType(basicType);
313 const int numComponents = glu::getDataTypeScalarSize(basicType);
314
315 if (numComponents > 1)
316 str << glu::getDataTypeName(basicType) << "(";
317
318 for (int compNdx = 0; compNdx < numComponents; compNdx++)
319 {
320 if (compNdx != 0)
321 str << ", ";
322
323 switch (scalarType)
324 {
325 case glu::TYPE_FLOAT: str << HexFloat(((const float*)varValue.value)[compNdx]); break;
326 case glu::TYPE_INT: str << ((const deInt32*)varValue.value)[compNdx]; break;
327 case glu::TYPE_UINT: str << tcu::toHex(((const deUint32*)varValue.value)[compNdx]); break;
328 case glu::TYPE_BOOL: str << HexBool(((const deUint32*)varValue.value)[compNdx]); break;
329
330 default:
331 DE_ASSERT(false);
332 }
333 }
334
335 if (numComponents > 1)
336 str << ")";
337
338 return str;
339 }
340
getPrecisionPostfix(glu::Precision precision)341 static const char* getPrecisionPostfix (glu::Precision precision)
342 {
343 static const char* s_postfix[] =
344 {
345 "_lowp",
346 "_mediump",
347 "_highp"
348 };
349 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_postfix) == glu::PRECISION_LAST);
350 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
351 return s_postfix[precision];
352 }
353
getShaderTypePostfix(glu::ShaderType shaderType)354 static const char* getShaderTypePostfix (glu::ShaderType shaderType)
355 {
356 static const char* s_postfix[] =
357 {
358 "_vertex",
359 "_fragment",
360 "_geometry",
361 "_tess_control",
362 "_tess_eval",
363 "_compute"
364 };
365 DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
366 return s_postfix[shaderType];
367 }
368
getCommonFuncCaseName(glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)369 static std::string getCommonFuncCaseName (glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
370 {
371 return string(glu::getDataTypeName(baseType)) + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType);
372 }
373
frexp(float in,float * significand,int * exponent)374 static inline void frexp (float in, float* significand, int* exponent)
375 {
376 const tcu::Float32 fpValue(in);
377
378 if (!fpValue.isZero())
379 {
380 // Construct float that has exactly the mantissa, and exponent of -1.
381 *significand = tcu::Float32::construct(fpValue.sign(), -1, fpValue.mantissa()).asFloat();
382 *exponent = fpValue.exponent()+1;
383 }
384 else
385 {
386 *significand = fpValue.sign() < 0 ? -0.0f : 0.0f;
387 *exponent = 0;
388 }
389 }
390
ldexp(float significand,int exponent)391 static inline float ldexp (float significand, int exponent)
392 {
393 const tcu::Float32 mant(significand);
394
395 if (exponent == 0 && mant.isZero())
396 {
397 return mant.sign() < 0 ? -0.0f : 0.0f;
398 }
399 else
400 {
401 return tcu::Float32::construct(mant.sign(), exponent+mant.exponent(), mant.mantissa()).asFloat();
402 }
403 }
404
405 template<class TestClass>
addFunctionCases(tcu::TestCaseGroup * parent,const char * functionName,bool floatTypes,bool intTypes,bool uintTypes,deUint32 shaderBits)406 static void addFunctionCases (tcu::TestCaseGroup* parent, const char* functionName, bool floatTypes, bool intTypes, bool uintTypes, deUint32 shaderBits)
407 {
408 tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), functionName, functionName);
409 parent->addChild(group);
410
411 const glu::DataType scalarTypes[] =
412 {
413 glu::TYPE_FLOAT,
414 glu::TYPE_INT,
415 glu::TYPE_UINT
416 };
417
418 for (int scalarTypeNdx = 0; scalarTypeNdx < DE_LENGTH_OF_ARRAY(scalarTypes); scalarTypeNdx++)
419 {
420 const glu::DataType scalarType = scalarTypes[scalarTypeNdx];
421
422 if ((!floatTypes && scalarType == glu::TYPE_FLOAT) ||
423 (!intTypes && scalarType == glu::TYPE_INT) ||
424 (!uintTypes && scalarType == glu::TYPE_UINT))
425 continue;
426
427 for (int vecSize = 1; vecSize <= 4; vecSize++)
428 {
429 for (int prec = glu::PRECISION_MEDIUMP; prec <= glu::PRECISION_HIGHP; prec++)
430 {
431 for (int shaderTypeNdx = 0; shaderTypeNdx < glu::SHADERTYPE_LAST; shaderTypeNdx++)
432 {
433 if (shaderBits & (1<<shaderTypeNdx))
434 group->addChild(new TestClass(parent->getTestContext(), glu::DataType(scalarType + vecSize - 1), glu::Precision(prec), glu::ShaderType(shaderTypeNdx)));
435 }
436 }
437 }
438 }
439 }
440
441 // CommonFunctionCase
442
443 class CommonFunctionCase : public TestCase
444 {
445 public:
446 CommonFunctionCase (tcu::TestContext& testCtx, const char* name, const char* description, glu::ShaderType shaderType);
447 ~CommonFunctionCase (void);
initPrograms(vk::SourceCollections & programCollection) const448 virtual void initPrograms (vk::SourceCollections& programCollection) const
449 {
450 m_executor->setShaderSources(programCollection);
451 }
452
453 virtual TestInstance* createInstance (Context& context) const = 0;
454
455 void init (void);
456
457 protected:
458 CommonFunctionCase (const CommonFunctionCase& other);
459 CommonFunctionCase& operator= (const CommonFunctionCase& other);
460
461 const glu::ShaderType m_shaderType;
462 ShaderSpec m_spec;
463 const int m_numValues;
464 de::MovePtr<ShaderExecutor> m_executor;
465 };
466
CommonFunctionCase(tcu::TestContext & testCtx,const char * name,const char * description,glu::ShaderType shaderType)467 CommonFunctionCase::CommonFunctionCase (tcu::TestContext& testCtx, const char* name, const char* description, glu::ShaderType shaderType)
468 : TestCase (testCtx, name, description)
469 , m_shaderType (shaderType)
470 , m_numValues (100)
471 , m_executor (DE_NULL)
472 {
473 }
474
~CommonFunctionCase(void)475 CommonFunctionCase::~CommonFunctionCase (void)
476 {
477 }
478
init(void)479 void CommonFunctionCase::init (void)
480 {
481 DE_ASSERT(!m_executor);
482
483 m_executor = de::MovePtr<ShaderExecutor>(createExecutor(m_shaderType, m_spec));
484 m_testCtx.getLog() << *m_executor;
485 }
486
487 // CommonFunctionTestInstance
488
489 class CommonFunctionTestInstance : public TestInstance
490 {
491 public:
CommonFunctionTestInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)492 CommonFunctionTestInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
493 : TestInstance (context)
494 , m_shaderType (shaderType)
495 , m_spec (spec)
496 , m_numValues (numValues)
497 , m_name (name)
498 , m_executor (executor)
499 {
500 }
501 virtual tcu::TestStatus iterate (void);
502
503 protected:
504 virtual void getInputValues (int numValues, void* const* values) const = 0;
505 virtual bool compare (const void* const* inputs, const void* const* outputs) = 0;
506
507 const glu::ShaderType m_shaderType;
508 ShaderSpec m_spec;
509 const int m_numValues;
510
511 const char* m_name;
512
513 std::ostringstream m_failMsg; //!< Comparison failure help message.
514
515 ShaderExecutor& m_executor;
516 };
517
iterate(void)518 tcu::TestStatus CommonFunctionTestInstance::iterate (void)
519 {
520 const int numInputScalars = computeTotalScalarSize(m_spec.inputs);
521 const int numOutputScalars = computeTotalScalarSize(m_spec.outputs);
522 vector<deUint32> inputData (numInputScalars * m_numValues);
523 vector<deUint32> outputData (numOutputScalars * m_numValues);
524 const vector<void*> inputPointers = getInputOutputPointers(m_spec.inputs, inputData, m_numValues);
525 const vector<void*> outputPointers = getInputOutputPointers(m_spec.outputs, outputData, m_numValues);
526
527 // Initialize input data.
528 getInputValues(m_numValues, &inputPointers[0]);
529
530 // Execute shader.
531 m_executor.execute(m_context, m_numValues, &inputPointers[0], &outputPointers[0]);
532
533 // Compare results.
534 {
535 const vector<int> inScalarSizes = getScalarSizes(m_spec.inputs);
536 const vector<int> outScalarSizes = getScalarSizes(m_spec.outputs);
537 vector<void*> curInputPtr (inputPointers.size());
538 vector<void*> curOutputPtr (outputPointers.size());
539 int numFailed = 0;
540 tcu::TestContext& testCtx = m_context.getTestContext();
541
542 for (int valNdx = 0; valNdx < m_numValues; valNdx++)
543 {
544 // Set up pointers for comparison.
545 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx)
546 curInputPtr[inNdx] = (deUint32*)inputPointers[inNdx] + inScalarSizes[inNdx]*valNdx;
547
548 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx)
549 curOutputPtr[outNdx] = (deUint32*)outputPointers[outNdx] + outScalarSizes[outNdx]*valNdx;
550
551 if (!compare(&curInputPtr[0], &curOutputPtr[0]))
552 {
553 // \todo [2013-08-08 pyry] We probably want to log reference value as well?
554
555 testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n " << m_failMsg.str() << TestLog::EndMessage;
556
557 testCtx.getLog() << TestLog::Message << " inputs:" << TestLog::EndMessage;
558 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++)
559 testCtx.getLog() << TestLog::Message << " " << m_spec.inputs[inNdx].name << " = "
560 << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx])
561 << TestLog::EndMessage;
562
563 testCtx.getLog() << TestLog::Message << " outputs:" << TestLog::EndMessage;
564 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++)
565 testCtx.getLog() << TestLog::Message << " " << m_spec.outputs[outNdx].name << " = "
566 << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx])
567 << TestLog::EndMessage;
568
569 m_failMsg.str("");
570 m_failMsg.clear();
571 numFailed += 1;
572 }
573 }
574
575 testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage;
576
577 if (numFailed == 0)
578 return tcu::TestStatus::pass("Pass");
579 else
580 return tcu::TestStatus::fail("Result comparison failed");
581 }
582 }
583
584 // Test cases
585
586 class AbsCaseInstance : public CommonFunctionTestInstance
587 {
588 public:
AbsCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)589 AbsCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
590 : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
591 {
592 }
593
getInputValues(int numValues,void * const * values) const594 void getInputValues (int numValues, void* const* values) const
595 {
596 const Vec2 floatRanges[] =
597 {
598 Vec2(-2.0f, 2.0f), // lowp
599 Vec2(-1e3f, 1e3f), // mediump
600 Vec2(-1e7f, 1e7f) // highp
601 };
602 const IVec2 intRanges[] =
603 {
604 IVec2(-(1<<7)+1, (1<<7)-1),
605 IVec2(-(1<<15)+1, (1<<15)-1),
606 IVec2(0x80000001, 0x7fffffff)
607 };
608
609 de::Random rnd (deStringHash(m_name) ^ 0x235facu);
610 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
611 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
612 const int scalarSize = glu::getDataTypeScalarSize(type);
613
614 if (glu::isDataTypeFloatOrVec(type))
615 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), values[0], numValues*scalarSize);
616 else
617 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize);
618 }
619
compare(const void * const * inputs,const void * const * outputs)620 bool compare (const void* const* inputs, const void* const* outputs)
621 {
622 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
623 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
624 const int scalarSize = glu::getDataTypeScalarSize(type);
625
626 if (glu::isDataTypeFloatOrVec(type))
627 {
628 const int mantissaBits = getMinMantissaBits(precision);
629 const deUint32 maxUlpDiff = (1u<<(23-mantissaBits))-1u;
630
631 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
632 {
633 const float in0 = ((const float*)inputs[0])[compNdx];
634 const float out0 = ((const float*)outputs[0])[compNdx];
635 const float ref0 = de::abs(in0);
636 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
637
638 if (ulpDiff0 > maxUlpDiff)
639 {
640 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
641 return false;
642 }
643 }
644 }
645 else
646 {
647 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
648 {
649 const int in0 = ((const int*)inputs[0])[compNdx];
650 const int out0 = ((const int*)outputs[0])[compNdx];
651 const int ref0 = de::abs(in0);
652
653 if (out0 != ref0)
654 {
655 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
656 return false;
657 }
658 }
659 }
660
661 return true;
662 }
663 };
664
665 class AbsCase : public CommonFunctionCase
666 {
667 public:
AbsCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)668 AbsCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
669 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "abs", shaderType)
670 {
671 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
672 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
673 m_spec.source = "out0 = abs(in0);";
674 init();
675 }
676
createInstance(Context & ctx) const677 TestInstance* createInstance (Context& ctx) const
678 {
679 return new AbsCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
680 }
681 };
682
683 class SignCaseInstance : public CommonFunctionTestInstance
684 {
685 public:
SignCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)686 SignCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
687 : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
688 {
689 }
690
getInputValues(int numValues,void * const * values) const691 void getInputValues (int numValues, void* const* values) const
692 {
693 const Vec2 floatRanges[] =
694 {
695 Vec2(-2.0f, 2.0f), // lowp
696 Vec2(-1e4f, 1e4f), // mediump - note: may end up as inf
697 Vec2(-1e8f, 1e8f) // highp - note: may end up as inf
698 };
699 const IVec2 intRanges[] =
700 {
701 IVec2(-(1<<7), (1<<7)-1),
702 IVec2(-(1<<15), (1<<15)-1),
703 IVec2(0x80000000, 0x7fffffff)
704 };
705
706 de::Random rnd (deStringHash(m_name) ^ 0x324u);
707 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
708 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
709 const int scalarSize = glu::getDataTypeScalarSize(type);
710
711 if (glu::isDataTypeFloatOrVec(type))
712 {
713 // Special cases.
714 std::fill((float*)values[0], (float*)values[0] + scalarSize, +1.0f);
715 std::fill((float*)values[0], (float*)values[0] + scalarSize, -1.0f);
716 std::fill((float*)values[0], (float*)values[0] + scalarSize, 0.0f);
717 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), (float*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
718 }
719 else
720 {
721 std::fill((int*)values[0], (int*)values[0] + scalarSize, +1);
722 std::fill((int*)values[0], (int*)values[0] + scalarSize, -1);
723 std::fill((int*)values[0], (int*)values[0] + scalarSize, 0);
724 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
725 }
726 }
727
compare(const void * const * inputs,const void * const * outputs)728 bool compare (const void* const* inputs, const void* const* outputs)
729 {
730 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
731 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
732 const int scalarSize = glu::getDataTypeScalarSize(type);
733
734 if (glu::isDataTypeFloatOrVec(type))
735 {
736 // Both highp and mediump should be able to represent -1, 0, and +1 exactly
737 const deUint32 maxUlpDiff = precision == glu::PRECISION_LOWP ? getMaxUlpDiffFromBits(getMinMantissaBits(precision)) : 0;
738
739 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
740 {
741 const float in0 = ((const float*)inputs[0])[compNdx];
742 const float out0 = ((const float*)outputs[0])[compNdx];
743 const float ref0 = in0 < 0.0f ? -1.0f :
744 in0 > 0.0f ? +1.0f : 0.0f;
745 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
746
747 if (ulpDiff0 > maxUlpDiff)
748 {
749 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
750 return false;
751 }
752 }
753 }
754 else
755 {
756 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
757 {
758 const int in0 = ((const int*)inputs[0])[compNdx];
759 const int out0 = ((const int*)outputs[0])[compNdx];
760 const int ref0 = in0 < 0 ? -1 :
761 in0 > 0 ? +1 : 0;
762
763 if (out0 != ref0)
764 {
765 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
766 return false;
767 }
768 }
769 }
770
771 return true;
772 }
773 };
774
775 class SignCase : public CommonFunctionCase
776 {
777 public:
SignCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)778 SignCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
779 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "sign", shaderType)
780 {
781 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
782 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
783 m_spec.source = "out0 = sign(in0);";
784 init();
785 }
786
createInstance(Context & ctx) const787 TestInstance* createInstance (Context& ctx) const
788 {
789 return new SignCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
790 }
791 };
792
roundEven(float v)793 static float roundEven (float v)
794 {
795 const float q = deFloatFrac(v);
796 const int truncated = int(v-q);
797 const int rounded = (q > 0.5f) ? (truncated + 1) : // Rounded up
798 (q == 0.5f && (truncated % 2 != 0)) ? (truncated + 1) : // Round to nearest even at 0.5
799 truncated; // Rounded down
800
801 return float(rounded);
802 }
803
804 class RoundEvenCaseInstance : public CommonFunctionTestInstance
805 {
806 public:
RoundEvenCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)807 RoundEvenCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
808 : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
809 {
810 }
811
getInputValues(int numValues,void * const * values) const812 void getInputValues (int numValues, void* const* values) const
813 {
814 const Vec2 ranges[] =
815 {
816 Vec2(-2.0f, 2.0f), // lowp
817 Vec2(-1e3f, 1e3f), // mediump
818 Vec2(-1e7f, 1e7f) // highp
819 };
820
821 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
822 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
823 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
824 const int scalarSize = glu::getDataTypeScalarSize(type);
825 int numSpecialCases = 0;
826
827 // Special cases.
828 if (precision != glu::PRECISION_LOWP)
829 {
830 DE_ASSERT(numValues >= 20);
831 for (int ndx = 0; ndx < 20; ndx++)
832 {
833 const float v = de::clamp(float(ndx) - 10.5f, ranges[precision].x(), ranges[precision].y());
834 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
835 numSpecialCases += 1;
836 }
837 }
838
839 // Random cases.
840 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
841
842 // If precision is mediump, make sure values can be represented in fp16 exactly
843 if (precision == glu::PRECISION_MEDIUMP)
844 {
845 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
846 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
847 }
848 }
849
compare(const void * const * inputs,const void * const * outputs)850 bool compare (const void* const* inputs, const void* const* outputs)
851 {
852 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
853 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
854 const bool hasSignedZero = supportsSignedZero(precision);
855 const int scalarSize = glu::getDataTypeScalarSize(type);
856
857 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
858 {
859 // Require exact rounding result.
860 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
861 {
862 const float in0 = ((const float*)inputs[0])[compNdx];
863 const float out0 = ((const float*)outputs[0])[compNdx];
864 const float ref = roundEven(in0);
865
866 const deUint32 ulpDiff = hasSignedZero ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
867
868 if (ulpDiff > 0)
869 {
870 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
871 return false;
872 }
873 }
874 }
875 else
876 {
877 const int mantissaBits = getMinMantissaBits(precision);
878 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
879 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
880
881 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
882 {
883 const float in0 = ((const float*)inputs[0])[compNdx];
884 const float out0 = ((const float*)outputs[0])[compNdx];
885 const int minRes = int(roundEven(in0-eps));
886 const int maxRes = int(roundEven(in0+eps));
887 bool anyOk = false;
888
889 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
890 {
891 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
892
893 if (ulpDiff <= maxUlpDiff)
894 {
895 anyOk = true;
896 break;
897 }
898 }
899
900 if (!anyOk)
901 {
902 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
903 return false;
904 }
905 }
906 }
907
908 return true;
909 }
910 };
911
912 class RoundEvenCase : public CommonFunctionCase
913 {
914 public:
RoundEvenCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)915 RoundEvenCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
916 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "roundEven", shaderType)
917 {
918 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
919 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
920 m_spec.source = "out0 = roundEven(in0);";
921 init();
922 }
923
createInstance(Context & ctx) const924 TestInstance* createInstance (Context& ctx) const
925 {
926 return new RoundEvenCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
927 }
928 };
929
930 class ModfCaseInstance : public CommonFunctionTestInstance
931 {
932 public:
ModfCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)933 ModfCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
934 : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
935 {
936 }
937
getInputValues(int numValues,void * const * values) const938 void getInputValues (int numValues, void* const* values) const
939 {
940 const Vec2 ranges[] =
941 {
942 Vec2(-2.0f, 2.0f), // lowp
943 Vec2(-1e3f, 1e3f), // mediump
944 Vec2(-1e7f, 1e7f) // highp
945 };
946
947 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
948 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
949 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
950 const int scalarSize = glu::getDataTypeScalarSize(type);
951
952 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
953 }
954
compare(const void * const * inputs,const void * const * outputs)955 bool compare (const void* const* inputs, const void* const* outputs)
956 {
957 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
958 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
959 const bool hasZeroSign = supportsSignedZero(precision);
960 const int scalarSize = glu::getDataTypeScalarSize(type);
961
962 const int mantissaBits = getMinMantissaBits(precision);
963
964 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
965 {
966 const float in0 = ((const float*)inputs[0])[compNdx];
967 const float out0 = ((const float*)outputs[0])[compNdx];
968 const float out1 = ((const float*)outputs[1])[compNdx];
969
970 const float refOut1 = float(int(in0));
971 const float refOut0 = in0 - refOut1;
972
973 const int bitsLost = precision != glu::PRECISION_HIGHP ? numBitsLostInOp(in0, refOut0) : 0;
974 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(mantissaBits - bitsLost, 0));
975
976 const float resSum = out0 + out1;
977
978 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(resSum, in0) : getUlpDiffIgnoreZeroSign(resSum, in0);
979
980 if (ulpDiff > maxUlpDiff)
981 {
982 m_failMsg << "Expected [" << compNdx << "] = (" << HexFloat(refOut0) << ") + (" << HexFloat(refOut1) << ") = " << HexFloat(in0) << " with ULP threshold "
983 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
984 return false;
985 }
986 }
987
988 return true;
989 }
990 };
991
992 class ModfCase : public CommonFunctionCase
993 {
994 public:
ModfCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)995 ModfCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
996 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "modf", shaderType)
997 {
998 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
999 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1000 m_spec.outputs.push_back(Symbol("out1", glu::VarType(baseType, precision)));
1001 m_spec.source = "out0 = modf(in0, out1);";
1002 init();
1003 }
1004
createInstance(Context & ctx) const1005 TestInstance* createInstance (Context& ctx) const
1006 {
1007 return new ModfCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1008 }
1009 };
1010
1011 class IsnanCaseInstance : public CommonFunctionTestInstance
1012 {
1013 public:
IsnanCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1014 IsnanCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1015 : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
1016 {
1017 }
1018
getInputValues(int numValues,void * const * values) const1019 void getInputValues (int numValues, void* const* values) const
1020 {
1021 de::Random rnd (deStringHash(m_name) ^ 0xc2a39fu);
1022 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1023 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1024 const int scalarSize = glu::getDataTypeScalarSize(type);
1025 const int mantissaBits = getMinMantissaBits(precision);
1026 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
1027
1028 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
1029 {
1030 const bool isNan = rnd.getFloat() > 0.3f;
1031 const bool isInf = !isNan && rnd.getFloat() > 0.4f;
1032 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
1033 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
1034 const deUint32 sign = rnd.getUint32() & 0x1u;
1035 const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
1036
1037 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
1038
1039 ((deUint32*)values[0])[valNdx] = value;
1040 }
1041 }
1042
compare(const void * const * inputs,const void * const * outputs)1043 bool compare (const void* const* inputs, const void* const* outputs)
1044 {
1045 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1046 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1047 const int scalarSize = glu::getDataTypeScalarSize(type);
1048
1049 if (precision == glu::PRECISION_HIGHP)
1050 {
1051 // Only highp is required to support inf/nan
1052 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1053 {
1054 const float in0 = ((const float*)inputs[0])[compNdx];
1055 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
1056 const bool ref = tcu::Float32(in0).isNaN();
1057
1058 if (out0 != ref)
1059 {
1060 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
1061 return false;
1062 }
1063 }
1064 }
1065 else if (precision == glu::PRECISION_MEDIUMP || precision == glu::PRECISION_LOWP)
1066 {
1067 // NaN support is optional, check that inputs that are not NaN don't result in true.
1068 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1069 {
1070 const float in0 = ((const float*)inputs[0])[compNdx];
1071 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
1072 const bool ref = tcu::Float32(in0).isNaN();
1073
1074 if (!ref && out0)
1075 {
1076 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
1077 return false;
1078 }
1079 }
1080 }
1081
1082 return true;
1083 }
1084 };
1085
1086 class IsnanCase : public CommonFunctionCase
1087 {
1088 public:
IsnanCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1089 IsnanCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1090 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isnan", shaderType)
1091 {
1092 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
1093
1094 const int vecSize = glu::getDataTypeScalarSize(baseType);
1095 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
1096
1097 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1098 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
1099 m_spec.source = "out0 = isnan(in0);";
1100 init();
1101 }
1102
createInstance(Context & ctx) const1103 TestInstance* createInstance (Context& ctx) const
1104 {
1105 return new IsnanCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1106 }
1107 };
1108
1109 class IsinfCaseInstance : public CommonFunctionTestInstance
1110 {
1111 public:
IsinfCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1112 IsinfCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1113 : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
1114 {
1115 }
1116
getInputValues(int numValues,void * const * values) const1117 void getInputValues (int numValues, void* const* values) const
1118 {
1119 de::Random rnd (deStringHash(m_name) ^ 0xc2a39fu);
1120 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1121 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1122 const int scalarSize = glu::getDataTypeScalarSize(type);
1123 const int mantissaBits = getMinMantissaBits(precision);
1124 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
1125
1126 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
1127 {
1128 const bool isInf = rnd.getFloat() > 0.3f;
1129 const bool isNan = !isInf && rnd.getFloat() > 0.4f;
1130 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
1131 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
1132 const deUint32 sign = rnd.getUint32() & 0x1u;
1133 const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
1134
1135 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
1136
1137 ((deUint32*)values[0])[valNdx] = value;
1138 }
1139 }
1140
compare(const void * const * inputs,const void * const * outputs)1141 bool compare (const void* const* inputs, const void* const* outputs)
1142 {
1143 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1144 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1145 const int scalarSize = glu::getDataTypeScalarSize(type);
1146
1147 if (precision == glu::PRECISION_HIGHP)
1148 {
1149 // Only highp is required to support inf/nan
1150 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1151 {
1152 const float in0 = ((const float*)inputs[0])[compNdx];
1153 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
1154 const bool ref = tcu::Float32(in0).isInf();
1155
1156 if (out0 != ref)
1157 {
1158 m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
1159 return false;
1160 }
1161 }
1162 }
1163 else if (precision == glu::PRECISION_MEDIUMP)
1164 {
1165 // Inf support is optional, check that inputs that are not Inf in mediump don't result in true.
1166 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1167 {
1168 const float in0 = ((const float*)inputs[0])[compNdx];
1169 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
1170 const bool ref = tcu::Float16(in0).isInf();
1171
1172 if (!ref && out0)
1173 {
1174 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
1175 return false;
1176 }
1177 }
1178 }
1179 // else: no verification can be performed
1180
1181 return true;
1182 }
1183 };
1184
1185 class IsinfCase : public CommonFunctionCase
1186 {
1187 public:
IsinfCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1188 IsinfCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1189 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isinf", shaderType)
1190 {
1191 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
1192
1193 const int vecSize = glu::getDataTypeScalarSize(baseType);
1194 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
1195
1196 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1197 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
1198 m_spec.source = "out0 = isinf(in0);";
1199 init();
1200 }
1201
createInstance(Context & ctx) const1202 TestInstance* createInstance (Context& ctx) const
1203 {
1204 return new IsinfCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1205 }
1206 };
1207
1208 class FloatBitsToUintIntCaseInstance : public CommonFunctionTestInstance
1209 {
1210 public:
FloatBitsToUintIntCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1211 FloatBitsToUintIntCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1212 : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
1213 {
1214 }
1215
getInputValues(int numValues,void * const * values) const1216 void getInputValues (int numValues, void* const* values) const
1217 {
1218 const Vec2 ranges[] =
1219 {
1220 Vec2(-2.0f, 2.0f), // lowp
1221 Vec2(-1e3f, 1e3f), // mediump
1222 Vec2(-1e7f, 1e7f) // highp
1223 };
1224
1225 de::Random rnd (deStringHash(m_name) ^ 0x2790au);
1226 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1227 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1228 const int scalarSize = glu::getDataTypeScalarSize(type);
1229
1230 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
1231 }
1232
compare(const void * const * inputs,const void * const * outputs)1233 bool compare (const void* const* inputs, const void* const* outputs)
1234 {
1235 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1236 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1237 const int scalarSize = glu::getDataTypeScalarSize(type);
1238
1239 const int mantissaBits = getMinMantissaBits(precision);
1240 const int maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
1241
1242 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1243 {
1244 const float in0 = ((const float*)inputs[0])[compNdx];
1245 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
1246 const deUint32 refOut0 = tcu::Float32(in0).bits();
1247 const int ulpDiff = de::abs((int)out0 - (int)refOut0);
1248
1249 if (ulpDiff > maxUlpDiff)
1250 {
1251 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold "
1252 << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1253 return false;
1254 }
1255 }
1256
1257 return true;
1258 }
1259 };
1260
1261 class FloatBitsToUintIntCase : public CommonFunctionCase
1262 {
1263 public:
FloatBitsToUintIntCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType,bool outIsSigned)1264 FloatBitsToUintIntCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType, bool outIsSigned)
1265 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), outIsSigned ? "floatBitsToInt" : "floatBitsToUint", shaderType)
1266 {
1267 const int vecSize = glu::getDataTypeScalarSize(baseType);
1268 const glu::DataType intType = outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT)
1269 : (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT);
1270
1271 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1272 m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP)));
1273 m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);";
1274 init();
1275 }
1276
createInstance(Context & ctx) const1277 TestInstance* createInstance (Context& ctx) const
1278 {
1279 return new FloatBitsToUintIntCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1280 }
1281 };
1282
1283 class FloatBitsToIntCaseInstance : public FloatBitsToUintIntCaseInstance
1284 {
1285 public:
FloatBitsToIntCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1286 FloatBitsToIntCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1287 : FloatBitsToUintIntCaseInstance (context, shaderType, spec, executor, numValues, name)
1288 {
1289 }
1290 };
1291
1292 class FloatBitsToIntCase : public FloatBitsToUintIntCase
1293 {
1294 public:
FloatBitsToIntCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1295 FloatBitsToIntCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1296 : FloatBitsToUintIntCase (testCtx, baseType, precision, shaderType, true)
1297 {
1298 }
1299
1300 };
1301
1302 class FloatBitsToUintCaseInstance : public FloatBitsToUintIntCaseInstance
1303 {
1304 public:
FloatBitsToUintCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1305 FloatBitsToUintCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1306 : FloatBitsToUintIntCaseInstance (context, shaderType, spec, executor, numValues, name)
1307 {
1308 }
1309 };
1310
1311 class FloatBitsToUintCase : public FloatBitsToUintIntCase
1312 {
1313 public:
FloatBitsToUintCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1314 FloatBitsToUintCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1315 : FloatBitsToUintIntCase (testCtx, baseType, precision, shaderType, false)
1316 {
1317 }
1318 };
1319
1320 class BitsToFloatCaseInstance : public CommonFunctionTestInstance
1321 {
1322 public:
BitsToFloatCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1323 BitsToFloatCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1324 : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
1325 {
1326 }
1327
getInputValues(int numValues,void * const * values) const1328 void getInputValues (int numValues, void* const* values) const
1329 {
1330 de::Random rnd (deStringHash(m_name) ^ 0xbbb225u);
1331 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1332 const int scalarSize = glu::getDataTypeScalarSize(type);
1333 const Vec2 range (-1e8f, +1e8f);
1334
1335 // \note Filled as floats.
1336 fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize);
1337 }
1338
compare(const void * const * inputs,const void * const * outputs)1339 bool compare (const void* const* inputs, const void* const* outputs)
1340 {
1341 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1342 const int scalarSize = glu::getDataTypeScalarSize(type);
1343 const deUint32 maxUlpDiff = 0;
1344
1345 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1346 {
1347 const float in0 = ((const float*)inputs[0])[compNdx];
1348 const float out0 = ((const float*)outputs[0])[compNdx];
1349 const deUint32 ulpDiff = getUlpDiff(in0, out0);
1350
1351 if (ulpDiff > maxUlpDiff)
1352 {
1353 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits()) << " with ULP threshold "
1354 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
1355 return false;
1356 }
1357 }
1358
1359 return true;
1360 }
1361 };
1362
1363 class BitsToFloatCase : public CommonFunctionCase
1364 {
1365 public:
BitsToFloatCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::ShaderType shaderType)1366 BitsToFloatCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::ShaderType shaderType)
1367 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP, shaderType).c_str(), glu::isDataTypeIntOrIVec(baseType) ? "intBitsToFloat" : "uintBitsToFloat", shaderType)
1368 {
1369 const bool inIsSigned = glu::isDataTypeIntOrIVec(baseType);
1370 const int vecSize = glu::getDataTypeScalarSize(baseType);
1371 const glu::DataType floatType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1372
1373 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1374 m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP)));
1375 m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);";
1376 init();
1377 }
1378
createInstance(Context & ctx) const1379 TestInstance* createInstance (Context& ctx) const
1380 {
1381 return new BitsToFloatCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1382 }
1383 };
1384
1385 class FloorCaseInstance : public CommonFunctionTestInstance
1386 {
1387 public:
FloorCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1388 FloorCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1389 : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
1390 {
1391 }
1392
getInputValues(int numValues,void * const * values) const1393 void getInputValues (int numValues, void* const* values) const
1394 {
1395 const Vec2 ranges[] =
1396 {
1397 Vec2(-2.0f, 2.0f), // lowp
1398 Vec2(-1e3f, 1e3f), // mediump
1399 Vec2(-1e7f, 1e7f) // highp
1400 };
1401
1402 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
1403 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1404 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1405 const int scalarSize = glu::getDataTypeScalarSize(type);
1406 // Random cases.
1407 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1408
1409 // If precision is mediump, make sure values can be represented in fp16 exactly
1410 if (precision == glu::PRECISION_MEDIUMP)
1411 {
1412 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1413 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1414 }
1415 }
1416
compare(const void * const * inputs,const void * const * outputs)1417 bool compare (const void* const* inputs, const void* const* outputs)
1418 {
1419 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1420 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1421 const int scalarSize = glu::getDataTypeScalarSize(type);
1422
1423 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1424 {
1425 // Require exact result.
1426 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1427 {
1428 const float in0 = ((const float*)inputs[0])[compNdx];
1429 const float out0 = ((const float*)outputs[0])[compNdx];
1430 const float ref = deFloatFloor(in0);
1431
1432 const deUint32 ulpDiff = getUlpDiff(out0, ref);
1433
1434 if (ulpDiff > 0)
1435 {
1436 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1437 return false;
1438 }
1439 }
1440 }
1441 else
1442 {
1443 const int mantissaBits = getMinMantissaBits(precision);
1444 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1445 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1446
1447 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1448 {
1449 const float in0 = ((const float*)inputs[0])[compNdx];
1450 const float out0 = ((const float*)outputs[0])[compNdx];
1451 const int minRes = int(deFloatFloor(in0-eps));
1452 const int maxRes = int(deFloatFloor(in0+eps));
1453 bool anyOk = false;
1454
1455 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1456 {
1457 const deUint32 ulpDiff = getUlpDiff(out0, float(roundedVal));
1458
1459 if (ulpDiff <= maxUlpDiff)
1460 {
1461 anyOk = true;
1462 break;
1463 }
1464 }
1465
1466 if (!anyOk)
1467 {
1468 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1469 return false;
1470 }
1471 }
1472 }
1473
1474 return true;
1475 }
1476 };
1477
1478 class FloorCase : public CommonFunctionCase
1479 {
1480 public:
FloorCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1481 FloorCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1482 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "floor", shaderType)
1483 {
1484 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1485 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1486 m_spec.source = "out0 = floor(in0);";
1487 init();
1488 }
1489
createInstance(Context & ctx) const1490 TestInstance* createInstance (Context& ctx) const
1491 {
1492 return new FloorCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1493 }
1494 };
1495
1496 class TruncCaseInstance : public CommonFunctionTestInstance
1497 {
1498 public:
TruncCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1499 TruncCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1500 : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
1501 {
1502 }
1503
getInputValues(int numValues,void * const * values) const1504 void getInputValues (int numValues, void* const* values) const
1505 {
1506 const Vec2 ranges[] =
1507 {
1508 Vec2(-2.0f, 2.0f), // lowp
1509 Vec2(-1e3f, 1e3f), // mediump
1510 Vec2(-1e7f, 1e7f) // highp
1511 };
1512
1513 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
1514 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1515 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1516 const int scalarSize = glu::getDataTypeScalarSize(type);
1517 const float specialCases[] = { 0.0f, -0.0f, -0.9f, 0.9f, 1.0f, -1.0f };
1518 const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
1519
1520 // Special cases
1521 for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
1522 {
1523 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1524 ((float*)values[0])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx];
1525 }
1526
1527 // Random cases.
1528 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + scalarSize*numSpecialCases, (numValues-numSpecialCases)*scalarSize);
1529
1530 // If precision is mediump, make sure values can be represented in fp16 exactly
1531 if (precision == glu::PRECISION_MEDIUMP)
1532 {
1533 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1534 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1535 }
1536 }
1537
compare(const void * const * inputs,const void * const * outputs)1538 bool compare (const void* const* inputs, const void* const* outputs)
1539 {
1540 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1541 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1542 const int scalarSize = glu::getDataTypeScalarSize(type);
1543
1544 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1545 {
1546 // Require exact result.
1547 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1548 {
1549 const float in0 = ((const float*)inputs[0])[compNdx];
1550 const float out0 = ((const float*)outputs[0])[compNdx];
1551 const bool isNeg = tcu::Float32(in0).sign() < 0;
1552 const float ref = isNeg ? (-float(int(-in0))) : float(int(in0));
1553
1554 // \note: trunc() function definition is a bit broad on negative zeros. Ignore result sign if zero.
1555 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1556
1557 if (ulpDiff > 0)
1558 {
1559 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1560 return false;
1561 }
1562 }
1563 }
1564 else
1565 {
1566 const int mantissaBits = getMinMantissaBits(precision);
1567 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1568 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1569
1570 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1571 {
1572 const float in0 = ((const float*)inputs[0])[compNdx];
1573 const float out0 = ((const float*)outputs[0])[compNdx];
1574 const int minRes = int(in0-eps);
1575 const int maxRes = int(in0+eps);
1576 bool anyOk = false;
1577
1578 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1579 {
1580 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1581
1582 if (ulpDiff <= maxUlpDiff)
1583 {
1584 anyOk = true;
1585 break;
1586 }
1587 }
1588
1589 if (!anyOk)
1590 {
1591 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1592 return false;
1593 }
1594 }
1595 }
1596
1597 return true;
1598 }
1599 };
1600
1601 class TruncCase : public CommonFunctionCase
1602 {
1603 public:
TruncCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1604 TruncCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1605 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "trunc", shaderType)
1606 {
1607 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1608 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1609 m_spec.source = "out0 = trunc(in0);";
1610 init();
1611 }
1612
createInstance(Context & ctx) const1613 TestInstance* createInstance (Context& ctx) const
1614 {
1615 return new TruncCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1616 }
1617 };
1618
1619 class RoundCaseInstance : public CommonFunctionTestInstance
1620 {
1621 public:
RoundCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1622 RoundCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1623 : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
1624 {
1625 }
1626
getInputValues(int numValues,void * const * values) const1627 void getInputValues (int numValues, void* const* values) const
1628 {
1629 const Vec2 ranges[] =
1630 {
1631 Vec2(-2.0f, 2.0f), // lowp
1632 Vec2(-1e3f, 1e3f), // mediump
1633 Vec2(-1e7f, 1e7f) // highp
1634 };
1635
1636 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
1637 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1638 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1639 const int scalarSize = glu::getDataTypeScalarSize(type);
1640 int numSpecialCases = 0;
1641
1642 // Special cases.
1643 if (precision != glu::PRECISION_LOWP)
1644 {
1645 DE_ASSERT(numValues >= 10);
1646 for (int ndx = 0; ndx < 10; ndx++)
1647 {
1648 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1649 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1650 numSpecialCases += 1;
1651 }
1652 }
1653
1654 // Random cases.
1655 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1656
1657 // If precision is mediump, make sure values can be represented in fp16 exactly
1658 if (precision == glu::PRECISION_MEDIUMP)
1659 {
1660 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1661 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1662 }
1663 }
1664
compare(const void * const * inputs,const void * const * outputs)1665 bool compare (const void* const* inputs, const void* const* outputs)
1666 {
1667 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1668 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1669 const bool hasZeroSign = supportsSignedZero(precision);
1670 const int scalarSize = glu::getDataTypeScalarSize(type);
1671
1672 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1673 {
1674 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1675 {
1676 const float in0 = ((const float*)inputs[0])[compNdx];
1677 const float out0 = ((const float*)outputs[0])[compNdx];
1678
1679 if (deFloatFrac(in0) == 0.5f)
1680 {
1681 // Allow both ceil(in) and floor(in)
1682 const float ref0 = deFloatFloor(in0);
1683 const float ref1 = deFloatCeil(in0);
1684 const deUint32 ulpDiff0 = hasZeroSign ? getUlpDiff(out0, ref0) : getUlpDiffIgnoreZeroSign(out0, ref0);
1685 const deUint32 ulpDiff1 = hasZeroSign ? getUlpDiff(out0, ref1) : getUlpDiffIgnoreZeroSign(out0, ref1);
1686
1687 if (ulpDiff0 > 0 && ulpDiff1 > 0)
1688 {
1689 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " or " << HexFloat(ref1) << ", got ULP diff " << tcu::toHex(de::min(ulpDiff0, ulpDiff1));
1690 return false;
1691 }
1692 }
1693 else
1694 {
1695 // Require exact result
1696 const float ref = roundEven(in0);
1697 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1698
1699 if (ulpDiff > 0)
1700 {
1701 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1702 return false;
1703 }
1704 }
1705 }
1706 }
1707 else
1708 {
1709 const int mantissaBits = getMinMantissaBits(precision);
1710 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1711 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1712
1713 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1714 {
1715 const float in0 = ((const float*)inputs[0])[compNdx];
1716 const float out0 = ((const float*)outputs[0])[compNdx];
1717 const int minRes = int(roundEven(in0-eps));
1718 const int maxRes = int(roundEven(in0+eps));
1719 bool anyOk = false;
1720
1721 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1722 {
1723 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1724
1725 if (ulpDiff <= maxUlpDiff)
1726 {
1727 anyOk = true;
1728 break;
1729 }
1730 }
1731
1732 if (!anyOk)
1733 {
1734 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1735 return false;
1736 }
1737 }
1738 }
1739
1740 return true;
1741 }
1742 };
1743
1744 class RoundCase : public CommonFunctionCase
1745 {
1746 public:
RoundCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1747 RoundCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1748 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "round", shaderType)
1749 {
1750 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1751 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1752 m_spec.source = "out0 = round(in0);";
1753 init();
1754 }
1755
createInstance(Context & ctx) const1756 TestInstance* createInstance (Context& ctx) const
1757 {
1758 return new RoundCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1759 }
1760 };
1761
1762 class CeilCaseInstance : public CommonFunctionTestInstance
1763 {
1764 public:
CeilCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1765 CeilCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1766 : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
1767 {
1768 }
1769
getInputValues(int numValues,void * const * values) const1770 void getInputValues (int numValues, void* const* values) const
1771 {
1772 const Vec2 ranges[] =
1773 {
1774 Vec2(-2.0f, 2.0f), // lowp
1775 Vec2(-1e3f, 1e3f), // mediump
1776 Vec2(-1e7f, 1e7f) // highp
1777 };
1778
1779 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
1780 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1781 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1782 const int scalarSize = glu::getDataTypeScalarSize(type);
1783
1784 // Random cases.
1785 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1786
1787 // If precision is mediump, make sure values can be represented in fp16 exactly
1788 if (precision == glu::PRECISION_MEDIUMP)
1789 {
1790 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1791 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1792 }
1793 }
1794
compare(const void * const * inputs,const void * const * outputs)1795 bool compare (const void* const* inputs, const void* const* outputs)
1796 {
1797 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1798 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1799 const bool hasZeroSign = supportsSignedZero(precision);
1800 const int scalarSize = glu::getDataTypeScalarSize(type);
1801
1802 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1803 {
1804 // Require exact result.
1805 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1806 {
1807 const float in0 = ((const float*)inputs[0])[compNdx];
1808 const float out0 = ((const float*)outputs[0])[compNdx];
1809 const float ref = deFloatCeil(in0);
1810
1811 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1812
1813 if (ulpDiff > 0)
1814 {
1815 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1816 return false;
1817 }
1818 }
1819 }
1820 else
1821 {
1822 const int mantissaBits = getMinMantissaBits(precision);
1823 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1824 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1825
1826 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1827 {
1828 const float in0 = ((const float*)inputs[0])[compNdx];
1829 const float out0 = ((const float*)outputs[0])[compNdx];
1830 const int minRes = int(deFloatCeil(in0-eps));
1831 const int maxRes = int(deFloatCeil(in0+eps));
1832 bool anyOk = false;
1833
1834 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1835 {
1836 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1837
1838 if (ulpDiff <= maxUlpDiff)
1839 {
1840 anyOk = true;
1841 break;
1842 }
1843 }
1844
1845 if (!anyOk && de::inRange(0, minRes, maxRes))
1846 {
1847 // Allow -0 as well.
1848 const int ulpDiff = de::abs((int)tcu::Float32(out0).bits() - (int)0x80000000u);
1849 anyOk = ((deUint32)ulpDiff <= maxUlpDiff);
1850 }
1851
1852 if (!anyOk)
1853 {
1854 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1855 return false;
1856 }
1857 }
1858 }
1859
1860 return true;
1861 }
1862 };
1863
1864 class CeilCase : public CommonFunctionCase
1865 {
1866 public:
CeilCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1867 CeilCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1868 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ceil", shaderType)
1869 {
1870 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1871 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1872 m_spec.source = "out0 = ceil(in0);";
1873 init();
1874 }
1875
createInstance(Context & ctx) const1876 TestInstance* createInstance (Context& ctx) const
1877 {
1878 return new CeilCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
1879 }
1880 };
1881
1882 class FractCaseInstance : public CommonFunctionTestInstance
1883 {
1884 public:
FractCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)1885 FractCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
1886 : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
1887 {
1888 }
1889
getInputValues(int numValues,void * const * values) const1890 void getInputValues (int numValues, void* const* values) const
1891 {
1892 const Vec2 ranges[] =
1893 {
1894 Vec2(-2.0f, 2.0f), // lowp
1895 Vec2(-1e3f, 1e3f), // mediump
1896 Vec2(-1e7f, 1e7f) // highp
1897 };
1898
1899 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
1900 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1901 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1902 const int scalarSize = glu::getDataTypeScalarSize(type);
1903 int numSpecialCases = 0;
1904
1905 // Special cases.
1906 if (precision != glu::PRECISION_LOWP)
1907 {
1908 DE_ASSERT(numValues >= 10);
1909 for (int ndx = 0; ndx < 10; ndx++)
1910 {
1911 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1912 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1913 numSpecialCases += 1;
1914 }
1915 }
1916
1917 // Random cases.
1918 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1919
1920 // If precision is mediump, make sure values can be represented in fp16 exactly
1921 if (precision == glu::PRECISION_MEDIUMP)
1922 {
1923 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1924 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1925 }
1926 }
1927
compare(const void * const * inputs,const void * const * outputs)1928 bool compare (const void* const* inputs, const void* const* outputs)
1929 {
1930 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1931 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1932 const bool hasZeroSign = supportsSignedZero(precision);
1933 const int scalarSize = glu::getDataTypeScalarSize(type);
1934
1935 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1936 {
1937 // Require exact result.
1938 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1939 {
1940 const float in0 = ((const float*)inputs[0])[compNdx];
1941 const float out0 = ((const float*)outputs[0])[compNdx];
1942 const float ref = deFloatFrac(in0);
1943
1944 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1945
1946 if (ulpDiff > 0)
1947 {
1948 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1949 return false;
1950 }
1951 }
1952 }
1953 else
1954 {
1955 const int mantissaBits = getMinMantissaBits(precision);
1956 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1957
1958 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1959 {
1960 const float in0 = ((const float*)inputs[0])[compNdx];
1961 const float out0 = ((const float*)outputs[0])[compNdx];
1962
1963 if (int(deFloatFloor(in0-eps)) == int(deFloatFloor(in0+eps)))
1964 {
1965 const float ref = deFloatFrac(in0);
1966 const int bitsLost = numBitsLostInOp(in0, ref);
1967 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(0, mantissaBits-bitsLost)); // ULP diff for rounded integer value.
1968 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1969
1970 if (ulpDiff > maxUlpDiff)
1971 {
1972 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << " with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1973 return false;
1974 }
1975 }
1976 else
1977 {
1978 if (out0 >= 1.0f)
1979 {
1980 m_failMsg << "Expected [" << compNdx << "] < 1.0";
1981 return false;
1982 }
1983 }
1984 }
1985 }
1986
1987 return true;
1988 }
1989 };
1990
1991 class FractCase : public CommonFunctionCase
1992 {
1993 public:
FractCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1994 FractCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1995 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fract", shaderType)
1996 {
1997 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1998 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1999 m_spec.source = "out0 = fract(in0);";
2000 init();
2001 }
2002
createInstance(Context & ctx) const2003 TestInstance* createInstance (Context& ctx) const
2004 {
2005 return new FractCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
2006 }
2007 };
2008
2009 class FrexpCaseInstance : public CommonFunctionTestInstance
2010 {
2011 public:
FrexpCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)2012 FrexpCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
2013 : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
2014 {
2015 }
2016
getInputValues(int numValues,void * const * values) const2017 void getInputValues (int numValues, void* const* values) const
2018 {
2019 const Vec2 ranges[] =
2020 {
2021 Vec2(-2.0f, 2.0f), // lowp
2022 Vec2(-1e3f, 1e3f), // mediump
2023 Vec2(-1e7f, 1e7f) // highp
2024 };
2025
2026 de::Random rnd (deStringHash(m_name) ^ 0x2790au);
2027 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2028 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2029 const int scalarSize = glu::getDataTypeScalarSize(type);
2030
2031 // Special cases
2032 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2033 {
2034 ((float*)values[0])[scalarSize*0 + compNdx] = 0.0f;
2035 ((float*)values[0])[scalarSize*1 + compNdx] = -0.0f;
2036 ((float*)values[0])[scalarSize*2 + compNdx] = 0.5f;
2037 ((float*)values[0])[scalarSize*3 + compNdx] = -0.5f;
2038 ((float*)values[0])[scalarSize*4 + compNdx] = 1.0f;
2039 ((float*)values[0])[scalarSize*5 + compNdx] = -1.0f;
2040 ((float*)values[0])[scalarSize*6 + compNdx] = 2.0f;
2041 ((float*)values[0])[scalarSize*7 + compNdx] = -2.0f;
2042 }
2043
2044 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + 8*scalarSize, (numValues-8)*scalarSize);
2045
2046 // Make sure the values are representable in the target format
2047 for (int caseNdx = 0; caseNdx < numValues; ++caseNdx)
2048 {
2049 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
2050 {
2051 float* const valuePtr = &((float*)values[0])[caseNdx * scalarSize + scalarNdx];
2052
2053 *valuePtr = makeFloatRepresentable(*valuePtr, precision);
2054 }
2055 }
2056 }
2057
compare(const void * const * inputs,const void * const * outputs)2058 bool compare (const void* const* inputs, const void* const* outputs)
2059 {
2060 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2061 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2062 const int scalarSize = glu::getDataTypeScalarSize(type);
2063 const bool transitSupportsSignedZero = (m_shaderType != glu::SHADERTYPE_FRAGMENT); // executor cannot reliably transit negative zero to fragment stage
2064 const bool signedZero = supportsSignedZero(precision) && transitSupportsSignedZero;
2065
2066 const int mantissaBits = getMinMantissaBits(precision);
2067 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
2068
2069 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2070 {
2071 const float in0 = ((const float*)inputs[0])[compNdx];
2072 const float out0 = ((const float*)outputs[0])[compNdx];
2073 const int out1 = ((const int*)outputs[1])[compNdx];
2074
2075 float refOut0;
2076 int refOut1;
2077
2078 frexp(in0, &refOut0, &refOut1);
2079
2080 const deUint32 ulpDiff0 = signedZero ? getUlpDiff(out0, refOut0) : getUlpDiffIgnoreZeroSign(out0, refOut0);
2081
2082 if (ulpDiff0 > maxUlpDiff || out1 != refOut1)
2083 {
2084 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", " << refOut1 << " with ULP threshold "
2085 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff0);
2086 return false;
2087 }
2088 }
2089
2090 return true;
2091 }
2092 };
2093
2094 class FrexpCase : public CommonFunctionCase
2095 {
2096 public:
FrexpCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)2097 FrexpCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
2098 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "frexp", shaderType)
2099 {
2100 const int vecSize = glu::getDataTypeScalarSize(baseType);
2101 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
2102
2103 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
2104 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
2105 m_spec.outputs.push_back(Symbol("out1", glu::VarType(intType, glu::PRECISION_HIGHP)));
2106 m_spec.source = "out0 = frexp(in0, out1);";
2107 init();
2108 }
2109
createInstance(Context & ctx) const2110 TestInstance* createInstance (Context& ctx) const
2111 {
2112 return new FrexpCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
2113 }
2114 };
2115
2116 class LdexpCaseInstance : public CommonFunctionTestInstance
2117 {
2118 public:
LdexpCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)2119 LdexpCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
2120 : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
2121 {
2122 }
2123
getInputValues(int numValues,void * const * values) const2124 void getInputValues (int numValues, void* const* values) const
2125 {
2126 const Vec2 ranges[] =
2127 {
2128 Vec2(-2.0f, 2.0f), // lowp
2129 Vec2(-1e3f, 1e3f), // mediump
2130 Vec2(-1e7f, 1e7f) // highp
2131 };
2132
2133 de::Random rnd (deStringHash(m_name) ^ 0x2790au);
2134 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2135 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2136 const int scalarSize = glu::getDataTypeScalarSize(type);
2137 int valueNdx = 0;
2138
2139 {
2140 const float easySpecialCases[] = { 0.0f, -0.0f, 0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f };
2141
2142 DE_ASSERT(valueNdx + DE_LENGTH_OF_ARRAY(easySpecialCases) <= numValues);
2143 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(easySpecialCases); caseNdx++)
2144 {
2145 float in0;
2146 int in1;
2147
2148 frexp(easySpecialCases[caseNdx], &in0, &in1);
2149
2150 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2151 {
2152 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
2153 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
2154 }
2155
2156 valueNdx += 1;
2157 }
2158 }
2159
2160 {
2161 // \note lowp and mediump can not necessarily fit the values in hard cases, so we'll use only easy ones.
2162 const int numEasyRandomCases = precision == glu::PRECISION_HIGHP ? 50 : (numValues-valueNdx);
2163
2164 DE_ASSERT(valueNdx + numEasyRandomCases <= numValues);
2165 for (int caseNdx = 0; caseNdx < numEasyRandomCases; caseNdx++)
2166 {
2167 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2168 {
2169 const float in = rnd.getFloat(ranges[precision].x(), ranges[precision].y());
2170 float in0;
2171 int in1;
2172
2173 frexp(in, &in0, &in1);
2174
2175 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
2176 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
2177 }
2178
2179 valueNdx += 1;
2180 }
2181 }
2182
2183 {
2184 const int numHardRandomCases = numValues-valueNdx;
2185 DE_ASSERT(numHardRandomCases >= 0 && valueNdx + numHardRandomCases <= numValues);
2186
2187 for (int caseNdx = 0; caseNdx < numHardRandomCases; caseNdx++)
2188 {
2189 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2190 {
2191 const int fpExp = rnd.getInt(-126, 127);
2192 const int sign = rnd.getBool() ? -1 : +1;
2193 const deUint32 mantissa = (1u<<23) | (rnd.getUint32() & ((1u<<23)-1));
2194 const int in1 = rnd.getInt(de::max(-126, -126-fpExp), de::min(127, 127-fpExp));
2195 const float in0 = tcu::Float32::construct(sign, fpExp, mantissa).asFloat();
2196
2197 DE_ASSERT(de::inRange(in1, -126, 127)); // See Khronos bug 11180
2198 DE_ASSERT(de::inRange(in1+fpExp, -126, 127));
2199
2200 const float out = ldexp(in0, in1);
2201
2202 DE_ASSERT(!tcu::Float32(out).isInf() && !tcu::Float32(out).isDenorm());
2203 DE_UNREF(out);
2204
2205 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
2206 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
2207 }
2208
2209 valueNdx += 1;
2210 }
2211 }
2212 }
2213
compare(const void * const * inputs,const void * const * outputs)2214 bool compare (const void* const* inputs, const void* const* outputs)
2215 {
2216 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2217 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2218 const int scalarSize = glu::getDataTypeScalarSize(type);
2219
2220 const int mantissaBits = getMinMantissaBits(precision);
2221 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
2222
2223 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2224 {
2225 const float in0 = ((const float*)inputs[0])[compNdx];
2226 const int in1 = ((const int*)inputs[1])[compNdx];
2227 const float out0 = ((const float*)outputs[0])[compNdx];
2228 const float refOut0 = ldexp(in0, in1);
2229 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, refOut0);
2230
2231 const int inExp = tcu::Float32(in0).exponent();
2232
2233 if (ulpDiff > maxUlpDiff)
2234 {
2235 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", (exp = " << inExp << ") with ULP threshold "
2236 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
2237 return false;
2238 }
2239 }
2240
2241 return true;
2242 }
2243 };
2244
2245 class LdexpCase : public CommonFunctionCase
2246 {
2247 public:
LdexpCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)2248 LdexpCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
2249 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ldexp", shaderType)
2250 {
2251 const int vecSize = glu::getDataTypeScalarSize(baseType);
2252 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
2253
2254 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
2255 m_spec.inputs.push_back(Symbol("in1", glu::VarType(intType, glu::PRECISION_HIGHP)));
2256 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
2257 m_spec.source = "out0 = ldexp(in0, in1);";
2258 init();
2259 }
2260
createInstance(Context & ctx) const2261 TestInstance* createInstance (Context& ctx) const
2262 {
2263 return new LdexpCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
2264 }
2265 };
2266
2267 class FmaCaseInstance : public CommonFunctionTestInstance
2268 {
2269 public:
FmaCaseInstance(Context & context,glu::ShaderType shaderType,ShaderSpec spec,ShaderExecutor & executor,int numValues,const char * name)2270 FmaCaseInstance (Context& context, glu::ShaderType shaderType, ShaderSpec spec, ShaderExecutor& executor, int numValues, const char* name)
2271 : CommonFunctionTestInstance (context, shaderType, spec, executor, numValues, name)
2272 {
2273 }
2274
getInputValues(int numValues,void * const * values) const2275 void getInputValues (int numValues, void* const* values) const
2276 {
2277 const Vec2 ranges[] =
2278 {
2279 Vec2(-2.0f, 2.0f), // lowp
2280 Vec2(-127.f, 127.f), // mediump
2281 Vec2(-1e7f, 1e7f) // highp
2282 };
2283
2284 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
2285 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2286 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2287 const int scalarSize = glu::getDataTypeScalarSize(type);
2288 const float specialCases[][3] =
2289 {
2290 // a b c
2291 { 0.0f, 0.0f, 0.0f },
2292 { 0.0f, 1.0f, 0.0f },
2293 { 0.0f, 0.0f, -1.0f },
2294 { 1.0f, 1.0f, 0.0f },
2295 { 1.0f, 1.0f, 1.0f },
2296 { -1.0f, 1.0f, 0.0f },
2297 { 1.0f, -1.0f, 0.0f },
2298 { -1.0f, -1.0f, 0.0f },
2299 { -0.0f, 1.0f, 0.0f },
2300 { 1.0f, -0.0f, 0.0f }
2301 };
2302 const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
2303
2304 // Special cases
2305 for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
2306 {
2307 for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2308 {
2309 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
2310 ((float*)values[inputNdx])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx][inputNdx];
2311 }
2312 }
2313
2314 // Random cases.
2315 {
2316 const int numScalars = (numValues-numSpecialCases)*scalarSize;
2317 const int offs = scalarSize*numSpecialCases;
2318
2319 for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2320 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[inputNdx] + offs, numScalars);
2321 }
2322
2323 // Make sure the values are representable in the target format
2324 for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2325 {
2326 for (int caseNdx = 0; caseNdx < numValues; ++caseNdx)
2327 {
2328 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
2329 {
2330 float* const valuePtr = &((float*)values[inputNdx])[caseNdx * scalarSize + scalarNdx];
2331
2332 *valuePtr = makeFloatRepresentable(*valuePtr, precision);
2333 }
2334 }
2335 }
2336 }
2337
fma(glu::Precision precision,float a,float b,float c)2338 static tcu::Interval fma (glu::Precision precision, float a, float b, float c)
2339 {
2340 const tcu::FloatFormat formats[] =
2341 {
2342 // minExp maxExp mantissa exact, subnormals infinities NaN
2343 tcu::FloatFormat(0, 0, 7, false, tcu::YES, tcu::MAYBE, tcu::MAYBE),
2344 tcu::FloatFormat(-13, 13, 9, false, tcu::MAYBE, tcu::MAYBE, tcu::MAYBE),
2345 tcu::FloatFormat(-126, 127, 23, true, tcu::MAYBE, tcu::YES, tcu::MAYBE)
2346 };
2347 const tcu::FloatFormat& format = de::getSizedArrayElement<glu::PRECISION_LAST>(formats, precision);
2348 const tcu::Interval ia = format.convert(a);
2349 const tcu::Interval ib = format.convert(b);
2350 const tcu::Interval ic = format.convert(c);
2351 tcu::Interval prod0;
2352 tcu::Interval prod1;
2353 tcu::Interval prod2;
2354 tcu::Interval prod3;
2355 tcu::Interval prod;
2356 tcu::Interval res;
2357
2358 TCU_SET_INTERVAL(prod0, tmp, tmp = ia.lo() * ib.lo());
2359 TCU_SET_INTERVAL(prod1, tmp, tmp = ia.lo() * ib.hi());
2360 TCU_SET_INTERVAL(prod2, tmp, tmp = ia.hi() * ib.lo());
2361 TCU_SET_INTERVAL(prod3, tmp, tmp = ia.hi() * ib.hi());
2362
2363 prod = format.convert(format.roundOut(prod0 | prod1 | prod2 | prod3, ia.isFinite() && ib.isFinite()));
2364
2365 TCU_SET_INTERVAL_BOUNDS(res, tmp,
2366 tmp = prod.lo() + ic.lo(),
2367 tmp = prod.hi() + ic.hi());
2368
2369 return format.convert(format.roundOut(res, prod.isFinite() && ic.isFinite()));
2370 }
2371
compare(const void * const * inputs,const void * const * outputs)2372 bool compare (const void* const* inputs, const void* const* outputs)
2373 {
2374 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2375 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2376 const int scalarSize = glu::getDataTypeScalarSize(type);
2377
2378 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2379 {
2380 const float a = ((const float*)inputs[0])[compNdx];
2381 const float b = ((const float*)inputs[1])[compNdx];
2382 const float c = ((const float*)inputs[2])[compNdx];
2383 const float res = ((const float*)outputs[0])[compNdx];
2384 const tcu::Interval ref = fma(precision, a, b, c);
2385
2386 if (!ref.contains(res))
2387 {
2388 m_failMsg << "Expected [" << compNdx << "] = " << ref;
2389 return false;
2390 }
2391 }
2392
2393 return true;
2394 }
2395 };
2396
2397 class FmaCase : public CommonFunctionCase
2398 {
2399 public:
init(void)2400 void init (void)
2401 {
2402 CommonFunctionCase::init();
2403 }
2404
FmaCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)2405 FmaCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
2406 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fma", shaderType)
2407 {
2408 m_spec.inputs.push_back(Symbol("a", glu::VarType(baseType, precision)));
2409 m_spec.inputs.push_back(Symbol("b", glu::VarType(baseType, precision)));
2410 m_spec.inputs.push_back(Symbol("c", glu::VarType(baseType, precision)));
2411 m_spec.outputs.push_back(Symbol("res", glu::VarType(baseType, precision)));
2412 m_spec.source = "res = fma(a, b, c);";
2413 m_spec.globalDeclarations = "#extension GL_EXT_gpu_shader5 : require\n";
2414 init();
2415 }
2416
createInstance(Context & ctx) const2417 TestInstance* createInstance (Context& ctx) const
2418 {
2419 return new FmaCaseInstance(ctx, m_shaderType, m_spec, *m_executor, m_numValues, getName());
2420 }
2421 };
2422
2423 } // anonymous
2424
ShaderCommonFunctionTests(tcu::TestContext & testCtx)2425 ShaderCommonFunctionTests::ShaderCommonFunctionTests (tcu::TestContext& testCtx)
2426 : tcu::TestCaseGroup (testCtx, "common", "Common function tests")
2427 {
2428 }
2429
~ShaderCommonFunctionTests(void)2430 ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void)
2431 {
2432 }
2433
init(void)2434 void ShaderCommonFunctionTests::init (void)
2435 {
2436 enum
2437 {
2438 VS = (1<<glu::SHADERTYPE_VERTEX),
2439 TC = (1<<glu::SHADERTYPE_TESSELLATION_CONTROL),
2440 TE = (1<<glu::SHADERTYPE_TESSELLATION_EVALUATION),
2441 GS = (1<<glu::SHADERTYPE_GEOMETRY),
2442 FS = (1<<glu::SHADERTYPE_FRAGMENT),
2443 CS = (1<<glu::SHADERTYPE_COMPUTE),
2444
2445 ALL_SHADERS = VS|TC|TE|GS|FS|CS,
2446 NEW_SHADERS = TC|TE|GS|CS,
2447 };
2448
2449 // Float? Int? Uint? Shaders
2450 addFunctionCases<AbsCase> (this, "abs", true, true, false, ALL_SHADERS);
2451 addFunctionCases<SignCase> (this, "sign", true, true, false, ALL_SHADERS);
2452 addFunctionCases<FloorCase> (this, "floor", true, false, false, ALL_SHADERS);
2453 addFunctionCases<TruncCase> (this, "trunc", true, false, false, ALL_SHADERS);
2454 addFunctionCases<RoundCase> (this, "round", true, false, false, ALL_SHADERS);
2455 addFunctionCases<RoundEvenCase> (this, "roundeven", true, false, false, ALL_SHADERS);
2456 addFunctionCases<CeilCase> (this, "ceil", true, false, false, ALL_SHADERS);
2457 addFunctionCases<FractCase> (this, "fract", true, false, false, ALL_SHADERS);
2458 // mod
2459 addFunctionCases<ModfCase> (this, "modf", true, false, false, ALL_SHADERS);
2460 // min
2461 // max
2462 // clamp
2463 // mix
2464 // step
2465 // smoothstep
2466 addFunctionCases<IsnanCase> (this, "isnan", true, false, false, ALL_SHADERS);
2467 addFunctionCases<IsinfCase> (this, "isinf", true, false, false, ALL_SHADERS);
2468 addFunctionCases<FloatBitsToIntCase> (this, "floatbitstoint", true, false, false, ALL_SHADERS);
2469 addFunctionCases<FloatBitsToUintCase> (this, "floatbitstouint", true, false, false, ALL_SHADERS);
2470
2471 addFunctionCases<FrexpCase> (this, "frexp", true, false, false, ALL_SHADERS);
2472 addFunctionCases<LdexpCase> (this, "ldexp", true, false, false, ALL_SHADERS);
2473 addFunctionCases<FmaCase> (this, "fma", true, false, false, ALL_SHADERS);
2474
2475 // (u)intBitsToFloat()
2476 {
2477 const deUint32 shaderBits = NEW_SHADERS;
2478 tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "intbitstofloat", "intBitsToFloat() Tests");
2479 tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat", "uintBitsToFloat() Tests");
2480
2481 addChild(intGroup);
2482 addChild(uintGroup);
2483
2484 for (int vecSize = 1; vecSize < 4; vecSize++)
2485 {
2486 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
2487 const glu::DataType uintType = vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT;
2488
2489 for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
2490 {
2491 if (shaderBits & (1<<shaderType))
2492 {
2493 intGroup->addChild(new BitsToFloatCase(getTestContext(), intType, glu::ShaderType(shaderType)));
2494 uintGroup->addChild(new BitsToFloatCase(getTestContext(), uintType, glu::ShaderType(shaderType)));
2495 }
2496 }
2497 }
2498 }
2499 }
2500
2501 } // shaderexecutor
2502 } // vkt
2503