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