1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.1 Module
3 * -------------------------------------------------
5 * Copyright 2014 The Android Open Source Project
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief Common built-in function tests.
22 *//*--------------------------------------------------------------------*/
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"
46 using namespace gls::ShaderExecUtil;
57 template<typename T, int Size>
61 VecArrayAccess (const void* ptr) : m_array((tcu::Vector<T, Size>*)ptr) {}
62 ~VecArrayAccess (void) {}
64 const tcu::Vector<T, Size>& operator[] (size_t offset) const { return m_array[offset]; }
65 tcu::Vector<T, Size>& operator[] (size_t offset) { return m_array[offset]; }
68 tcu::Vector<T, Size>* m_array;
71 template<typename T> T randomScalar (de::Random& rnd, T minValue, T maxValue);
72 template<> inline float randomScalar (de::Random& rnd, float minValue, float maxValue) { return rnd.getFloat(minValue, maxValue); }
73 template<> inline deInt32 randomScalar (de::Random& rnd, deInt32 minValue, deInt32 maxValue) { return rnd.getInt(minValue, maxValue); }
74 template<> inline deUint32 randomScalar (de::Random& rnd, deUint32 minValue, deUint32 maxValue) { return minValue + rnd.getUint32() % (maxValue - minValue + 1); }
76 template<typename T, int Size>
77 inline tcu::Vector<T, Size> randomVector (de::Random& rnd, const tcu::Vector<T, Size>& minValue, const tcu::Vector<T, Size>& maxValue)
79 tcu::Vector<T, Size> res;
80 for (int ndx = 0; ndx < Size; ndx++)
81 res[ndx] = randomScalar<T>(rnd, minValue[ndx], maxValue[ndx]);
85 template<typename T, int Size>
86 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 VecArrayAccess<T, Size> access(dst);
89 for (int ndx = 0; ndx < numValues; ndx++)
90 access[offset + ndx] = randomVector<T, Size>(rnd, minValue, maxValue);
94 static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0)
96 T* typedPtr = (T*)dst;
97 for (int ndx = 0; ndx < numValues; ndx++)
98 typedPtr[offset + ndx] = randomScalar<T>(rnd, minValue, maxValue);
101 inline int numBitsLostInOp (float input, float output)
103 const int inExp = tcu::Float32(input).exponent();
104 const int outExp = tcu::Float32(output).exponent();
106 return de::max(0, inExp-outExp); // Lost due to mantissa shift.
109 inline deUint32 getUlpDiff (float a, float b)
111 const deUint32 aBits = tcu::Float32(a).bits();
112 const deUint32 bBits = tcu::Float32(b).bits();
113 return aBits > bBits ? aBits - bBits : bBits - aBits;
116 inline deUint32 getUlpDiffIgnoreZeroSign (float a, float b)
118 if (tcu::Float32(a).isZero())
119 return getUlpDiff(tcu::Float32::construct(tcu::Float32(b).sign(), 0, 0).asFloat(), b);
120 else if (tcu::Float32(b).isZero())
121 return getUlpDiff(a, tcu::Float32::construct(tcu::Float32(a).sign(), 0, 0).asFloat());
123 return getUlpDiff(a, b);
126 inline bool supportsSignedZero (glu::Precision precision)
128 // \note GLSL ES 3.1 doesn't really require support for -0, but we require it for highp
129 // as it is very widely supported.
130 return precision == glu::PRECISION_HIGHP;
133 inline float getEpsFromMaxUlpDiff (float value, deUint32 ulpDiff)
135 const int exp = tcu::Float32(value).exponent();
136 return tcu::Float32::construct(+1, exp, (1u<<23) | ulpDiff).asFloat() - tcu::Float32::construct(+1, exp, 1u<<23).asFloat();
139 inline deUint32 getMaxUlpDiffFromBits (int numAccurateBits)
141 const int numGarbageBits = 23-numAccurateBits;
142 const deUint32 mask = (1u<<numGarbageBits)-1u;
147 inline float getEpsFromBits (float value, int numAccurateBits)
149 return getEpsFromMaxUlpDiff(value, getMaxUlpDiffFromBits(numAccurateBits));
152 static int getMinMantissaBits (glu::Precision precision)
160 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(bits) == glu::PRECISION_LAST);
161 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(bits)));
162 return bits[precision];
165 static int getMaxNormalizedValueExponent (glu::Precision precision)
167 const int exponent[] =
173 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(exponent) == glu::PRECISION_LAST);
174 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(exponent)));
175 return exponent[precision];
178 static int getMinNormalizedValueExponent (glu::Precision precision)
180 const int exponent[] =
186 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(exponent) == glu::PRECISION_LAST);
187 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(exponent)));
188 return exponent[precision];
191 // CommonFunctionCase
193 class CommonFunctionCase : public TestCase
196 CommonFunctionCase (Context& context, const char* name, const char* description, glu::ShaderType shaderType);
197 ~CommonFunctionCase (void);
201 IterateResult iterate (void);
204 CommonFunctionCase (const CommonFunctionCase& other);
205 CommonFunctionCase& operator= (const CommonFunctionCase& other);
207 virtual void getInputValues (int numValues, void* const* values) const = 0;
208 virtual bool compare (const void* const* inputs, const void* const* outputs) = 0;
210 glu::ShaderType m_shaderType;
214 std::ostringstream m_failMsg; //!< Comparison failure help message.
217 ShaderExecutor* m_executor;
220 CommonFunctionCase::CommonFunctionCase (Context& context, const char* name, const char* description, glu::ShaderType shaderType)
221 : TestCase (context, name, description)
222 , m_shaderType (shaderType)
224 , m_executor (DE_NULL)
226 m_spec.version = glu::GLSL_VERSION_310_ES;
229 CommonFunctionCase::~CommonFunctionCase (void)
231 CommonFunctionCase::deinit();
234 void CommonFunctionCase::init (void)
236 DE_ASSERT(!m_executor);
238 m_executor = createExecutor(m_context.getRenderContext(), m_shaderType, m_spec);
239 m_testCtx.getLog() << m_executor;
241 if (!m_executor->isOk())
242 throw tcu::TestError("Compile failed");
245 void CommonFunctionCase::deinit (void)
248 m_executor = DE_NULL;
251 static vector<int> getScalarSizes (const vector<Symbol>& symbols)
253 vector<int> sizes(symbols.size());
254 for (int ndx = 0; ndx < (int)symbols.size(); ++ndx)
255 sizes[ndx] = symbols[ndx].varType.getScalarSize();
259 static int computeTotalScalarSize (const vector<Symbol>& symbols)
262 for (vector<Symbol>::const_iterator sym = symbols.begin(); sym != symbols.end(); ++sym)
263 totalSize += sym->varType.getScalarSize();
267 static vector<void*> getInputOutputPointers (const vector<Symbol>& symbols, vector<deUint32>& data, const int numValues)
269 vector<void*> pointers (symbols.size());
270 int curScalarOffset = 0;
272 for (int varNdx = 0; varNdx < (int)symbols.size(); ++varNdx)
274 const Symbol& var = symbols[varNdx];
275 const int scalarSize = var.varType.getScalarSize();
277 // Uses planar layout as input/output specs do not support strides.
278 pointers[varNdx] = &data[curScalarOffset];
279 curScalarOffset += scalarSize*numValues;
282 DE_ASSERT(curScalarOffset == (int)data.size());
287 // \todo [2013-08-08 pyry] Make generic utility and move to glu?
292 HexFloat (const float value_) : value(value_) {}
295 std::ostream& operator<< (std::ostream& str, const HexFloat& v)
297 return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
302 const deUint32 value;
303 HexBool (const deUint32 value_) : value(value_) {}
306 std::ostream& operator<< (std::ostream& str, const HexBool& v)
308 return str << (v.value ? "true" : "false") << " / " << tcu::toHex(v.value);
313 const glu::VarType& type;
316 VarValue (const glu::VarType& type_, const void* value_) : type(type_), value(value_) {}
319 std::ostream& operator<< (std::ostream& str, const VarValue& varValue)
321 DE_ASSERT(varValue.type.isBasicType());
323 const glu::DataType basicType = varValue.type.getBasicType();
324 const glu::DataType scalarType = glu::getDataTypeScalarType(basicType);
325 const int numComponents = glu::getDataTypeScalarSize(basicType);
327 if (numComponents > 1)
328 str << glu::getDataTypeName(basicType) << "(";
330 for (int compNdx = 0; compNdx < numComponents; compNdx++)
337 case glu::TYPE_FLOAT: str << HexFloat(((const float*)varValue.value)[compNdx]); break;
338 case glu::TYPE_INT: str << ((const deInt32*)varValue.value)[compNdx]; break;
339 case glu::TYPE_UINT: str << tcu::toHex(((const deUint32*)varValue.value)[compNdx]); break;
340 case glu::TYPE_BOOL: str << HexBool(((const deUint32*)varValue.value)[compNdx]); break;
347 if (numComponents > 1)
353 CommonFunctionCase::IterateResult CommonFunctionCase::iterate (void)
355 const int numInputScalars = computeTotalScalarSize(m_spec.inputs);
356 const int numOutputScalars = computeTotalScalarSize(m_spec.outputs);
357 vector<deUint32> inputData (numInputScalars * m_numValues);
358 vector<deUint32> outputData (numOutputScalars * m_numValues);
359 const vector<void*> inputPointers = getInputOutputPointers(m_spec.inputs, inputData, m_numValues);
360 const vector<void*> outputPointers = getInputOutputPointers(m_spec.outputs, outputData, m_numValues);
362 // Initialize input data.
363 getInputValues(m_numValues, &inputPointers[0]);
366 m_executor->useProgram();
367 m_executor->execute(m_numValues, &inputPointers[0], &outputPointers[0]);
371 const vector<int> inScalarSizes = getScalarSizes(m_spec.inputs);
372 const vector<int> outScalarSizes = getScalarSizes(m_spec.outputs);
373 vector<void*> curInputPtr (inputPointers.size());
374 vector<void*> curOutputPtr (outputPointers.size());
377 for (int valNdx = 0; valNdx < m_numValues; valNdx++)
379 // Set up pointers for comparison.
380 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx)
381 curInputPtr[inNdx] = (deUint32*)inputPointers[inNdx] + inScalarSizes[inNdx]*valNdx;
383 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx)
384 curOutputPtr[outNdx] = (deUint32*)outputPointers[outNdx] + outScalarSizes[outNdx]*valNdx;
386 if (!compare(&curInputPtr[0], &curOutputPtr[0]))
388 // \todo [2013-08-08 pyry] We probably want to log reference value as well?
390 m_testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n " << m_failMsg.str() << TestLog::EndMessage;
392 m_testCtx.getLog() << TestLog::Message << " inputs:" << TestLog::EndMessage;
393 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++)
394 m_testCtx.getLog() << TestLog::Message << " " << m_spec.inputs[inNdx].name << " = "
395 << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx])
396 << TestLog::EndMessage;
398 m_testCtx.getLog() << TestLog::Message << " outputs:" << TestLog::EndMessage;
399 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++)
400 m_testCtx.getLog() << TestLog::Message << " " << m_spec.outputs[outNdx].name << " = "
401 << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx])
402 << TestLog::EndMessage;
410 m_testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage;
412 m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
413 numFailed == 0 ? "Pass" : "Result comparison failed");
419 static const char* getPrecisionPostfix (glu::Precision precision)
421 static const char* s_postfix[] =
427 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_postfix) == glu::PRECISION_LAST);
428 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
429 return s_postfix[precision];
432 static const char* getShaderTypePostfix (glu::ShaderType shaderType)
434 static const char* s_postfix[] =
443 DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
444 return s_postfix[shaderType];
447 static std::string getCommonFuncCaseName (glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
449 return string(glu::getDataTypeName(baseType)) + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType);
452 class AbsCase : public CommonFunctionCase
455 AbsCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
456 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "abs", shaderType)
458 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
459 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
460 m_spec.source = "out0 = abs(in0);";
463 void getInputValues (int numValues, void* const* values) const
465 const Vec2 floatRanges[] =
467 Vec2(-2.0f, 2.0f), // lowp
468 Vec2(-1e3f, 1e3f), // mediump
469 Vec2(-1e7f, 1e7f) // highp
471 const IVec2 intRanges[] =
473 IVec2(-(1<<7)+1, (1<<7)-1),
474 IVec2(-(1<<15)+1, (1<<15)-1),
475 IVec2(0x80000001, 0x7fffffff)
478 de::Random rnd (deStringHash(getName()) ^ 0x235facu);
479 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
480 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
481 const int scalarSize = glu::getDataTypeScalarSize(type);
483 if (glu::isDataTypeFloatOrVec(type))
484 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), values[0], numValues*scalarSize);
486 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize);
489 bool compare (const void* const* inputs, const void* const* outputs)
491 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
492 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
493 const int scalarSize = glu::getDataTypeScalarSize(type);
495 if (glu::isDataTypeFloatOrVec(type))
497 const int mantissaBits = getMinMantissaBits(precision);
498 const deUint32 maxUlpDiff = (1u<<(23-mantissaBits))-1u;
500 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
502 const float in0 = ((const float*)inputs[0])[compNdx];
503 const float out0 = ((const float*)outputs[0])[compNdx];
504 const float ref0 = de::abs(in0);
505 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
507 if (ulpDiff0 > maxUlpDiff)
509 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
516 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
518 const int in0 = ((const int*)inputs[0])[compNdx];
519 const int out0 = ((const int*)outputs[0])[compNdx];
520 const int ref0 = de::abs(in0);
524 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
534 class SignCase : public CommonFunctionCase
537 SignCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
538 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "sign", shaderType)
540 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
541 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
542 m_spec.source = "out0 = sign(in0);";
545 void getInputValues (int numValues, void* const* values) const
547 const Vec2 floatRanges[] =
549 Vec2(-2.0f, 2.0f), // lowp
550 Vec2(-1e4f, 1e4f), // mediump - note: may end up as inf
551 Vec2(-1e8f, 1e8f) // highp - note: may end up as inf
553 const IVec2 intRanges[] =
555 IVec2(-(1<<7), (1<<7)-1),
556 IVec2(-(1<<15), (1<<15)-1),
557 IVec2(0x80000000, 0x7fffffff)
560 de::Random rnd (deStringHash(getName()) ^ 0x324u);
561 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
562 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
563 const int scalarSize = glu::getDataTypeScalarSize(type);
565 if (glu::isDataTypeFloatOrVec(type))
568 std::fill((float*)values[0], (float*)values[0] + scalarSize, +1.0f);
569 std::fill((float*)values[0], (float*)values[0] + scalarSize, -1.0f);
570 std::fill((float*)values[0], (float*)values[0] + scalarSize, 0.0f);
571 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), (float*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
575 std::fill((int*)values[0], (int*)values[0] + scalarSize, +1);
576 std::fill((int*)values[0], (int*)values[0] + scalarSize, -1);
577 std::fill((int*)values[0], (int*)values[0] + scalarSize, 0);
578 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
582 bool compare (const void* const* inputs, const void* const* outputs)
584 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
585 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
586 const int scalarSize = glu::getDataTypeScalarSize(type);
588 if (glu::isDataTypeFloatOrVec(type))
590 // Both highp and mediump should be able to represent -1, 0, and +1 exactly
591 const deUint32 maxUlpDiff = precision == glu::PRECISION_LOWP ? getMaxUlpDiffFromBits(getMinMantissaBits(precision)) : 0;
593 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
595 const float in0 = ((const float*)inputs[0])[compNdx];
596 const float out0 = ((const float*)outputs[0])[compNdx];
597 const float ref0 = in0 < 0.0f ? -1.0f :
598 in0 > 0.0f ? +1.0f : 0.0f;
599 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
601 if (ulpDiff0 > maxUlpDiff)
603 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
610 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
612 const int in0 = ((const int*)inputs[0])[compNdx];
613 const int out0 = ((const int*)outputs[0])[compNdx];
614 const int ref0 = in0 < 0 ? -1 :
619 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
629 static float roundEven (float v)
631 const float q = deFloatFrac(v);
632 const int truncated = int(v-q);
633 const int rounded = (q > 0.5f) ? (truncated + 1) : // Rounded up
634 (q == 0.5f && (truncated % 2 != 0)) ? (truncated + 1) : // Round to nearest even at 0.5
635 truncated; // Rounded down
637 return float(rounded);
640 class RoundEvenCase : public CommonFunctionCase
643 RoundEvenCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
644 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "roundEven", shaderType)
646 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
647 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
648 m_spec.source = "out0 = roundEven(in0);";
651 void getInputValues (int numValues, void* const* values) const
653 const Vec2 ranges[] =
655 Vec2(-2.0f, 2.0f), // lowp
656 Vec2(-1e3f, 1e3f), // mediump
657 Vec2(-1e7f, 1e7f) // highp
660 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
661 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
662 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
663 const int scalarSize = glu::getDataTypeScalarSize(type);
664 int numSpecialCases = 0;
667 if (precision != glu::PRECISION_LOWP)
669 DE_ASSERT(numValues >= 20);
670 for (int ndx = 0; ndx < 20; ndx++)
672 const float v = de::clamp(float(ndx) - 10.5f, ranges[precision].x(), ranges[precision].y());
673 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
674 numSpecialCases += 1;
679 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
681 // If precision is mediump, make sure values can be represented in fp16 exactly
682 if (precision == glu::PRECISION_MEDIUMP)
684 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
685 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
689 bool compare (const void* const* inputs, const void* const* outputs)
691 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
692 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
693 const bool hasSignedZero = supportsSignedZero(precision);
694 const int scalarSize = glu::getDataTypeScalarSize(type);
696 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
698 // Require exact rounding result.
699 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
701 const float in0 = ((const float*)inputs[0])[compNdx];
702 const float out0 = ((const float*)outputs[0])[compNdx];
703 const float ref = roundEven(in0);
705 const deUint32 ulpDiff = hasSignedZero ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
709 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
716 const int mantissaBits = getMinMantissaBits(precision);
717 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
718 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
720 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
722 const float in0 = ((const float*)inputs[0])[compNdx];
723 const float out0 = ((const float*)outputs[0])[compNdx];
724 const int minRes = int(roundEven(in0-eps));
725 const int maxRes = int(roundEven(in0+eps));
728 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
730 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
732 if (ulpDiff <= maxUlpDiff)
741 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
751 class ModfCase : public CommonFunctionCase
754 ModfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
755 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "modf", shaderType)
757 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
758 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
759 m_spec.outputs.push_back(Symbol("out1", glu::VarType(baseType, precision)));
760 m_spec.source = "out0 = modf(in0, out1);";
763 void getInputValues (int numValues, void* const* values) const
765 const Vec2 ranges[] =
767 Vec2(-2.0f, 2.0f), // lowp
768 Vec2(-1e3f, 1e3f), // mediump
769 Vec2(-1e7f, 1e7f) // highp
772 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
773 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
774 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
775 const int scalarSize = glu::getDataTypeScalarSize(type);
777 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
780 bool compare (const void* const* inputs, const void* const* outputs)
782 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
783 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
784 const bool hasZeroSign = supportsSignedZero(precision);
785 const int scalarSize = glu::getDataTypeScalarSize(type);
787 const int mantissaBits = getMinMantissaBits(precision);
789 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
791 const float in0 = ((const float*)inputs[0])[compNdx];
792 const float out0 = ((const float*)outputs[0])[compNdx];
793 const float out1 = ((const float*)outputs[1])[compNdx];
795 const float refOut1 = float(int(in0));
796 const float refOut0 = in0 - refOut1;
798 const int bitsLost = precision != glu::PRECISION_HIGHP ? numBitsLostInOp(in0, refOut0) : 0;
799 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(mantissaBits - bitsLost, 0));
801 const float resSum = out0 + out1;
803 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(resSum, in0) : getUlpDiffIgnoreZeroSign(resSum, in0);
805 if (ulpDiff > maxUlpDiff)
807 m_failMsg << "Expected [" << compNdx << "] = (" << HexFloat(refOut0) << ") + (" << HexFloat(refOut1) << ") = " << HexFloat(in0) << " with ULP threshold "
808 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
817 class IsnanCase : public CommonFunctionCase
820 IsnanCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
821 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isnan", shaderType)
823 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
825 const int vecSize = glu::getDataTypeScalarSize(baseType);
826 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
828 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
829 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
830 m_spec.source = "out0 = isnan(in0);";
833 void getInputValues (int numValues, void* const* values) const
835 de::Random rnd (deStringHash(getName()) ^ 0xc2a39fu);
836 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
837 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
838 const int scalarSize = glu::getDataTypeScalarSize(type);
839 const int mantissaBits = getMinMantissaBits(precision);
840 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
842 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
844 const bool isNan = rnd.getFloat() > 0.3f;
845 const bool isInf = !isNan && rnd.getFloat() > 0.4f;
846 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
847 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
848 const deUint32 sign = rnd.getUint32() & 0x1u;
849 const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
851 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
853 ((deUint32*)values[0])[valNdx] = value;
857 bool compare (const void* const* inputs, const void* const* outputs)
859 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
860 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
861 const int scalarSize = glu::getDataTypeScalarSize(type);
863 if (precision == glu::PRECISION_HIGHP)
865 // Only highp is required to support inf/nan
866 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
868 const float in0 = ((const float*)inputs[0])[compNdx];
869 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
870 const bool ref = tcu::Float32(in0).isNaN();
874 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
879 else if (precision == glu::PRECISION_MEDIUMP || precision == glu::PRECISION_LOWP)
881 // NaN support is optional, check that inputs that are not NaN don't result in true.
882 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
884 const float in0 = ((const float*)inputs[0])[compNdx];
885 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
886 const bool ref = tcu::Float32(in0).isNaN();
890 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
900 class IsinfCase : public CommonFunctionCase
903 IsinfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
904 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isinf", shaderType)
906 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
908 const int vecSize = glu::getDataTypeScalarSize(baseType);
909 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
911 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
912 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
913 m_spec.source = "out0 = isinf(in0);";
916 void getInputValues (int numValues, void* const* values) const
918 de::Random rnd (deStringHash(getName()) ^ 0xc2a39fu);
919 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
920 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
921 const int scalarSize = glu::getDataTypeScalarSize(type);
922 const int mantissaBits = getMinMantissaBits(precision);
923 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
925 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
927 const bool isInf = rnd.getFloat() > 0.3f;
928 const bool isNan = !isInf && rnd.getFloat() > 0.4f;
929 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
930 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
931 const deUint32 sign = rnd.getUint32() & 0x1u;
932 const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
934 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
936 ((deUint32*)values[0])[valNdx] = value;
940 bool compare (const void* const* inputs, const void* const* outputs)
942 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
943 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
944 const int scalarSize = glu::getDataTypeScalarSize(type);
946 if (precision == glu::PRECISION_HIGHP)
948 // Only highp is required to support inf/nan
949 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
951 const float in0 = ((const float*)inputs[0])[compNdx];
952 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
953 const bool ref = tcu::Float32(in0).isInf();
957 m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
962 else if (precision == glu::PRECISION_MEDIUMP)
964 // Inf support is optional, check that inputs that are not Inf in mediump don't result in true.
965 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
967 const float in0 = ((const float*)inputs[0])[compNdx];
968 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
969 const bool ref = tcu::Float16(in0).isInf();
973 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
978 // else: no verification can be performed
984 class FloatBitsToUintIntCase : public CommonFunctionCase
987 FloatBitsToUintIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType, bool outIsSigned)
988 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), outIsSigned ? "floatBitsToInt" : "floatBitsToUint", shaderType)
990 const int vecSize = glu::getDataTypeScalarSize(baseType);
991 const glu::DataType intType = outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT)
992 : (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT);
994 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
995 m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP)));
996 m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);";
999 void getInputValues (int numValues, void* const* values) const
1001 const Vec2 ranges[] =
1003 Vec2(-2.0f, 2.0f), // lowp
1004 Vec2(-1e3f, 1e3f), // mediump
1005 Vec2(-1e7f, 1e7f) // highp
1008 de::Random rnd (deStringHash(getName()) ^ 0x2790au);
1009 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1010 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1011 const int scalarSize = glu::getDataTypeScalarSize(type);
1013 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
1016 bool compare (const void* const* inputs, const void* const* outputs)
1018 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1019 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1020 const int scalarSize = glu::getDataTypeScalarSize(type);
1022 const int mantissaBits = getMinMantissaBits(precision);
1023 const int maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
1025 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1027 const float in0 = ((const float*)inputs[0])[compNdx];
1028 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
1029 const deUint32 refOut0 = tcu::Float32(in0).bits();
1030 const int ulpDiff = de::abs((int)out0 - (int)refOut0);
1032 if (ulpDiff > maxUlpDiff)
1034 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold "
1035 << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1044 class FloatBitsToIntCase : public FloatBitsToUintIntCase
1047 FloatBitsToIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1048 : FloatBitsToUintIntCase(context, baseType, precision, shaderType, true)
1053 class FloatBitsToUintCase : public FloatBitsToUintIntCase
1056 FloatBitsToUintCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1057 : FloatBitsToUintIntCase(context, baseType, precision, shaderType, false)
1062 class BitsToFloatCase : public CommonFunctionCase
1065 BitsToFloatCase (Context& context, glu::DataType baseType, glu::ShaderType shaderType)
1066 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP, shaderType).c_str(), glu::isDataTypeIntOrIVec(baseType) ? "intBitsToFloat" : "uintBitsToFloat", shaderType)
1068 const bool inIsSigned = glu::isDataTypeIntOrIVec(baseType);
1069 const int vecSize = glu::getDataTypeScalarSize(baseType);
1070 const glu::DataType floatType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1072 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1073 m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP)));
1074 m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);";
1077 void getInputValues (int numValues, void* const* values) const
1079 de::Random rnd (deStringHash(getName()) ^ 0xbbb225u);
1080 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1081 const int scalarSize = glu::getDataTypeScalarSize(type);
1082 const Vec2 range (-1e8f, +1e8f);
1084 // \note Filled as floats.
1085 fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize);
1088 bool compare (const void* const* inputs, const void* const* outputs)
1090 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1091 const int scalarSize = glu::getDataTypeScalarSize(type);
1092 const int maxUlpDiff = 0;
1094 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1096 const float in0 = ((const float*)inputs[0])[compNdx];
1097 const float out0 = ((const float*)outputs[0])[compNdx];
1098 const int ulpDiff = de::abs((int)in0 - (int)out0);
1100 if (ulpDiff > maxUlpDiff)
1102 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(in0) << " with ULP threshold "
1103 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
1112 class FloorCase : public CommonFunctionCase
1115 FloorCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1116 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "floor", shaderType)
1118 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1119 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1120 m_spec.source = "out0 = floor(in0);";
1123 void getInputValues (int numValues, void* const* values) const
1125 const Vec2 ranges[] =
1127 Vec2(-2.0f, 2.0f), // lowp
1128 Vec2(-1e3f, 1e3f), // mediump
1129 Vec2(-1e7f, 1e7f) // highp
1132 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1133 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1134 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1135 const int scalarSize = glu::getDataTypeScalarSize(type);
1137 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1139 // If precision is mediump, make sure values can be represented in fp16 exactly
1140 if (precision == glu::PRECISION_MEDIUMP)
1142 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1143 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1147 bool compare (const void* const* inputs, const void* const* outputs)
1149 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1150 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1151 const int scalarSize = glu::getDataTypeScalarSize(type);
1153 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1155 // Require exact result.
1156 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1158 const float in0 = ((const float*)inputs[0])[compNdx];
1159 const float out0 = ((const float*)outputs[0])[compNdx];
1160 const float ref = deFloatFloor(in0);
1162 const deUint32 ulpDiff = getUlpDiff(out0, ref);
1166 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1173 const int mantissaBits = getMinMantissaBits(precision);
1174 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1175 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1177 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1179 const float in0 = ((const float*)inputs[0])[compNdx];
1180 const float out0 = ((const float*)outputs[0])[compNdx];
1181 const int minRes = int(deFloatFloor(in0-eps));
1182 const int maxRes = int(deFloatFloor(in0+eps));
1185 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1187 const deUint32 ulpDiff = getUlpDiff(out0, float(roundedVal));
1189 if (ulpDiff <= maxUlpDiff)
1198 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1208 class TruncCase : public CommonFunctionCase
1211 TruncCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1212 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "trunc", shaderType)
1214 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1215 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1216 m_spec.source = "out0 = trunc(in0);";
1219 void getInputValues (int numValues, void* const* values) const
1221 const Vec2 ranges[] =
1223 Vec2(-2.0f, 2.0f), // lowp
1224 Vec2(-1e3f, 1e3f), // mediump
1225 Vec2(-1e7f, 1e7f) // highp
1228 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1229 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1230 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1231 const int scalarSize = glu::getDataTypeScalarSize(type);
1232 const float specialCases[] = { 0.0f, -0.0f, -0.9f, 0.9f, 1.0f, -1.0f };
1233 const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
1236 for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
1238 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1239 ((float*)values[0])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx];
1243 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + scalarSize*numSpecialCases, (numValues-numSpecialCases)*scalarSize);
1245 // If precision is mediump, make sure values can be represented in fp16 exactly
1246 if (precision == glu::PRECISION_MEDIUMP)
1248 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1249 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1253 bool compare (const void* const* inputs, const void* const* outputs)
1255 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1256 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1257 const int scalarSize = glu::getDataTypeScalarSize(type);
1259 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1261 // Require exact result.
1262 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1264 const float in0 = ((const float*)inputs[0])[compNdx];
1265 const float out0 = ((const float*)outputs[0])[compNdx];
1266 const bool isNeg = tcu::Float32(in0).sign() < 0;
1267 const float ref = isNeg ? (-float(int(-in0))) : float(int(in0));
1269 // \note: trunc() function definition is a bit broad on negative zeros. Ignore result sign if zero.
1270 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1274 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1281 const int mantissaBits = getMinMantissaBits(precision);
1282 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1283 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1285 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1287 const float in0 = ((const float*)inputs[0])[compNdx];
1288 const float out0 = ((const float*)outputs[0])[compNdx];
1289 const int minRes = int(in0-eps);
1290 const int maxRes = int(in0+eps);
1293 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1295 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1297 if (ulpDiff <= maxUlpDiff)
1306 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1316 class RoundCase : public CommonFunctionCase
1319 RoundCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1320 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "round", shaderType)
1322 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1323 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1324 m_spec.source = "out0 = round(in0);";
1327 void getInputValues (int numValues, void* const* values) const
1329 const Vec2 ranges[] =
1331 Vec2(-2.0f, 2.0f), // lowp
1332 Vec2(-1e3f, 1e3f), // mediump
1333 Vec2(-1e7f, 1e7f) // highp
1336 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1337 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1338 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1339 const int scalarSize = glu::getDataTypeScalarSize(type);
1340 int numSpecialCases = 0;
1343 if (precision != glu::PRECISION_LOWP)
1345 DE_ASSERT(numValues >= 10);
1346 for (int ndx = 0; ndx < 10; ndx++)
1348 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1349 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1350 numSpecialCases += 1;
1355 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1357 // If precision is mediump, make sure values can be represented in fp16 exactly
1358 if (precision == glu::PRECISION_MEDIUMP)
1360 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1361 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1365 bool compare (const void* const* inputs, const void* const* outputs)
1367 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1368 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1369 const bool hasZeroSign = supportsSignedZero(precision);
1370 const int scalarSize = glu::getDataTypeScalarSize(type);
1372 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1374 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1376 const float in0 = ((const float*)inputs[0])[compNdx];
1377 const float out0 = ((const float*)outputs[0])[compNdx];
1379 if (deFloatFrac(in0) == 0.5f)
1381 // Allow both ceil(in) and floor(in)
1382 const float ref0 = deFloatFloor(in0);
1383 const float ref1 = deFloatCeil(in0);
1384 const deUint32 ulpDiff0 = hasZeroSign ? getUlpDiff(out0, ref0) : getUlpDiffIgnoreZeroSign(out0, ref0);
1385 const deUint32 ulpDiff1 = hasZeroSign ? getUlpDiff(out0, ref1) : getUlpDiffIgnoreZeroSign(out0, ref1);
1387 if (ulpDiff0 > 0 && ulpDiff1 > 0)
1389 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " or " << HexFloat(ref1) << ", got ULP diff " << tcu::toHex(de::min(ulpDiff0, ulpDiff1));
1395 // Require exact result
1396 const float ref = roundEven(in0);
1397 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1401 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1409 const int mantissaBits = getMinMantissaBits(precision);
1410 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1411 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1413 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1415 const float in0 = ((const float*)inputs[0])[compNdx];
1416 const float out0 = ((const float*)outputs[0])[compNdx];
1417 const int minRes = int(roundEven(in0-eps));
1418 const int maxRes = int(roundEven(in0+eps));
1421 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1423 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1425 if (ulpDiff <= maxUlpDiff)
1434 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1444 class CeilCase : public CommonFunctionCase
1447 CeilCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1448 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ceil", shaderType)
1450 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1451 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1452 m_spec.source = "out0 = ceil(in0);";
1455 void getInputValues (int numValues, void* const* values) const
1457 const Vec2 ranges[] =
1459 Vec2(-2.0f, 2.0f), // lowp
1460 Vec2(-1e3f, 1e3f), // mediump
1461 Vec2(-1e7f, 1e7f) // highp
1464 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1465 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1466 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1467 const int scalarSize = glu::getDataTypeScalarSize(type);
1470 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1472 // If precision is mediump, make sure values can be represented in fp16 exactly
1473 if (precision == glu::PRECISION_MEDIUMP)
1475 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1476 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1480 bool compare (const void* const* inputs, const void* const* outputs)
1482 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1483 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1484 const bool hasZeroSign = supportsSignedZero(precision);
1485 const int scalarSize = glu::getDataTypeScalarSize(type);
1487 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1489 // Require exact result.
1490 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1492 const float in0 = ((const float*)inputs[0])[compNdx];
1493 const float out0 = ((const float*)outputs[0])[compNdx];
1494 const float ref = deFloatCeil(in0);
1496 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1500 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1507 const int mantissaBits = getMinMantissaBits(precision);
1508 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1509 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1511 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1513 const float in0 = ((const float*)inputs[0])[compNdx];
1514 const float out0 = ((const float*)outputs[0])[compNdx];
1515 const int minRes = int(deFloatCeil(in0-eps));
1516 const int maxRes = int(deFloatCeil(in0+eps));
1519 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1521 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1523 if (ulpDiff <= maxUlpDiff)
1530 if (!anyOk && de::inRange(0, minRes, maxRes))
1532 // Allow -0 as well.
1533 const int ulpDiff = de::abs((int)tcu::Float32(out0).bits() - (int)0x80000000u);
1534 anyOk = ((deUint32)ulpDiff <= maxUlpDiff);
1539 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1549 class FractCase : public CommonFunctionCase
1552 FractCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1553 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fract", shaderType)
1555 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1556 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1557 m_spec.source = "out0 = fract(in0);";
1560 void getInputValues (int numValues, void* const* values) const
1562 const Vec2 ranges[] =
1564 Vec2(-2.0f, 2.0f), // lowp
1565 Vec2(-1e3f, 1e3f), // mediump
1566 Vec2(-1e7f, 1e7f) // highp
1569 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1570 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1571 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1572 const int scalarSize = glu::getDataTypeScalarSize(type);
1573 int numSpecialCases = 0;
1576 if (precision != glu::PRECISION_LOWP)
1578 DE_ASSERT(numValues >= 10);
1579 for (int ndx = 0; ndx < 10; ndx++)
1581 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1582 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1583 numSpecialCases += 1;
1588 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1590 // If precision is mediump, make sure values can be represented in fp16 exactly
1591 if (precision == glu::PRECISION_MEDIUMP)
1593 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1594 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1598 bool compare (const void* const* inputs, const void* const* outputs)
1600 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1601 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1602 const bool hasZeroSign = supportsSignedZero(precision);
1603 const int scalarSize = glu::getDataTypeScalarSize(type);
1605 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1607 // Require exact result.
1608 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1610 const float in0 = ((const float*)inputs[0])[compNdx];
1611 const float out0 = ((const float*)outputs[0])[compNdx];
1612 const float ref = deFloatFrac(in0);
1614 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1618 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1625 const int mantissaBits = getMinMantissaBits(precision);
1626 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1628 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1630 const float in0 = ((const float*)inputs[0])[compNdx];
1631 const float out0 = ((const float*)outputs[0])[compNdx];
1633 if (int(deFloatFloor(in0-eps)) == int(deFloatFloor(in0+eps)))
1635 const float ref = deFloatFrac(in0);
1636 const int bitsLost = numBitsLostInOp(in0, ref);
1637 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(0, mantissaBits-bitsLost)); // ULP diff for rounded integer value.
1638 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1640 if (ulpDiff > maxUlpDiff)
1642 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << " with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1650 m_failMsg << "Expected [" << compNdx << "] < 1.0";
1661 static inline void frexp (float in, float* significand, int* exponent)
1663 const tcu::Float32 fpValue(in);
1665 if (!fpValue.isZero())
1667 // Construct float that has exactly the mantissa, and exponent of -1.
1668 *significand = tcu::Float32::construct(fpValue.sign(), -1, fpValue.mantissa()).asFloat();
1669 *exponent = fpValue.exponent()+1;
1673 *significand = fpValue.sign() < 0 ? -0.0f : 0.0f;
1678 static inline float ldexp (float significand, int exponent)
1680 const tcu::Float32 mant(significand);
1682 if (exponent == 0 && mant.isZero())
1684 return mant.sign() < 0 ? -0.0f : 0.0f;
1688 return tcu::Float32::construct(mant.sign(), exponent+mant.exponent(), mant.mantissa()).asFloat();
1692 class FrexpCase : public CommonFunctionCase
1695 FrexpCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1696 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "frexp", shaderType)
1698 const int vecSize = glu::getDataTypeScalarSize(baseType);
1699 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
1701 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1702 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1703 m_spec.outputs.push_back(Symbol("out1", glu::VarType(intType, glu::PRECISION_HIGHP)));
1704 m_spec.source = "out0 = frexp(in0, out1);";
1707 void getInputValues (int numValues, void* const* values) const
1709 const Vec2 ranges[] =
1711 Vec2(-2.0f, 2.0f), // lowp
1712 Vec2(-1e3f, 1e3f), // mediump
1713 Vec2(-1e7f, 1e7f) // highp
1716 de::Random rnd (deStringHash(getName()) ^ 0x2790au);
1717 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1718 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1719 const int scalarSize = glu::getDataTypeScalarSize(type);
1722 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1724 ((float*)values[0])[scalarSize*0 + compNdx] = 0.0f;
1725 ((float*)values[0])[scalarSize*1 + compNdx] = -0.0f;
1726 ((float*)values[0])[scalarSize*2 + compNdx] = 0.5f;
1727 ((float*)values[0])[scalarSize*3 + compNdx] = -0.5f;
1728 ((float*)values[0])[scalarSize*4 + compNdx] = 1.0f;
1729 ((float*)values[0])[scalarSize*5 + compNdx] = -1.0f;
1730 ((float*)values[0])[scalarSize*6 + compNdx] = 2.0f;
1731 ((float*)values[0])[scalarSize*7 + compNdx] = -2.0f;
1734 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + 8*scalarSize, (numValues-8)*scalarSize);
1737 bool compare (const void* const* inputs, const void* const* outputs)
1739 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1740 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1741 const int scalarSize = glu::getDataTypeScalarSize(type);
1742 const bool signedZero = supportsSignedZero(precision);
1744 const int mantissaBits = getMinMantissaBits(precision);
1745 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
1747 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1749 const float in0 = ((const float*)inputs[0])[compNdx];
1750 const float out0 = ((const float*)outputs[0])[compNdx];
1751 const int out1 = ((const int*)outputs[1])[compNdx];
1756 frexp(in0, &refOut0, &refOut1);
1758 const deUint32 ulpDiff0 = signedZero ? getUlpDiff(out0, refOut0) : getUlpDiffIgnoreZeroSign(out0, refOut0);
1760 if (ulpDiff0 > maxUlpDiff || out1 != refOut1)
1762 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", " << refOut1 << " with ULP threshold "
1763 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff0);
1772 class LdexpCase : public CommonFunctionCase
1775 LdexpCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1776 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ldexp", shaderType)
1778 const int vecSize = glu::getDataTypeScalarSize(baseType);
1779 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
1781 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1782 m_spec.inputs.push_back(Symbol("in1", glu::VarType(intType, glu::PRECISION_HIGHP)));
1783 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1784 m_spec.source = "out0 = ldexp(in0, in1);";
1787 void getInputValues (int numValues, void* const* values) const
1789 const Vec2 ranges[] =
1791 Vec2(-2.0f, 2.0f), // lowp
1792 Vec2(-1e3f, 1e3f), // mediump
1793 Vec2(-1e7f, 1e7f) // highp
1796 de::Random rnd (deStringHash(getName()) ^ 0x2790au);
1797 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1798 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1799 const int scalarSize = glu::getDataTypeScalarSize(type);
1803 const float easySpecialCases[] = { 0.0f, -0.0f, 0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f };
1805 DE_ASSERT(valueNdx + DE_LENGTH_OF_ARRAY(easySpecialCases) <= numValues);
1806 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(easySpecialCases); caseNdx++)
1811 frexp(easySpecialCases[caseNdx], &in0, &in1);
1813 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1815 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
1816 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
1824 // \note lowp and mediump can not necessarily fit the values in hard cases, so we'll use only easy ones.
1825 const int numEasyRandomCases = precision == glu::PRECISION_HIGHP ? 50 : (numValues-valueNdx);
1827 DE_ASSERT(valueNdx + numEasyRandomCases <= numValues);
1828 for (int caseNdx = 0; caseNdx < numEasyRandomCases; caseNdx++)
1830 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1832 const float in = rnd.getFloat(ranges[precision].x(), ranges[precision].y());
1836 frexp(in, &in0, &in1);
1838 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
1839 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
1847 const int numHardRandomCases = numValues-valueNdx;
1848 DE_ASSERT(numHardRandomCases >= 0 && valueNdx + numHardRandomCases <= numValues);
1850 for (int caseNdx = 0; caseNdx < numHardRandomCases; caseNdx++)
1852 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1854 const int fpExp = rnd.getInt(-126, 127);
1855 const int sign = rnd.getBool() ? -1 : +1;
1856 const deUint32 mantissa = (1u<<23) | (rnd.getUint32() & ((1u<<23)-1));
1857 const int in1 = rnd.getInt(de::max(-126, -126-fpExp), de::min(127, 127-fpExp));
1858 const float in0 = tcu::Float32::construct(sign, fpExp, mantissa).asFloat();
1860 DE_ASSERT(de::inRange(in1, -126, 127)); // See Khronos bug 11180
1861 DE_ASSERT(de::inRange(in1+fpExp, -126, 127));
1863 const float out = ldexp(in0, in1);
1865 DE_ASSERT(!tcu::Float32(out).isInf() && !tcu::Float32(out).isDenorm());
1868 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
1869 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
1877 bool compare (const void* const* inputs, const void* const* outputs)
1879 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1880 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1881 const int scalarSize = glu::getDataTypeScalarSize(type);
1882 const bool signedZero = supportsSignedZero(precision);
1884 const int mantissaBits = getMinMantissaBits(precision);
1885 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
1887 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1889 const float in0 = ((const float*)inputs[0])[compNdx];
1890 const int in1 = ((const int*)inputs[1])[compNdx];
1891 const float out0 = ((const float*)outputs[0])[compNdx];
1892 const float refOut0 = ldexp(in0, in1);
1893 const deUint32 ulpDiff = signedZero ? getUlpDiff(out0, refOut0) : getUlpDiffIgnoreZeroSign(out0, refOut0);
1895 const int inExp = tcu::Float32(in0).exponent();
1897 if (ulpDiff > maxUlpDiff)
1899 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", (exp = " << inExp << ") with ULP threshold "
1900 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
1909 class FmaCase : public CommonFunctionCase
1912 FmaCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1913 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fma", shaderType)
1915 m_spec.inputs.push_back(Symbol("a", glu::VarType(baseType, precision)));
1916 m_spec.inputs.push_back(Symbol("b", glu::VarType(baseType, precision)));
1917 m_spec.inputs.push_back(Symbol("c", glu::VarType(baseType, precision)));
1918 m_spec.outputs.push_back(Symbol("res", glu::VarType(baseType, precision)));
1919 m_spec.source = "res = fma(a, b, c);";
1920 m_spec.globalDeclarations = "#extension GL_EXT_gpu_shader5 : require\n";
1925 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_gpu_shader5"))
1926 throw tcu::NotSupportedError("GL_EXT_gpu_shader5 not supported");
1928 CommonFunctionCase::init();
1931 void getInputValues (int numValues, void* const* values) const
1933 const Vec2 ranges[] =
1935 Vec2(-2.0f, 2.0f), // lowp
1936 Vec2(-127.f, 127.f), // mediump
1937 Vec2(-1e7f, 1e7f) // highp
1940 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1941 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1942 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1943 const int scalarSize = glu::getDataTypeScalarSize(type);
1944 const int numMantissaBits = getMinMantissaBits(precision);
1945 const int maxNormalizedValueExponent = getMaxNormalizedValueExponent(precision);
1946 const int minNormalizedValueExponent = getMinNormalizedValueExponent(precision);
1947 const deUint32 representableMantissaMask = ((deUint32(1) << numMantissaBits) - 1) << (23 - (deUint32)numMantissaBits);
1948 const float specialCases[][3] =
1951 { 0.0f, 0.0f, 0.0f },
1952 { 0.0f, 1.0f, 0.0f },
1953 { 0.0f, 0.0f, -1.0f },
1954 { 1.0f, 1.0f, 0.0f },
1955 { 1.0f, 1.0f, 1.0f },
1956 { -1.0f, 1.0f, 0.0f },
1957 { 1.0f, -1.0f, 0.0f },
1958 { -1.0f, -1.0f, 0.0f },
1959 { -0.0f, 1.0f, 0.0f },
1960 { 1.0f, -0.0f, 0.0f }
1962 const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
1965 for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
1967 for (int inputNdx = 0; inputNdx < 3; inputNdx++)
1969 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1970 ((float*)values[inputNdx])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx][inputNdx];
1976 const int numScalars = (numValues-numSpecialCases)*scalarSize;
1977 const int offs = scalarSize*numSpecialCases;
1979 for (int inputNdx = 0; inputNdx < 3; inputNdx++)
1980 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[inputNdx] + offs, numScalars);
1983 // Make sure the values are representable in the target format
1984 if (precision != glu::PRECISION_HIGHP)
1986 const float largestRepresentableValue = tcu::Float32::constructBits(+1, maxNormalizedValueExponent, ((1u << numMantissaBits) - 1u) << (23u - (deUint32)numMantissaBits)).asFloat();
1988 // zero is not required to be representable, use smallest positive non-subnormal value
1989 const float zeroReplacement = tcu::Float32::constructBits(+1, minNormalizedValueExponent, 1).asFloat();
1991 for (int inputNdx = 0; inputNdx < 3; inputNdx++)
1993 for (int caseNdx = 0; caseNdx < numValues; ++caseNdx)
1995 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1997 float& value = ((float*)values[inputNdx])[caseNdx * scalarSize + scalarNdx];
1998 const tcu::Float32 float32Representation (value);
2000 // flush too small values to zero
2001 if (float32Representation.exponent() < minNormalizedValueExponent)
2003 value = zeroReplacement;
2005 // clamp too large values
2006 else if (float32Representation.exponent() > maxNormalizedValueExponent)
2008 value = (float32Representation.sign() == +1) ? (largestRepresentableValue) : (-largestRepresentableValue);
2010 // remove unrepresentable mantissa bits
2013 const tcu::Float32 targetRepresentation (tcu::Float32::constructBits(float32Representation.sign(),
2014 float32Representation.exponent(),
2015 float32Representation.mantissaBits() & representableMantissaMask));
2017 value = targetRepresentation.asFloat();
2025 static tcu::Interval fma (glu::Precision precision, float a, float b, float c)
2027 const tcu::FloatFormat formats[] =
2029 // minExp maxExp mantissa exact, subnormals infinities NaN
2030 tcu::FloatFormat(0, 0, 7, false, tcu::YES, tcu::MAYBE, tcu::MAYBE),
2031 tcu::FloatFormat(-13, 13, 9, false, tcu::MAYBE, tcu::MAYBE, tcu::MAYBE),
2032 tcu::FloatFormat(-126, 127, 23, true, tcu::MAYBE, tcu::YES, tcu::MAYBE)
2034 const tcu::FloatFormat& format = de::getSizedArrayElement<glu::PRECISION_LAST>(formats, precision);
2035 const tcu::Interval ia = format.convert(a);
2036 const tcu::Interval ib = format.convert(b);
2037 const tcu::Interval ic = format.convert(c);
2038 tcu::Interval prod0;
2039 tcu::Interval prod1;
2040 tcu::Interval prod2;
2041 tcu::Interval prod3;
2045 TCU_SET_INTERVAL(prod0, tmp, tmp = ia.lo() * ib.lo());
2046 TCU_SET_INTERVAL(prod1, tmp, tmp = ia.lo() * ib.hi());
2047 TCU_SET_INTERVAL(prod2, tmp, tmp = ia.hi() * ib.lo());
2048 TCU_SET_INTERVAL(prod3, tmp, tmp = ia.hi() * ib.hi());
2050 prod = format.convert(format.roundOut(prod0 | prod1 | prod2 | prod3, ia.isFinite() && ib.isFinite()));
2052 TCU_SET_INTERVAL_BOUNDS(res, tmp,
2053 tmp = prod.lo() + ic.lo(),
2054 tmp = prod.hi() + ic.hi());
2056 return format.convert(format.roundOut(res, prod.isFinite() && ic.isFinite()));
2059 bool compare (const void* const* inputs, const void* const* outputs)
2061 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2062 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2063 const int scalarSize = glu::getDataTypeScalarSize(type);
2065 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2067 const float a = ((const float*)inputs[0])[compNdx];
2068 const float b = ((const float*)inputs[1])[compNdx];
2069 const float c = ((const float*)inputs[2])[compNdx];
2070 const float res = ((const float*)outputs[0])[compNdx];
2071 const tcu::Interval ref = fma(precision, a, b, c);
2073 if (!ref.contains(res))
2075 m_failMsg << "Expected [" << compNdx << "] = " << ref;
2084 ShaderCommonFunctionTests::ShaderCommonFunctionTests (Context& context)
2085 : TestCaseGroup(context, "common", "Common function tests")
2089 ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void)
2093 template<class TestClass>
2094 static void addFunctionCases (TestCaseGroup* parent, const char* functionName, bool floatTypes, bool intTypes, bool uintTypes, deUint32 shaderBits)
2096 tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), functionName, functionName);
2097 parent->addChild(group);
2099 const glu::DataType scalarTypes[] =
2106 for (int scalarTypeNdx = 0; scalarTypeNdx < DE_LENGTH_OF_ARRAY(scalarTypes); scalarTypeNdx++)
2108 const glu::DataType scalarType = scalarTypes[scalarTypeNdx];
2110 if ((!floatTypes && scalarType == glu::TYPE_FLOAT) ||
2111 (!intTypes && scalarType == glu::TYPE_INT) ||
2112 (!uintTypes && scalarType == glu::TYPE_UINT))
2115 for (int vecSize = 1; vecSize <= 4; vecSize++)
2117 for (int prec = glu::PRECISION_LOWP; prec <= glu::PRECISION_HIGHP; prec++)
2119 for (int shaderTypeNdx = 0; shaderTypeNdx < glu::SHADERTYPE_LAST; shaderTypeNdx++)
2121 if (shaderBits & (1<<shaderTypeNdx))
2122 group->addChild(new TestClass(parent->getContext(), glu::DataType(scalarType + vecSize - 1), glu::Precision(prec), glu::ShaderType(shaderTypeNdx)));
2129 void ShaderCommonFunctionTests::init (void)
2133 VS = (1<<glu::SHADERTYPE_VERTEX),
2134 TC = (1<<glu::SHADERTYPE_TESSELLATION_CONTROL),
2135 TE = (1<<glu::SHADERTYPE_TESSELLATION_EVALUATION),
2136 GS = (1<<glu::SHADERTYPE_GEOMETRY),
2137 FS = (1<<glu::SHADERTYPE_FRAGMENT),
2138 CS = (1<<glu::SHADERTYPE_COMPUTE),
2140 ALL_SHADERS = VS|TC|TE|GS|FS|CS,
2141 NEW_SHADERS = TC|TE|GS|CS,
2144 // Float? Int? Uint? Shaders
2145 addFunctionCases<AbsCase> (this, "abs", true, true, false, NEW_SHADERS);
2146 addFunctionCases<SignCase> (this, "sign", true, true, false, NEW_SHADERS);
2147 addFunctionCases<FloorCase> (this, "floor", true, false, false, NEW_SHADERS);
2148 addFunctionCases<TruncCase> (this, "trunc", true, false, false, NEW_SHADERS);
2149 addFunctionCases<RoundCase> (this, "round", true, false, false, NEW_SHADERS);
2150 addFunctionCases<RoundEvenCase> (this, "roundeven", true, false, false, NEW_SHADERS);
2151 addFunctionCases<CeilCase> (this, "ceil", true, false, false, NEW_SHADERS);
2152 addFunctionCases<FractCase> (this, "fract", true, false, false, NEW_SHADERS);
2154 addFunctionCases<ModfCase> (this, "modf", true, false, false, NEW_SHADERS);
2161 addFunctionCases<IsnanCase> (this, "isnan", true, false, false, NEW_SHADERS);
2162 addFunctionCases<IsinfCase> (this, "isinf", true, false, false, NEW_SHADERS);
2163 addFunctionCases<FloatBitsToIntCase> (this, "floatbitstoint", true, false, false, NEW_SHADERS);
2164 addFunctionCases<FloatBitsToUintCase> (this, "floatbitstouint", true, false, false, NEW_SHADERS);
2166 addFunctionCases<FrexpCase> (this, "frexp", true, false, false, ALL_SHADERS);
2167 addFunctionCases<LdexpCase> (this, "ldexp", true, false, false, ALL_SHADERS);
2168 addFunctionCases<FmaCase> (this, "fma", true, false, false, ALL_SHADERS);
2170 // (u)intBitsToFloat()
2172 const deUint32 shaderBits = NEW_SHADERS;
2173 tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "intbitstofloat", "intBitsToFloat() Tests");
2174 tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat", "uintBitsToFloat() Tests");
2177 addChild(uintGroup);
2179 for (int vecSize = 1; vecSize < 4; vecSize++)
2181 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
2182 const glu::DataType uintType = vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT;
2184 for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
2186 if (shaderBits & (1<<shaderType))
2188 intGroup->addChild(new BitsToFloatCase(m_context, intType, glu::ShaderType(shaderType)));
2189 uintGroup->addChild(new BitsToFloatCase(m_context, uintType, glu::ShaderType(shaderType)));