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::__anon9069f6a10111::VecArrayAccess67 VecArrayAccess (const void* ptr) : m_array((tcu::Vector<T, Size>*)ptr) {}
~VecArrayAccessvkt::shaderexecutor::__anon9069f6a10111::VecArrayAccess68 ~VecArrayAccess (void) {}
69
operator []vkt::shaderexecutor::__anon9069f6a10111::VecArrayAccess70 const tcu::Vector<T, Size>& operator[] (size_t offset) const { return m_array[offset]; }
operator []vkt::shaderexecutor::__anon9069f6a10111::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::__anon9069f6a10111::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::__anon9069f6a10111::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::__anon9069f6a10111::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 generateSources(m_shaderType, m_spec, programCollection);
451 }
452
453 virtual TestInstance* createInstance (Context& context) const = 0;
454
455 protected:
456 CommonFunctionCase (const CommonFunctionCase&);
457 CommonFunctionCase& operator= (const CommonFunctionCase&);
458
459 const glu::ShaderType m_shaderType;
460 ShaderSpec m_spec;
461 const int m_numValues;
462 };
463
CommonFunctionCase(tcu::TestContext & testCtx,const char * name,const char * description,glu::ShaderType shaderType)464 CommonFunctionCase::CommonFunctionCase (tcu::TestContext& testCtx, const char* name, const char* description, glu::ShaderType shaderType)
465 : TestCase (testCtx, name, description)
466 , m_shaderType (shaderType)
467 , m_numValues (100)
468 {
469 }
470
~CommonFunctionCase(void)471 CommonFunctionCase::~CommonFunctionCase (void)
472 {
473 }
474
475 // CommonFunctionTestInstance
476
477 class CommonFunctionTestInstance : public TestInstance
478 {
479 public:
CommonFunctionTestInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)480 CommonFunctionTestInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
481 : TestInstance (context)
482 , m_shaderType (shaderType)
483 , m_spec (spec)
484 , m_numValues (numValues)
485 , m_name (name)
486 , m_executor (createExecutor(context, shaderType, spec))
487 {
488 }
489 virtual tcu::TestStatus iterate (void);
490
491 protected:
492 virtual void getInputValues (int numValues, void* const* values) const = 0;
493 virtual bool compare (const void* const* inputs, const void* const* outputs) = 0;
494
495 const glu::ShaderType m_shaderType;
496 const ShaderSpec m_spec;
497 const int m_numValues;
498
499 // \todo [2017-03-07 pyry] Hack used to generate seeds for test cases - get rid of this.
500 const char* m_name;
501
502 std::ostringstream m_failMsg; //!< Comparison failure help message.
503
504 de::UniquePtr<ShaderExecutor> m_executor;
505 };
506
iterate(void)507 tcu::TestStatus CommonFunctionTestInstance::iterate (void)
508 {
509 const int numInputScalars = computeTotalScalarSize(m_spec.inputs);
510 const int numOutputScalars = computeTotalScalarSize(m_spec.outputs);
511 vector<deUint32> inputData (numInputScalars * m_numValues);
512 vector<deUint32> outputData (numOutputScalars * m_numValues);
513 const vector<void*> inputPointers = getInputOutputPointers(m_spec.inputs, inputData, m_numValues);
514 const vector<void*> outputPointers = getInputOutputPointers(m_spec.outputs, outputData, m_numValues);
515
516 // Initialize input data.
517 getInputValues(m_numValues, &inputPointers[0]);
518
519 // Execute shader.
520 m_executor->execute(m_numValues, &inputPointers[0], &outputPointers[0]);
521
522 // Compare results.
523 {
524 const vector<int> inScalarSizes = getScalarSizes(m_spec.inputs);
525 const vector<int> outScalarSizes = getScalarSizes(m_spec.outputs);
526 vector<void*> curInputPtr (inputPointers.size());
527 vector<void*> curOutputPtr (outputPointers.size());
528 int numFailed = 0;
529 tcu::TestContext& testCtx = m_context.getTestContext();
530
531 for (int valNdx = 0; valNdx < m_numValues; valNdx++)
532 {
533 // Set up pointers for comparison.
534 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx)
535 curInputPtr[inNdx] = (deUint32*)inputPointers[inNdx] + inScalarSizes[inNdx]*valNdx;
536
537 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx)
538 curOutputPtr[outNdx] = (deUint32*)outputPointers[outNdx] + outScalarSizes[outNdx]*valNdx;
539
540 if (!compare(&curInputPtr[0], &curOutputPtr[0]))
541 {
542 // \todo [2013-08-08 pyry] We probably want to log reference value as well?
543
544 testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n " << m_failMsg.str() << TestLog::EndMessage;
545
546 testCtx.getLog() << TestLog::Message << " inputs:" << TestLog::EndMessage;
547 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++)
548 testCtx.getLog() << TestLog::Message << " " << m_spec.inputs[inNdx].name << " = "
549 << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx])
550 << TestLog::EndMessage;
551
552 testCtx.getLog() << TestLog::Message << " outputs:" << TestLog::EndMessage;
553 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++)
554 testCtx.getLog() << TestLog::Message << " " << m_spec.outputs[outNdx].name << " = "
555 << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx])
556 << TestLog::EndMessage;
557
558 m_failMsg.str("");
559 m_failMsg.clear();
560 numFailed += 1;
561 }
562 }
563
564 testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage;
565
566 if (numFailed == 0)
567 return tcu::TestStatus::pass("Pass");
568 else
569 return tcu::TestStatus::fail("Result comparison failed");
570 }
571 }
572
573 // Test cases
574
575 class AbsCaseInstance : public CommonFunctionTestInstance
576 {
577 public:
AbsCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)578 AbsCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
579 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
580 {
581 }
582
getInputValues(int numValues,void * const * values) const583 void getInputValues (int numValues, void* const* values) const
584 {
585 const Vec2 floatRanges[] =
586 {
587 Vec2(-2.0f, 2.0f), // lowp
588 Vec2(-1e3f, 1e3f), // mediump
589 Vec2(-1e7f, 1e7f) // highp
590 };
591 const IVec2 intRanges[] =
592 {
593 IVec2(-(1<<7)+1, (1<<7)-1),
594 IVec2(-(1<<15)+1, (1<<15)-1),
595 IVec2(0x80000001, 0x7fffffff)
596 };
597
598 de::Random rnd (deStringHash(m_name) ^ 0x235facu);
599 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
600 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
601 const int scalarSize = glu::getDataTypeScalarSize(type);
602
603 if (glu::isDataTypeFloatOrVec(type))
604 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), values[0], numValues*scalarSize);
605 else
606 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize);
607 }
608
compare(const void * const * inputs,const void * const * outputs)609 bool compare (const void* const* inputs, const void* const* outputs)
610 {
611 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
612 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
613 const int scalarSize = glu::getDataTypeScalarSize(type);
614
615 if (glu::isDataTypeFloatOrVec(type))
616 {
617 const int mantissaBits = getMinMantissaBits(precision);
618 const deUint32 maxUlpDiff = (1u<<(23-mantissaBits))-1u;
619
620 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
621 {
622 const float in0 = ((const float*)inputs[0])[compNdx];
623 const float out0 = ((const float*)outputs[0])[compNdx];
624 const float ref0 = de::abs(in0);
625 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
626
627 if (ulpDiff0 > maxUlpDiff)
628 {
629 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
630 return false;
631 }
632 }
633 }
634 else
635 {
636 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
637 {
638 const int in0 = ((const int*)inputs[0])[compNdx];
639 const int out0 = ((const int*)outputs[0])[compNdx];
640 const int ref0 = de::abs(in0);
641
642 if (out0 != ref0)
643 {
644 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
645 return false;
646 }
647 }
648 }
649
650 return true;
651 }
652 };
653
654 class AbsCase : public CommonFunctionCase
655 {
656 public:
AbsCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)657 AbsCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
658 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "abs", shaderType)
659 {
660 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
661 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
662 m_spec.source = "out0 = abs(in0);";
663 }
664
createInstance(Context & ctx) const665 TestInstance* createInstance (Context& ctx) const
666 {
667 return new AbsCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
668 }
669 };
670
671 class SignCaseInstance : public CommonFunctionTestInstance
672 {
673 public:
SignCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)674 SignCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
675 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
676 {
677 }
678
getInputValues(int numValues,void * const * values) const679 void getInputValues (int numValues, void* const* values) const
680 {
681 const Vec2 floatRanges[] =
682 {
683 Vec2(-2.0f, 2.0f), // lowp
684 Vec2(-1e4f, 1e4f), // mediump - note: may end up as inf
685 Vec2(-1e8f, 1e8f) // highp - note: may end up as inf
686 };
687 const IVec2 intRanges[] =
688 {
689 IVec2(-(1<<7), (1<<7)-1),
690 IVec2(-(1<<15), (1<<15)-1),
691 IVec2(0x80000000, 0x7fffffff)
692 };
693
694 de::Random rnd (deStringHash(m_name) ^ 0x324u);
695 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
696 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
697 const int scalarSize = glu::getDataTypeScalarSize(type);
698
699 if (glu::isDataTypeFloatOrVec(type))
700 {
701 // Special cases.
702 std::fill((float*)values[0], (float*)values[0] + scalarSize, +1.0f);
703 std::fill((float*)values[0], (float*)values[0] + scalarSize, -1.0f);
704 std::fill((float*)values[0], (float*)values[0] + scalarSize, 0.0f);
705 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), (float*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
706 }
707 else
708 {
709 std::fill((int*)values[0], (int*)values[0] + scalarSize, +1);
710 std::fill((int*)values[0], (int*)values[0] + scalarSize, -1);
711 std::fill((int*)values[0], (int*)values[0] + scalarSize, 0);
712 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
713 }
714 }
715
compare(const void * const * inputs,const void * const * outputs)716 bool compare (const void* const* inputs, const void* const* outputs)
717 {
718 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
719 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
720 const int scalarSize = glu::getDataTypeScalarSize(type);
721
722 if (glu::isDataTypeFloatOrVec(type))
723 {
724 // Both highp and mediump should be able to represent -1, 0, and +1 exactly
725 const deUint32 maxUlpDiff = precision == glu::PRECISION_LOWP ? getMaxUlpDiffFromBits(getMinMantissaBits(precision)) : 0;
726
727 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
728 {
729 const float in0 = ((const float*)inputs[0])[compNdx];
730 const float out0 = ((const float*)outputs[0])[compNdx];
731 const float ref0 = in0 < 0.0f ? -1.0f :
732 in0 > 0.0f ? +1.0f : 0.0f;
733 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
734
735 if (ulpDiff0 > maxUlpDiff)
736 {
737 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
738 return false;
739 }
740 }
741 }
742 else
743 {
744 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
745 {
746 const int in0 = ((const int*)inputs[0])[compNdx];
747 const int out0 = ((const int*)outputs[0])[compNdx];
748 const int ref0 = in0 < 0 ? -1 :
749 in0 > 0 ? +1 : 0;
750
751 if (out0 != ref0)
752 {
753 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
754 return false;
755 }
756 }
757 }
758
759 return true;
760 }
761 };
762
763 class SignCase : public CommonFunctionCase
764 {
765 public:
SignCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)766 SignCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
767 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "sign", shaderType)
768 {
769 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
770 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
771 m_spec.source = "out0 = sign(in0);";
772 }
773
createInstance(Context & ctx) const774 TestInstance* createInstance (Context& ctx) const
775 {
776 return new SignCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
777 }
778 };
779
roundEven(float v)780 static float roundEven (float v)
781 {
782 const float q = deFloatFrac(v);
783 const int truncated = int(v-q);
784 const int rounded = (q > 0.5f) ? (truncated + 1) : // Rounded up
785 (q == 0.5f && (truncated % 2 != 0)) ? (truncated + 1) : // Round to nearest even at 0.5
786 truncated; // Rounded down
787
788 return float(rounded);
789 }
790
791 class RoundEvenCaseInstance : public CommonFunctionTestInstance
792 {
793 public:
RoundEvenCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)794 RoundEvenCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
795 : CommonFunctionTestInstance(context, shaderType, spec, numValues, name)
796 {
797 }
798
getInputValues(int numValues,void * const * values) const799 void getInputValues (int numValues, void* const* values) const
800 {
801 const Vec2 ranges[] =
802 {
803 Vec2(-2.0f, 2.0f), // lowp
804 Vec2(-1e3f, 1e3f), // mediump
805 Vec2(-1e7f, 1e7f) // highp
806 };
807
808 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
809 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
810 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
811 const int scalarSize = glu::getDataTypeScalarSize(type);
812 int numSpecialCases = 0;
813
814 // Special cases.
815 if (precision != glu::PRECISION_LOWP)
816 {
817 DE_ASSERT(numValues >= 20);
818 for (int ndx = 0; ndx < 20; ndx++)
819 {
820 const float v = de::clamp(float(ndx) - 10.5f, ranges[precision].x(), ranges[precision].y());
821 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
822 numSpecialCases += 1;
823 }
824 }
825
826 // Random cases.
827 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
828
829 // If precision is mediump, make sure values can be represented in fp16 exactly
830 if (precision == glu::PRECISION_MEDIUMP)
831 {
832 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
833 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
834 }
835 }
836
compare(const void * const * inputs,const void * const * outputs)837 bool compare (const void* const* inputs, const void* const* outputs)
838 {
839 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
840 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
841 const bool hasSignedZero = supportsSignedZero(precision);
842 const int scalarSize = glu::getDataTypeScalarSize(type);
843
844 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
845 {
846 // Require exact rounding result.
847 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
848 {
849 const float in0 = ((const float*)inputs[0])[compNdx];
850 const float out0 = ((const float*)outputs[0])[compNdx];
851 const float ref = roundEven(in0);
852
853 const deUint32 ulpDiff = hasSignedZero ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
854
855 if (ulpDiff > 0)
856 {
857 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
858 return false;
859 }
860 }
861 }
862 else
863 {
864 const int mantissaBits = getMinMantissaBits(precision);
865 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
866 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
867
868 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
869 {
870 const float in0 = ((const float*)inputs[0])[compNdx];
871 const float out0 = ((const float*)outputs[0])[compNdx];
872 const int minRes = int(roundEven(in0-eps));
873 const int maxRes = int(roundEven(in0+eps));
874 bool anyOk = false;
875
876 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
877 {
878 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
879
880 if (ulpDiff <= maxUlpDiff)
881 {
882 anyOk = true;
883 break;
884 }
885 }
886
887 if (!anyOk)
888 {
889 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
890 return false;
891 }
892 }
893 }
894
895 return true;
896 }
897 };
898
899 class RoundEvenCase : public CommonFunctionCase
900 {
901 public:
RoundEvenCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)902 RoundEvenCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
903 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "roundEven", shaderType)
904 {
905 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
906 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
907 m_spec.source = "out0 = roundEven(in0);";
908 }
909
createInstance(Context & ctx) const910 TestInstance* createInstance (Context& ctx) const
911 {
912 return new RoundEvenCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
913 }
914 };
915
916 class ModfCaseInstance : public CommonFunctionTestInstance
917 {
918 public:
ModfCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)919 ModfCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
920 : CommonFunctionTestInstance(context, shaderType, spec, numValues, name)
921 {
922 }
923
getInputValues(int numValues,void * const * values) const924 void getInputValues (int numValues, void* const* values) const
925 {
926 const Vec2 ranges[] =
927 {
928 Vec2(-2.0f, 2.0f), // lowp
929 Vec2(-1e3f, 1e3f), // mediump
930 Vec2(-1e7f, 1e7f) // highp
931 };
932
933 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
934 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
935 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
936 const int scalarSize = glu::getDataTypeScalarSize(type);
937
938 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
939 }
940
compare(const void * const * inputs,const void * const * outputs)941 bool compare (const void* const* inputs, const void* const* outputs)
942 {
943 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
944 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
945 const bool hasZeroSign = supportsSignedZero(precision);
946 const int scalarSize = glu::getDataTypeScalarSize(type);
947
948 const int mantissaBits = getMinMantissaBits(precision);
949
950 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
951 {
952 const float in0 = ((const float*)inputs[0])[compNdx];
953 const float out0 = ((const float*)outputs[0])[compNdx];
954 const float out1 = ((const float*)outputs[1])[compNdx];
955
956 const float refOut1 = float(int(in0));
957 const float refOut0 = in0 - refOut1;
958
959 const int bitsLost = precision != glu::PRECISION_HIGHP ? numBitsLostInOp(in0, refOut0) : 0;
960 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(mantissaBits - bitsLost, 0));
961
962 const float resSum = out0 + out1;
963
964 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(resSum, in0) : getUlpDiffIgnoreZeroSign(resSum, in0);
965
966 if (ulpDiff > maxUlpDiff)
967 {
968 m_failMsg << "Expected [" << compNdx << "] = (" << HexFloat(refOut0) << ") + (" << HexFloat(refOut1) << ") = " << HexFloat(in0) << " with ULP threshold "
969 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
970 return false;
971 }
972 }
973
974 return true;
975 }
976 };
977
978 class ModfCase : public CommonFunctionCase
979 {
980 public:
ModfCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)981 ModfCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
982 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "modf", shaderType)
983 {
984 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
985 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
986 m_spec.outputs.push_back(Symbol("out1", glu::VarType(baseType, precision)));
987 m_spec.source = "out0 = modf(in0, out1);";
988 }
989
createInstance(Context & ctx) const990 TestInstance* createInstance (Context& ctx) const
991 {
992 return new ModfCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
993 }
994 };
995
996 class IsnanCaseInstance : public CommonFunctionTestInstance
997 {
998 public:
IsnanCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)999 IsnanCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1000 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
1001 {
1002 }
1003
getInputValues(int numValues,void * const * values) const1004 void getInputValues (int numValues, void* const* values) const
1005 {
1006 de::Random rnd (deStringHash(m_name) ^ 0xc2a39fu);
1007 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1008 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1009 const int scalarSize = glu::getDataTypeScalarSize(type);
1010 const int mantissaBits = getMinMantissaBits(precision);
1011 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
1012
1013 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
1014 {
1015 const bool isNan = rnd.getFloat() > 0.3f;
1016 const bool isInf = !isNan && rnd.getFloat() > 0.4f;
1017 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
1018 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
1019 const deUint32 sign = rnd.getUint32() & 0x1u;
1020 const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
1021
1022 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
1023
1024 ((deUint32*)values[0])[valNdx] = value;
1025 }
1026 }
1027
compare(const void * const * inputs,const void * const * outputs)1028 bool compare (const void* const* inputs, const void* const* outputs)
1029 {
1030 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1031 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1032 const int scalarSize = glu::getDataTypeScalarSize(type);
1033
1034 if (precision == glu::PRECISION_HIGHP)
1035 {
1036 // Only highp is required to support inf/nan
1037 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1038 {
1039 const float in0 = ((const float*)inputs[0])[compNdx];
1040 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
1041 const bool ref = tcu::Float32(in0).isNaN();
1042
1043 if (out0 != ref)
1044 {
1045 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
1046 return false;
1047 }
1048 }
1049 }
1050 else if (precision == glu::PRECISION_MEDIUMP || precision == glu::PRECISION_LOWP)
1051 {
1052 // NaN support is optional, check that inputs that are not NaN don't result in true.
1053 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1054 {
1055 const float in0 = ((const float*)inputs[0])[compNdx];
1056 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
1057 const bool ref = tcu::Float32(in0).isNaN();
1058
1059 if (!ref && out0)
1060 {
1061 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
1062 return false;
1063 }
1064 }
1065 }
1066
1067 return true;
1068 }
1069 };
1070
1071 class IsnanCase : public CommonFunctionCase
1072 {
1073 public:
IsnanCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1074 IsnanCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1075 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isnan", shaderType)
1076 {
1077 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
1078
1079 const int vecSize = glu::getDataTypeScalarSize(baseType);
1080 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
1081
1082 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1083 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
1084 m_spec.source = "out0 = isnan(in0);";
1085 }
1086
createInstance(Context & ctx) const1087 TestInstance* createInstance (Context& ctx) const
1088 {
1089 return new IsnanCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1090 }
1091 };
1092
1093 class IsinfCaseInstance : public CommonFunctionTestInstance
1094 {
1095 public:
IsinfCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)1096 IsinfCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1097 : CommonFunctionTestInstance(context, shaderType, spec, numValues, name)
1098 {
1099 }
1100
getInputValues(int numValues,void * const * values) const1101 void getInputValues (int numValues, void* const* values) const
1102 {
1103 de::Random rnd (deStringHash(m_name) ^ 0xc2a39fu);
1104 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1105 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1106 const int scalarSize = glu::getDataTypeScalarSize(type);
1107 const int mantissaBits = getMinMantissaBits(precision);
1108 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
1109
1110 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
1111 {
1112 const bool isInf = rnd.getFloat() > 0.3f;
1113 const bool isNan = !isInf && rnd.getFloat() > 0.4f;
1114 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
1115 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
1116 const deUint32 sign = rnd.getUint32() & 0x1u;
1117 const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
1118
1119 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
1120
1121 ((deUint32*)values[0])[valNdx] = value;
1122 }
1123 }
1124
compare(const void * const * inputs,const void * const * outputs)1125 bool compare (const void* const* inputs, const void* const* outputs)
1126 {
1127 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1128 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1129 const int scalarSize = glu::getDataTypeScalarSize(type);
1130
1131 if (precision == glu::PRECISION_HIGHP)
1132 {
1133 // Only highp is required to support inf/nan
1134 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1135 {
1136 const float in0 = ((const float*)inputs[0])[compNdx];
1137 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
1138 const bool ref = tcu::Float32(in0).isInf();
1139
1140 if (out0 != ref)
1141 {
1142 m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
1143 return false;
1144 }
1145 }
1146 }
1147 else if (precision == glu::PRECISION_MEDIUMP)
1148 {
1149 // Inf support is optional, check that inputs that are not Inf in mediump don't result in true.
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::Float16(in0).isInf();
1155
1156 if (!ref && out0)
1157 {
1158 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
1159 return false;
1160 }
1161 }
1162 }
1163 // else: no verification can be performed
1164
1165 return true;
1166 }
1167 };
1168
1169 class IsinfCase : public CommonFunctionCase
1170 {
1171 public:
IsinfCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1172 IsinfCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1173 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isinf", shaderType)
1174 {
1175 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
1176
1177 const int vecSize = glu::getDataTypeScalarSize(baseType);
1178 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
1179
1180 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1181 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
1182 m_spec.source = "out0 = isinf(in0);";
1183 }
1184
createInstance(Context & ctx) const1185 TestInstance* createInstance (Context& ctx) const
1186 {
1187 return new IsinfCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1188 }
1189 };
1190
1191 class FloatBitsToUintIntCaseInstance : public CommonFunctionTestInstance
1192 {
1193 public:
FloatBitsToUintIntCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)1194 FloatBitsToUintIntCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1195 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
1196 {
1197 }
1198
getInputValues(int numValues,void * const * values) const1199 void getInputValues (int numValues, void* const* values) const
1200 {
1201 const Vec2 ranges[] =
1202 {
1203 Vec2(-2.0f, 2.0f), // lowp
1204 Vec2(-1e3f, 1e3f), // mediump
1205 Vec2(-1e7f, 1e7f) // highp
1206 };
1207
1208 de::Random rnd (deStringHash(m_name) ^ 0x2790au);
1209 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1210 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1211 const int scalarSize = glu::getDataTypeScalarSize(type);
1212
1213 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
1214 }
1215
compare(const void * const * inputs,const void * const * outputs)1216 bool compare (const void* const* inputs, const void* const* outputs)
1217 {
1218 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1219 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1220 const int scalarSize = glu::getDataTypeScalarSize(type);
1221
1222 const int mantissaBits = getMinMantissaBits(precision);
1223 const int maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
1224
1225 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1226 {
1227 const float in0 = ((const float*)inputs[0])[compNdx];
1228 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
1229 const deUint32 refOut0 = tcu::Float32(in0).bits();
1230 const int ulpDiff = de::abs((int)out0 - (int)refOut0);
1231
1232 if (ulpDiff > maxUlpDiff)
1233 {
1234 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold "
1235 << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1236 return false;
1237 }
1238 }
1239
1240 return true;
1241 }
1242 };
1243
1244 class FloatBitsToUintIntCase : public CommonFunctionCase
1245 {
1246 public:
FloatBitsToUintIntCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType,bool outIsSigned)1247 FloatBitsToUintIntCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType, bool outIsSigned)
1248 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), outIsSigned ? "floatBitsToInt" : "floatBitsToUint", shaderType)
1249 {
1250 const int vecSize = glu::getDataTypeScalarSize(baseType);
1251 const glu::DataType intType = outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT)
1252 : (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT);
1253
1254 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1255 m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP)));
1256 m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);";
1257 }
1258
createInstance(Context & ctx) const1259 TestInstance* createInstance (Context& ctx) const
1260 {
1261 return new FloatBitsToUintIntCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1262 }
1263 };
1264
1265 class FloatBitsToIntCaseInstance : public FloatBitsToUintIntCaseInstance
1266 {
1267 public:
FloatBitsToIntCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)1268 FloatBitsToIntCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1269 : FloatBitsToUintIntCaseInstance (context, shaderType, spec, numValues, name)
1270 {
1271 }
1272 };
1273
1274 class FloatBitsToIntCase : public FloatBitsToUintIntCase
1275 {
1276 public:
FloatBitsToIntCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1277 FloatBitsToIntCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1278 : FloatBitsToUintIntCase (testCtx, baseType, precision, shaderType, true)
1279 {
1280 }
1281
1282 };
1283
1284 class FloatBitsToUintCaseInstance : public FloatBitsToUintIntCaseInstance
1285 {
1286 public:
FloatBitsToUintCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)1287 FloatBitsToUintCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1288 : FloatBitsToUintIntCaseInstance (context, shaderType, spec, numValues, name)
1289 {
1290 }
1291 };
1292
1293 class FloatBitsToUintCase : public FloatBitsToUintIntCase
1294 {
1295 public:
FloatBitsToUintCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1296 FloatBitsToUintCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1297 : FloatBitsToUintIntCase (testCtx, baseType, precision, shaderType, false)
1298 {
1299 }
1300 };
1301
1302 class BitsToFloatCaseInstance : public CommonFunctionTestInstance
1303 {
1304 public:
BitsToFloatCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)1305 BitsToFloatCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1306 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
1307 {
1308 }
1309
getInputValues(int numValues,void * const * values) const1310 void getInputValues (int numValues, void* const* values) const
1311 {
1312 de::Random rnd (deStringHash(m_name) ^ 0xbbb225u);
1313 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1314 const int scalarSize = glu::getDataTypeScalarSize(type);
1315 const Vec2 range (-1e8f, +1e8f);
1316
1317 // \note Filled as floats.
1318 fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize);
1319 }
1320
compare(const void * const * inputs,const void * const * outputs)1321 bool compare (const void* const* inputs, const void* const* outputs)
1322 {
1323 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1324 const int scalarSize = glu::getDataTypeScalarSize(type);
1325 const deUint32 maxUlpDiff = 0;
1326
1327 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1328 {
1329 const float in0 = ((const float*)inputs[0])[compNdx];
1330 const float out0 = ((const float*)outputs[0])[compNdx];
1331 const deUint32 ulpDiff = getUlpDiff(in0, out0);
1332
1333 if (ulpDiff > maxUlpDiff)
1334 {
1335 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits()) << " with ULP threshold "
1336 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
1337 return false;
1338 }
1339 }
1340
1341 return true;
1342 }
1343 };
1344
1345 class BitsToFloatCase : public CommonFunctionCase
1346 {
1347 public:
BitsToFloatCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::ShaderType shaderType)1348 BitsToFloatCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::ShaderType shaderType)
1349 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP, shaderType).c_str(), glu::isDataTypeIntOrIVec(baseType) ? "intBitsToFloat" : "uintBitsToFloat", shaderType)
1350 {
1351 const bool inIsSigned = glu::isDataTypeIntOrIVec(baseType);
1352 const int vecSize = glu::getDataTypeScalarSize(baseType);
1353 const glu::DataType floatType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1354
1355 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1356 m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP)));
1357 m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);";
1358 }
1359
createInstance(Context & ctx) const1360 TestInstance* createInstance (Context& ctx) const
1361 {
1362 return new BitsToFloatCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1363 }
1364 };
1365
1366 class FloorCaseInstance : public CommonFunctionTestInstance
1367 {
1368 public:
FloorCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)1369 FloorCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1370 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
1371 {
1372 }
1373
getInputValues(int numValues,void * const * values) const1374 void getInputValues (int numValues, void* const* values) const
1375 {
1376 const Vec2 ranges[] =
1377 {
1378 Vec2(-2.0f, 2.0f), // lowp
1379 Vec2(-1e3f, 1e3f), // mediump
1380 Vec2(-1e7f, 1e7f) // highp
1381 };
1382
1383 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
1384 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1385 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1386 const int scalarSize = glu::getDataTypeScalarSize(type);
1387 // Random cases.
1388 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1389
1390 // If precision is mediump, make sure values can be represented in fp16 exactly
1391 if (precision == glu::PRECISION_MEDIUMP)
1392 {
1393 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1394 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1395 }
1396 }
1397
compare(const void * const * inputs,const void * const * outputs)1398 bool compare (const void* const* inputs, const void* const* outputs)
1399 {
1400 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1401 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1402 const int scalarSize = glu::getDataTypeScalarSize(type);
1403
1404 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1405 {
1406 // Require exact result.
1407 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1408 {
1409 const float in0 = ((const float*)inputs[0])[compNdx];
1410 const float out0 = ((const float*)outputs[0])[compNdx];
1411 const float ref = deFloatFloor(in0);
1412
1413 const deUint32 ulpDiff = getUlpDiff(out0, ref);
1414
1415 if (ulpDiff > 0)
1416 {
1417 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1418 return false;
1419 }
1420 }
1421 }
1422 else
1423 {
1424 const int mantissaBits = getMinMantissaBits(precision);
1425 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1426 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1427
1428 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1429 {
1430 const float in0 = ((const float*)inputs[0])[compNdx];
1431 const float out0 = ((const float*)outputs[0])[compNdx];
1432 const int minRes = int(deFloatFloor(in0-eps));
1433 const int maxRes = int(deFloatFloor(in0+eps));
1434 bool anyOk = false;
1435
1436 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1437 {
1438 const deUint32 ulpDiff = getUlpDiff(out0, float(roundedVal));
1439
1440 if (ulpDiff <= maxUlpDiff)
1441 {
1442 anyOk = true;
1443 break;
1444 }
1445 }
1446
1447 if (!anyOk)
1448 {
1449 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1450 return false;
1451 }
1452 }
1453 }
1454
1455 return true;
1456 }
1457 };
1458
1459 class FloorCase : public CommonFunctionCase
1460 {
1461 public:
FloorCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1462 FloorCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1463 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "floor", shaderType)
1464 {
1465 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1466 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1467 m_spec.source = "out0 = floor(in0);";
1468 }
1469
createInstance(Context & ctx) const1470 TestInstance* createInstance (Context& ctx) const
1471 {
1472 return new FloorCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1473 }
1474 };
1475
1476 class TruncCaseInstance : public CommonFunctionTestInstance
1477 {
1478 public:
TruncCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)1479 TruncCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1480 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
1481 {
1482 }
1483
getInputValues(int numValues,void * const * values) const1484 void getInputValues (int numValues, void* const* values) const
1485 {
1486 const Vec2 ranges[] =
1487 {
1488 Vec2(-2.0f, 2.0f), // lowp
1489 Vec2(-1e3f, 1e3f), // mediump
1490 Vec2(-1e7f, 1e7f) // highp
1491 };
1492
1493 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
1494 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1495 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1496 const int scalarSize = glu::getDataTypeScalarSize(type);
1497 const float specialCases[] = { 0.0f, -0.0f, -0.9f, 0.9f, 1.0f, -1.0f };
1498 const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
1499
1500 // Special cases
1501 for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
1502 {
1503 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1504 ((float*)values[0])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx];
1505 }
1506
1507 // Random cases.
1508 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + scalarSize*numSpecialCases, (numValues-numSpecialCases)*scalarSize);
1509
1510 // If precision is mediump, make sure values can be represented in fp16 exactly
1511 if (precision == glu::PRECISION_MEDIUMP)
1512 {
1513 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1514 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1515 }
1516 }
1517
compare(const void * const * inputs,const void * const * outputs)1518 bool compare (const void* const* inputs, const void* const* outputs)
1519 {
1520 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1521 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1522 const int scalarSize = glu::getDataTypeScalarSize(type);
1523
1524 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1525 {
1526 // Require exact result.
1527 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1528 {
1529 const float in0 = ((const float*)inputs[0])[compNdx];
1530 const float out0 = ((const float*)outputs[0])[compNdx];
1531 const bool isNeg = tcu::Float32(in0).sign() < 0;
1532 const float ref = isNeg ? (-float(int(-in0))) : float(int(in0));
1533
1534 // \note: trunc() function definition is a bit broad on negative zeros. Ignore result sign if zero.
1535 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1536
1537 if (ulpDiff > 0)
1538 {
1539 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1540 return false;
1541 }
1542 }
1543 }
1544 else
1545 {
1546 const int mantissaBits = getMinMantissaBits(precision);
1547 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1548 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1549
1550 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1551 {
1552 const float in0 = ((const float*)inputs[0])[compNdx];
1553 const float out0 = ((const float*)outputs[0])[compNdx];
1554 const int minRes = int(in0-eps);
1555 const int maxRes = int(in0+eps);
1556 bool anyOk = false;
1557
1558 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1559 {
1560 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1561
1562 if (ulpDiff <= maxUlpDiff)
1563 {
1564 anyOk = true;
1565 break;
1566 }
1567 }
1568
1569 if (!anyOk)
1570 {
1571 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1572 return false;
1573 }
1574 }
1575 }
1576
1577 return true;
1578 }
1579 };
1580
1581 class TruncCase : public CommonFunctionCase
1582 {
1583 public:
TruncCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1584 TruncCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1585 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "trunc", shaderType)
1586 {
1587 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1588 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1589 m_spec.source = "out0 = trunc(in0);";
1590 }
1591
createInstance(Context & ctx) const1592 TestInstance* createInstance (Context& ctx) const
1593 {
1594 return new TruncCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1595 }
1596 };
1597
1598 class RoundCaseInstance : public CommonFunctionTestInstance
1599 {
1600 public:
RoundCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)1601 RoundCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1602 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
1603 {
1604 }
1605
getInputValues(int numValues,void * const * values) const1606 void getInputValues (int numValues, void* const* values) const
1607 {
1608 const Vec2 ranges[] =
1609 {
1610 Vec2(-2.0f, 2.0f), // lowp
1611 Vec2(-1e3f, 1e3f), // mediump
1612 Vec2(-1e7f, 1e7f) // highp
1613 };
1614
1615 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
1616 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1617 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1618 const int scalarSize = glu::getDataTypeScalarSize(type);
1619 int numSpecialCases = 0;
1620
1621 // Special cases.
1622 if (precision != glu::PRECISION_LOWP)
1623 {
1624 DE_ASSERT(numValues >= 10);
1625 for (int ndx = 0; ndx < 10; ndx++)
1626 {
1627 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1628 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1629 numSpecialCases += 1;
1630 }
1631 }
1632
1633 // Random cases.
1634 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1635
1636 // If precision is mediump, make sure values can be represented in fp16 exactly
1637 if (precision == glu::PRECISION_MEDIUMP)
1638 {
1639 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1640 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1641 }
1642 }
1643
compare(const void * const * inputs,const void * const * outputs)1644 bool compare (const void* const* inputs, const void* const* outputs)
1645 {
1646 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1647 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1648 const bool hasZeroSign = supportsSignedZero(precision);
1649 const int scalarSize = glu::getDataTypeScalarSize(type);
1650
1651 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1652 {
1653 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1654 {
1655 const float in0 = ((const float*)inputs[0])[compNdx];
1656 const float out0 = ((const float*)outputs[0])[compNdx];
1657
1658 if (deFloatFrac(in0) == 0.5f)
1659 {
1660 // Allow both ceil(in) and floor(in)
1661 const float ref0 = deFloatFloor(in0);
1662 const float ref1 = deFloatCeil(in0);
1663 const deUint32 ulpDiff0 = hasZeroSign ? getUlpDiff(out0, ref0) : getUlpDiffIgnoreZeroSign(out0, ref0);
1664 const deUint32 ulpDiff1 = hasZeroSign ? getUlpDiff(out0, ref1) : getUlpDiffIgnoreZeroSign(out0, ref1);
1665
1666 if (ulpDiff0 > 0 && ulpDiff1 > 0)
1667 {
1668 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " or " << HexFloat(ref1) << ", got ULP diff " << tcu::toHex(de::min(ulpDiff0, ulpDiff1));
1669 return false;
1670 }
1671 }
1672 else
1673 {
1674 // Require exact result
1675 const float ref = roundEven(in0);
1676 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1677
1678 if (ulpDiff > 0)
1679 {
1680 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1681 return false;
1682 }
1683 }
1684 }
1685 }
1686 else
1687 {
1688 const int mantissaBits = getMinMantissaBits(precision);
1689 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1690 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1691
1692 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1693 {
1694 const float in0 = ((const float*)inputs[0])[compNdx];
1695 const float out0 = ((const float*)outputs[0])[compNdx];
1696 const int minRes = int(roundEven(in0-eps));
1697 const int maxRes = int(roundEven(in0+eps));
1698 bool anyOk = false;
1699
1700 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1701 {
1702 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1703
1704 if (ulpDiff <= maxUlpDiff)
1705 {
1706 anyOk = true;
1707 break;
1708 }
1709 }
1710
1711 if (!anyOk)
1712 {
1713 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1714 return false;
1715 }
1716 }
1717 }
1718
1719 return true;
1720 }
1721 };
1722
1723 class RoundCase : public CommonFunctionCase
1724 {
1725 public:
RoundCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1726 RoundCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1727 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "round", shaderType)
1728 {
1729 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1730 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1731 m_spec.source = "out0 = round(in0);";
1732 }
1733
createInstance(Context & ctx) const1734 TestInstance* createInstance (Context& ctx) const
1735 {
1736 return new RoundCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1737 }
1738 };
1739
1740 class CeilCaseInstance : public CommonFunctionTestInstance
1741 {
1742 public:
CeilCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)1743 CeilCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1744 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
1745 {
1746 }
1747
getInputValues(int numValues,void * const * values) const1748 void getInputValues (int numValues, void* const* values) const
1749 {
1750 const Vec2 ranges[] =
1751 {
1752 Vec2(-2.0f, 2.0f), // lowp
1753 Vec2(-1e3f, 1e3f), // mediump
1754 Vec2(-1e7f, 1e7f) // highp
1755 };
1756
1757 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
1758 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1759 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1760 const int scalarSize = glu::getDataTypeScalarSize(type);
1761
1762 // Random cases.
1763 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1764
1765 // If precision is mediump, make sure values can be represented in fp16 exactly
1766 if (precision == glu::PRECISION_MEDIUMP)
1767 {
1768 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1769 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1770 }
1771 }
1772
compare(const void * const * inputs,const void * const * outputs)1773 bool compare (const void* const* inputs, const void* const* outputs)
1774 {
1775 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1776 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1777 const bool hasZeroSign = supportsSignedZero(precision);
1778 const int scalarSize = glu::getDataTypeScalarSize(type);
1779
1780 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1781 {
1782 // Require exact result.
1783 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1784 {
1785 const float in0 = ((const float*)inputs[0])[compNdx];
1786 const float out0 = ((const float*)outputs[0])[compNdx];
1787 const float ref = deFloatCeil(in0);
1788
1789 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1790
1791 if (ulpDiff > 0)
1792 {
1793 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1794 return false;
1795 }
1796 }
1797 }
1798 else
1799 {
1800 const int mantissaBits = getMinMantissaBits(precision);
1801 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1802 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1803
1804 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1805 {
1806 const float in0 = ((const float*)inputs[0])[compNdx];
1807 const float out0 = ((const float*)outputs[0])[compNdx];
1808 const int minRes = int(deFloatCeil(in0-eps));
1809 const int maxRes = int(deFloatCeil(in0+eps));
1810 bool anyOk = false;
1811
1812 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1813 {
1814 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1815
1816 if (ulpDiff <= maxUlpDiff)
1817 {
1818 anyOk = true;
1819 break;
1820 }
1821 }
1822
1823 if (!anyOk && de::inRange(0, minRes, maxRes))
1824 {
1825 // Allow -0 as well.
1826 const int ulpDiff = de::abs((int)tcu::Float32(out0).bits() - (int)0x80000000u);
1827 anyOk = ((deUint32)ulpDiff <= maxUlpDiff);
1828 }
1829
1830 if (!anyOk)
1831 {
1832 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1833 return false;
1834 }
1835 }
1836 }
1837
1838 return true;
1839 }
1840 };
1841
1842 class CeilCase : public CommonFunctionCase
1843 {
1844 public:
CeilCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1845 CeilCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1846 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ceil", shaderType)
1847 {
1848 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1849 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1850 m_spec.source = "out0 = ceil(in0);";
1851 }
1852
createInstance(Context & ctx) const1853 TestInstance* createInstance (Context& ctx) const
1854 {
1855 return new CeilCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1856 }
1857 };
1858
1859 class FractCaseInstance : public CommonFunctionTestInstance
1860 {
1861 public:
FractCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)1862 FractCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1863 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
1864 {
1865 }
1866
getInputValues(int numValues,void * const * values) const1867 void getInputValues (int numValues, void* const* values) const
1868 {
1869 const Vec2 ranges[] =
1870 {
1871 Vec2(-2.0f, 2.0f), // lowp
1872 Vec2(-1e3f, 1e3f), // mediump
1873 Vec2(-1e7f, 1e7f) // highp
1874 };
1875
1876 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
1877 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1878 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1879 const int scalarSize = glu::getDataTypeScalarSize(type);
1880 int numSpecialCases = 0;
1881
1882 // Special cases.
1883 if (precision != glu::PRECISION_LOWP)
1884 {
1885 DE_ASSERT(numValues >= 10);
1886 for (int ndx = 0; ndx < 10; ndx++)
1887 {
1888 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1889 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1890 numSpecialCases += 1;
1891 }
1892 }
1893
1894 // Random cases.
1895 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1896
1897 // If precision is mediump, make sure values can be represented in fp16 exactly
1898 if (precision == glu::PRECISION_MEDIUMP)
1899 {
1900 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1901 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1902 }
1903 }
1904
compare(const void * const * inputs,const void * const * outputs)1905 bool compare (const void* const* inputs, const void* const* outputs)
1906 {
1907 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1908 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1909 const bool hasZeroSign = supportsSignedZero(precision);
1910 const int scalarSize = glu::getDataTypeScalarSize(type);
1911
1912 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1913 {
1914 // Require exact result.
1915 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1916 {
1917 const float in0 = ((const float*)inputs[0])[compNdx];
1918 const float out0 = ((const float*)outputs[0])[compNdx];
1919 const float ref = deFloatFrac(in0);
1920
1921 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1922
1923 if (ulpDiff > 0)
1924 {
1925 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1926 return false;
1927 }
1928 }
1929 }
1930 else
1931 {
1932 const int mantissaBits = getMinMantissaBits(precision);
1933 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1934
1935 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1936 {
1937 const float in0 = ((const float*)inputs[0])[compNdx];
1938 const float out0 = ((const float*)outputs[0])[compNdx];
1939
1940 if (int(deFloatFloor(in0-eps)) == int(deFloatFloor(in0+eps)))
1941 {
1942 const float ref = deFloatFrac(in0);
1943 const int bitsLost = numBitsLostInOp(in0, ref);
1944 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(0, mantissaBits-bitsLost)); // ULP diff for rounded integer value.
1945 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1946
1947 if (ulpDiff > maxUlpDiff)
1948 {
1949 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << " with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1950 return false;
1951 }
1952 }
1953 else
1954 {
1955 if (out0 >= 1.0f)
1956 {
1957 m_failMsg << "Expected [" << compNdx << "] < 1.0";
1958 return false;
1959 }
1960 }
1961 }
1962 }
1963
1964 return true;
1965 }
1966 };
1967
1968 class FractCase : public CommonFunctionCase
1969 {
1970 public:
FractCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1971 FractCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1972 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fract", shaderType)
1973 {
1974 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1975 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1976 m_spec.source = "out0 = fract(in0);";
1977 }
1978
createInstance(Context & ctx) const1979 TestInstance* createInstance (Context& ctx) const
1980 {
1981 return new FractCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1982 }
1983 };
1984
1985 class FrexpCaseInstance : public CommonFunctionTestInstance
1986 {
1987 public:
FrexpCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)1988 FrexpCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1989 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
1990 {
1991 }
1992
getInputValues(int numValues,void * const * values) const1993 void getInputValues (int numValues, void* const* values) const
1994 {
1995 const Vec2 ranges[] =
1996 {
1997 Vec2(-2.0f, 2.0f), // lowp
1998 Vec2(-1e3f, 1e3f), // mediump
1999 Vec2(-1e7f, 1e7f) // highp
2000 };
2001
2002 de::Random rnd (deStringHash(m_name) ^ 0x2790au);
2003 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2004 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2005 const int scalarSize = glu::getDataTypeScalarSize(type);
2006
2007 // Special cases
2008 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2009 {
2010 ((float*)values[0])[scalarSize*0 + compNdx] = 0.0f;
2011 ((float*)values[0])[scalarSize*1 + compNdx] = -0.0f;
2012 ((float*)values[0])[scalarSize*2 + compNdx] = 0.5f;
2013 ((float*)values[0])[scalarSize*3 + compNdx] = -0.5f;
2014 ((float*)values[0])[scalarSize*4 + compNdx] = 1.0f;
2015 ((float*)values[0])[scalarSize*5 + compNdx] = -1.0f;
2016 ((float*)values[0])[scalarSize*6 + compNdx] = 2.0f;
2017 ((float*)values[0])[scalarSize*7 + compNdx] = -2.0f;
2018 }
2019
2020 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + 8*scalarSize, (numValues-8)*scalarSize);
2021
2022 // Make sure the values are representable in the target format
2023 for (int caseNdx = 0; caseNdx < numValues; ++caseNdx)
2024 {
2025 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
2026 {
2027 float* const valuePtr = &((float*)values[0])[caseNdx * scalarSize + scalarNdx];
2028
2029 *valuePtr = makeFloatRepresentable(*valuePtr, precision);
2030 }
2031 }
2032 }
2033
compare(const void * const * inputs,const void * const * outputs)2034 bool compare (const void* const* inputs, const void* const* outputs)
2035 {
2036 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2037 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2038 const int scalarSize = glu::getDataTypeScalarSize(type);
2039 const bool transitSupportsSignedZero = (m_shaderType != glu::SHADERTYPE_FRAGMENT); // executor cannot reliably transit negative zero to fragment stage
2040 const bool signedZero = supportsSignedZero(precision) && transitSupportsSignedZero;
2041
2042 const int mantissaBits = getMinMantissaBits(precision);
2043 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
2044
2045 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2046 {
2047 const float in0 = ((const float*)inputs[0])[compNdx];
2048 const float out0 = ((const float*)outputs[0])[compNdx];
2049 const int out1 = ((const int*)outputs[1])[compNdx];
2050
2051 float refOut0;
2052 int refOut1;
2053
2054 frexp(in0, &refOut0, &refOut1);
2055
2056 const deUint32 ulpDiff0 = signedZero ? getUlpDiff(out0, refOut0) : getUlpDiffIgnoreZeroSign(out0, refOut0);
2057
2058 if (ulpDiff0 > maxUlpDiff || out1 != refOut1)
2059 {
2060 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", " << refOut1 << " with ULP threshold "
2061 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff0);
2062 return false;
2063 }
2064 }
2065
2066 return true;
2067 }
2068 };
2069
2070 class FrexpCase : public CommonFunctionCase
2071 {
2072 public:
FrexpCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)2073 FrexpCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
2074 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "frexp", shaderType)
2075 {
2076 const int vecSize = glu::getDataTypeScalarSize(baseType);
2077 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
2078
2079 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
2080 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
2081 m_spec.outputs.push_back(Symbol("out1", glu::VarType(intType, glu::PRECISION_HIGHP)));
2082 m_spec.source = "out0 = frexp(in0, out1);";
2083 }
2084
createInstance(Context & ctx) const2085 TestInstance* createInstance (Context& ctx) const
2086 {
2087 return new FrexpCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
2088 }
2089 };
2090
2091 class LdexpCaseInstance : public CommonFunctionTestInstance
2092 {
2093 public:
LdexpCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)2094 LdexpCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
2095 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
2096 {
2097 }
2098
getInputValues(int numValues,void * const * values) const2099 void getInputValues (int numValues, void* const* values) const
2100 {
2101 const Vec2 ranges[] =
2102 {
2103 Vec2(-2.0f, 2.0f), // lowp
2104 Vec2(-1e3f, 1e3f), // mediump
2105 Vec2(-1e7f, 1e7f) // highp
2106 };
2107
2108 de::Random rnd (deStringHash(m_name) ^ 0x2790au);
2109 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2110 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2111 const int scalarSize = glu::getDataTypeScalarSize(type);
2112 int valueNdx = 0;
2113
2114 {
2115 const float easySpecialCases[] = { 0.0f, -0.0f, 0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f };
2116
2117 DE_ASSERT(valueNdx + DE_LENGTH_OF_ARRAY(easySpecialCases) <= numValues);
2118 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(easySpecialCases); caseNdx++)
2119 {
2120 float in0;
2121 int in1;
2122
2123 frexp(easySpecialCases[caseNdx], &in0, &in1);
2124
2125 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2126 {
2127 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
2128 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
2129 }
2130
2131 valueNdx += 1;
2132 }
2133 }
2134
2135 {
2136 // \note lowp and mediump can not necessarily fit the values in hard cases, so we'll use only easy ones.
2137 const int numEasyRandomCases = precision == glu::PRECISION_HIGHP ? 50 : (numValues-valueNdx);
2138
2139 DE_ASSERT(valueNdx + numEasyRandomCases <= numValues);
2140 for (int caseNdx = 0; caseNdx < numEasyRandomCases; caseNdx++)
2141 {
2142 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2143 {
2144 const float in = rnd.getFloat(ranges[precision].x(), ranges[precision].y());
2145 float in0;
2146 int in1;
2147
2148 frexp(in, &in0, &in1);
2149
2150 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
2151 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
2152 }
2153
2154 valueNdx += 1;
2155 }
2156 }
2157
2158 {
2159 const int numHardRandomCases = numValues-valueNdx;
2160 DE_ASSERT(numHardRandomCases >= 0 && valueNdx + numHardRandomCases <= numValues);
2161
2162 for (int caseNdx = 0; caseNdx < numHardRandomCases; caseNdx++)
2163 {
2164 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2165 {
2166 const int fpExp = rnd.getInt(-126, 127);
2167 const int sign = rnd.getBool() ? -1 : +1;
2168 const deUint32 mantissa = (1u<<23) | (rnd.getUint32() & ((1u<<23)-1));
2169 const int in1 = rnd.getInt(de::max(-126, -126-fpExp), de::min(127, 127-fpExp));
2170 const float in0 = tcu::Float32::construct(sign, fpExp, mantissa).asFloat();
2171
2172 DE_ASSERT(de::inRange(in1, -126, 127)); // See Khronos bug 11180
2173 DE_ASSERT(de::inRange(in1+fpExp, -126, 127));
2174
2175 const float out = ldexp(in0, in1);
2176
2177 DE_ASSERT(!tcu::Float32(out).isInf() && !tcu::Float32(out).isDenorm());
2178 DE_UNREF(out);
2179
2180 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
2181 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
2182 }
2183
2184 valueNdx += 1;
2185 }
2186 }
2187 }
2188
compare(const void * const * inputs,const void * const * outputs)2189 bool compare (const void* const* inputs, const void* const* outputs)
2190 {
2191 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2192 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2193 const int scalarSize = glu::getDataTypeScalarSize(type);
2194
2195 const int mantissaBits = getMinMantissaBits(precision);
2196 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
2197
2198 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2199 {
2200 const float in0 = ((const float*)inputs[0])[compNdx];
2201 const int in1 = ((const int*)inputs[1])[compNdx];
2202 const float out0 = ((const float*)outputs[0])[compNdx];
2203 const float refOut0 = ldexp(in0, in1);
2204 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, refOut0);
2205
2206 const int inExp = tcu::Float32(in0).exponent();
2207
2208 if (ulpDiff > maxUlpDiff)
2209 {
2210 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", (exp = " << inExp << ") with ULP threshold "
2211 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
2212 return false;
2213 }
2214 }
2215
2216 return true;
2217 }
2218 };
2219
2220 class LdexpCase : public CommonFunctionCase
2221 {
2222 public:
LdexpCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)2223 LdexpCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
2224 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ldexp", shaderType)
2225 {
2226 const int vecSize = glu::getDataTypeScalarSize(baseType);
2227 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
2228
2229 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
2230 m_spec.inputs.push_back(Symbol("in1", glu::VarType(intType, glu::PRECISION_HIGHP)));
2231 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
2232 m_spec.source = "out0 = ldexp(in0, in1);";
2233 }
2234
createInstance(Context & ctx) const2235 TestInstance* createInstance (Context& ctx) const
2236 {
2237 return new LdexpCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
2238 }
2239 };
2240
2241 class FmaCaseInstance : public CommonFunctionTestInstance
2242 {
2243 public:
FmaCaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,int numValues,const char * name)2244 FmaCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
2245 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
2246 {
2247 }
2248
getInputValues(int numValues,void * const * values) const2249 void getInputValues (int numValues, void* const* values) const
2250 {
2251 const Vec2 ranges[] =
2252 {
2253 Vec2(-2.0f, 2.0f), // lowp
2254 Vec2(-127.f, 127.f), // mediump
2255 Vec2(-1e7f, 1e7f) // highp
2256 };
2257
2258 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
2259 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2260 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2261 const int scalarSize = glu::getDataTypeScalarSize(type);
2262 const float specialCases[][3] =
2263 {
2264 // a b c
2265 { 0.0f, 0.0f, 0.0f },
2266 { 0.0f, 1.0f, 0.0f },
2267 { 0.0f, 0.0f, -1.0f },
2268 { 1.0f, 1.0f, 0.0f },
2269 { 1.0f, 1.0f, 1.0f },
2270 { -1.0f, 1.0f, 0.0f },
2271 { 1.0f, -1.0f, 0.0f },
2272 { -1.0f, -1.0f, 0.0f },
2273 { -0.0f, 1.0f, 0.0f },
2274 { 1.0f, -0.0f, 0.0f }
2275 };
2276 const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
2277
2278 // Special cases
2279 for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
2280 {
2281 for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2282 {
2283 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
2284 ((float*)values[inputNdx])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx][inputNdx];
2285 }
2286 }
2287
2288 // Random cases.
2289 {
2290 const int numScalars = (numValues-numSpecialCases)*scalarSize;
2291 const int offs = scalarSize*numSpecialCases;
2292
2293 for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2294 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[inputNdx] + offs, numScalars);
2295 }
2296
2297 // Make sure the values are representable in the target format
2298 for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2299 {
2300 for (int caseNdx = 0; caseNdx < numValues; ++caseNdx)
2301 {
2302 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
2303 {
2304 float* const valuePtr = &((float*)values[inputNdx])[caseNdx * scalarSize + scalarNdx];
2305
2306 *valuePtr = makeFloatRepresentable(*valuePtr, precision);
2307 }
2308 }
2309 }
2310 }
2311
fma(glu::Precision precision,float a,float b,float c)2312 static tcu::Interval fma (glu::Precision precision, float a, float b, float c)
2313 {
2314 const tcu::FloatFormat formats[] =
2315 {
2316 // minExp maxExp mantissa exact, subnormals infinities NaN
2317 tcu::FloatFormat(0, 0, 7, false, tcu::YES, tcu::MAYBE, tcu::MAYBE),
2318 tcu::FloatFormat(-13, 13, 9, false, tcu::MAYBE, tcu::MAYBE, tcu::MAYBE),
2319 tcu::FloatFormat(-126, 127, 23, true, tcu::MAYBE, tcu::YES, tcu::MAYBE)
2320 };
2321 const tcu::FloatFormat& format = de::getSizedArrayElement<glu::PRECISION_LAST>(formats, precision);
2322 const tcu::Interval ia = format.convert(a);
2323 const tcu::Interval ib = format.convert(b);
2324 const tcu::Interval ic = format.convert(c);
2325 tcu::Interval prod0;
2326 tcu::Interval prod1;
2327 tcu::Interval prod2;
2328 tcu::Interval prod3;
2329 tcu::Interval prod;
2330 tcu::Interval res;
2331
2332 TCU_SET_INTERVAL(prod0, tmp, tmp = ia.lo() * ib.lo());
2333 TCU_SET_INTERVAL(prod1, tmp, tmp = ia.lo() * ib.hi());
2334 TCU_SET_INTERVAL(prod2, tmp, tmp = ia.hi() * ib.lo());
2335 TCU_SET_INTERVAL(prod3, tmp, tmp = ia.hi() * ib.hi());
2336
2337 prod = format.convert(format.roundOut(prod0 | prod1 | prod2 | prod3, ia.isFinite() && ib.isFinite()));
2338
2339 TCU_SET_INTERVAL_BOUNDS(res, tmp,
2340 tmp = prod.lo() + ic.lo(),
2341 tmp = prod.hi() + ic.hi());
2342
2343 return format.convert(format.roundOut(res, prod.isFinite() && ic.isFinite()));
2344 }
2345
compare(const void * const * inputs,const void * const * outputs)2346 bool compare (const void* const* inputs, const void* const* outputs)
2347 {
2348 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2349 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2350 const int scalarSize = glu::getDataTypeScalarSize(type);
2351
2352 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2353 {
2354 const float a = ((const float*)inputs[0])[compNdx];
2355 const float b = ((const float*)inputs[1])[compNdx];
2356 const float c = ((const float*)inputs[2])[compNdx];
2357 const float res = ((const float*)outputs[0])[compNdx];
2358 const tcu::Interval ref = fma(precision, a, b, c);
2359
2360 if (!ref.contains(res))
2361 {
2362 m_failMsg << "Expected [" << compNdx << "] = " << ref;
2363 return false;
2364 }
2365 }
2366
2367 return true;
2368 }
2369 };
2370
2371 class FmaCase : public CommonFunctionCase
2372 {
2373 public:
FmaCase(tcu::TestContext & testCtx,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)2374 FmaCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
2375 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fma", shaderType)
2376 {
2377 m_spec.inputs.push_back(Symbol("a", glu::VarType(baseType, precision)));
2378 m_spec.inputs.push_back(Symbol("b", glu::VarType(baseType, precision)));
2379 m_spec.inputs.push_back(Symbol("c", glu::VarType(baseType, precision)));
2380 m_spec.outputs.push_back(Symbol("res", glu::VarType(baseType, precision)));
2381 m_spec.source = "res = fma(a, b, c);";
2382 m_spec.globalDeclarations = "#extension GL_EXT_gpu_shader5 : require\n";
2383 }
2384
createInstance(Context & ctx) const2385 TestInstance* createInstance (Context& ctx) const
2386 {
2387 return new FmaCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
2388 }
2389 };
2390
2391 } // anonymous
2392
ShaderCommonFunctionTests(tcu::TestContext & testCtx)2393 ShaderCommonFunctionTests::ShaderCommonFunctionTests (tcu::TestContext& testCtx)
2394 : tcu::TestCaseGroup (testCtx, "common", "Common function tests")
2395 {
2396 }
2397
~ShaderCommonFunctionTests(void)2398 ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void)
2399 {
2400 }
2401
init(void)2402 void ShaderCommonFunctionTests::init (void)
2403 {
2404 enum
2405 {
2406 VS = (1<<glu::SHADERTYPE_VERTEX),
2407 TC = (1<<glu::SHADERTYPE_TESSELLATION_CONTROL),
2408 TE = (1<<glu::SHADERTYPE_TESSELLATION_EVALUATION),
2409 GS = (1<<glu::SHADERTYPE_GEOMETRY),
2410 FS = (1<<glu::SHADERTYPE_FRAGMENT),
2411 CS = (1<<glu::SHADERTYPE_COMPUTE),
2412
2413 ALL_SHADERS = VS|TC|TE|GS|FS|CS,
2414 NEW_SHADERS = TC|TE|GS|CS,
2415 };
2416
2417 // Float? Int? Uint? Shaders
2418 addFunctionCases<AbsCase> (this, "abs", true, true, false, ALL_SHADERS);
2419 addFunctionCases<SignCase> (this, "sign", true, true, false, ALL_SHADERS);
2420 addFunctionCases<FloorCase> (this, "floor", true, false, false, ALL_SHADERS);
2421 addFunctionCases<TruncCase> (this, "trunc", true, false, false, ALL_SHADERS);
2422 addFunctionCases<RoundCase> (this, "round", true, false, false, ALL_SHADERS);
2423 addFunctionCases<RoundEvenCase> (this, "roundeven", true, false, false, ALL_SHADERS);
2424 addFunctionCases<CeilCase> (this, "ceil", true, false, false, ALL_SHADERS);
2425 addFunctionCases<FractCase> (this, "fract", true, false, false, ALL_SHADERS);
2426 // mod
2427 addFunctionCases<ModfCase> (this, "modf", true, false, false, ALL_SHADERS);
2428 // min
2429 // max
2430 // clamp
2431 // mix
2432 // step
2433 // smoothstep
2434 addFunctionCases<IsnanCase> (this, "isnan", true, false, false, ALL_SHADERS);
2435 addFunctionCases<IsinfCase> (this, "isinf", true, false, false, ALL_SHADERS);
2436 addFunctionCases<FloatBitsToIntCase> (this, "floatbitstoint", true, false, false, ALL_SHADERS);
2437 addFunctionCases<FloatBitsToUintCase> (this, "floatbitstouint", true, false, false, ALL_SHADERS);
2438
2439 addFunctionCases<FrexpCase> (this, "frexp", true, false, false, ALL_SHADERS);
2440 addFunctionCases<LdexpCase> (this, "ldexp", true, false, false, ALL_SHADERS);
2441 addFunctionCases<FmaCase> (this, "fma", true, false, false, ALL_SHADERS);
2442
2443 // (u)intBitsToFloat()
2444 {
2445 const deUint32 shaderBits = NEW_SHADERS;
2446 tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "intbitstofloat", "intBitsToFloat() Tests");
2447 tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat", "uintBitsToFloat() Tests");
2448
2449 addChild(intGroup);
2450 addChild(uintGroup);
2451
2452 for (int vecSize = 1; vecSize < 4; vecSize++)
2453 {
2454 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
2455 const glu::DataType uintType = vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT;
2456
2457 for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
2458 {
2459 if (shaderBits & (1<<shaderType))
2460 {
2461 intGroup->addChild(new BitsToFloatCase(getTestContext(), intType, glu::ShaderType(shaderType)));
2462 uintGroup->addChild(new BitsToFloatCase(getTestContext(), uintType, glu::ShaderType(shaderType)));
2463 }
2464 }
2465 }
2466 }
2467 }
2468
2469 } // shaderexecutor
2470 } // vkt
2471