1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.0 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 "es3fShaderCommonFunctionTests.hpp"
25 #include "glsShaderExecUtil.hpp"
26 #include "tcuTestLog.hpp"
27 #include "tcuFormatUtil.hpp"
28 #include "tcuFloat.hpp"
29 #include "deRandom.hpp"
43 using namespace gls::ShaderExecUtil;
54 template<typename T, int Size>
58 VecArrayAccess (const void* ptr) : m_array((tcu::Vector<T, Size>*)ptr) {}
59 ~VecArrayAccess (void) {}
61 const tcu::Vector<T, Size>& operator[] (size_t offset) const { return m_array[offset]; }
62 tcu::Vector<T, Size>& operator[] (size_t offset) { return m_array[offset]; }
65 tcu::Vector<T, Size>* m_array;
68 template<typename T> T randomScalar (de::Random& rnd, T minValue, T maxValue);
69 template<> inline float randomScalar (de::Random& rnd, float minValue, float maxValue) { return rnd.getFloat(minValue, maxValue); }
70 template<> inline deInt32 randomScalar (de::Random& rnd, deInt32 minValue, deInt32 maxValue) { return rnd.getInt(minValue, maxValue); }
71 template<> inline deUint32 randomScalar (de::Random& rnd, deUint32 minValue, deUint32 maxValue) { return minValue + rnd.getUint32() % (maxValue - minValue + 1); }
73 template<typename T, int Size>
74 inline tcu::Vector<T, Size> randomVector (de::Random& rnd, const tcu::Vector<T, Size>& minValue, const tcu::Vector<T, Size>& maxValue)
76 tcu::Vector<T, Size> res;
77 for (int ndx = 0; ndx < Size; ndx++)
78 res[ndx] = randomScalar<T>(rnd, minValue[ndx], maxValue[ndx]);
82 template<typename T, int Size>
83 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)
85 VecArrayAccess<T, Size> access(dst);
86 for (int ndx = 0; ndx < numValues; ndx++)
87 access[offset + ndx] = randomVector<T, Size>(rnd, minValue, maxValue);
91 static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0)
93 T* typedPtr = (T*)dst;
94 for (int ndx = 0; ndx < numValues; ndx++)
95 typedPtr[offset + ndx] = randomScalar<T>(rnd, minValue, maxValue);
98 inline int numBitsLostInOp (float input, float output)
100 const int inExp = tcu::Float32(input).exponent();
101 const int outExp = tcu::Float32(output).exponent();
103 return de::max(0, inExp-outExp); // Lost due to mantissa shift.
106 inline deUint32 getUlpDiff (float a, float b)
108 const deUint32 aBits = tcu::Float32(a).bits();
109 const deUint32 bBits = tcu::Float32(b).bits();
110 return aBits > bBits ? aBits - bBits : bBits - aBits;
113 inline deUint32 getUlpDiffIgnoreZeroSign (float a, float b)
115 if (tcu::Float32(a).isZero())
116 return getUlpDiff(tcu::Float32::construct(tcu::Float32(b).sign(), 0, 0).asFloat(), b);
117 else if (tcu::Float32(b).isZero())
118 return getUlpDiff(a, tcu::Float32::construct(tcu::Float32(a).sign(), 0, 0).asFloat());
120 return getUlpDiff(a, b);
123 inline bool supportsSignedZero (glu::Precision precision)
125 // \note GLSL ES 3.0 doesn't really require support for -0, but we require it for highp
126 // as it is very widely supported.
127 return precision == glu::PRECISION_HIGHP;
130 inline float getEpsFromMaxUlpDiff (float value, deUint32 ulpDiff)
132 const int exp = tcu::Float32(value).exponent();
133 return tcu::Float32::construct(+1, exp, (1u<<23) | ulpDiff).asFloat() - tcu::Float32::construct(+1, exp, 1u<<23).asFloat();
136 inline deUint32 getMaxUlpDiffFromBits (int numAccurateBits)
138 const int numGarbageBits = 23-numAccurateBits;
139 const deUint32 mask = (1u<<numGarbageBits)-1u;
144 inline float getEpsFromBits (float value, int numAccurateBits)
146 return getEpsFromMaxUlpDiff(value, getMaxUlpDiffFromBits(numAccurateBits));
149 static int getMinMantissaBits (glu::Precision precision)
157 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(bits) == glu::PRECISION_LAST);
158 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(bits)));
159 return bits[precision];
162 // CommonFunctionCase
164 class CommonFunctionCase : public TestCase
167 CommonFunctionCase (Context& context, const char* name, const char* description, glu::ShaderType shaderType);
168 ~CommonFunctionCase (void);
172 IterateResult iterate (void);
175 CommonFunctionCase (const CommonFunctionCase& other);
176 CommonFunctionCase& operator= (const CommonFunctionCase& other);
178 virtual void getInputValues (int numValues, void* const* values) const = 0;
179 virtual bool compare (const void* const* inputs, const void* const* outputs) = 0;
181 glu::ShaderType m_shaderType;
185 std::ostringstream m_failMsg; //!< Comparison failure help message.
188 ShaderExecutor* m_executor;
191 CommonFunctionCase::CommonFunctionCase (Context& context, const char* name, const char* description, glu::ShaderType shaderType)
192 : TestCase (context, name, description)
193 , m_shaderType (shaderType)
195 , m_executor (DE_NULL)
197 m_spec.version = glu::GLSL_VERSION_300_ES;
200 CommonFunctionCase::~CommonFunctionCase (void)
202 CommonFunctionCase::deinit();
205 void CommonFunctionCase::init (void)
207 DE_ASSERT(!m_executor);
209 m_executor = createExecutor(m_context.getRenderContext(), m_shaderType, m_spec);
210 m_testCtx.getLog() << m_executor;
212 if (!m_executor->isOk())
213 throw tcu::TestError("Compile failed");
216 void CommonFunctionCase::deinit (void)
219 m_executor = DE_NULL;
222 static vector<int> getScalarSizes (const vector<Symbol>& symbols)
224 vector<int> sizes(symbols.size());
225 for (int ndx = 0; ndx < (int)symbols.size(); ++ndx)
226 sizes[ndx] = symbols[ndx].varType.getScalarSize();
230 static int computeTotalScalarSize (const vector<Symbol>& symbols)
233 for (vector<Symbol>::const_iterator sym = symbols.begin(); sym != symbols.end(); ++sym)
234 totalSize += sym->varType.getScalarSize();
238 static vector<void*> getInputOutputPointers (const vector<Symbol>& symbols, vector<deUint32>& data, const int numValues)
240 vector<void*> pointers (symbols.size());
241 int curScalarOffset = 0;
243 for (int varNdx = 0; varNdx < (int)symbols.size(); ++varNdx)
245 const Symbol& var = symbols[varNdx];
246 const int scalarSize = var.varType.getScalarSize();
248 // Uses planar layout as input/output specs do not support strides.
249 pointers[varNdx] = &data[curScalarOffset];
250 curScalarOffset += scalarSize*numValues;
253 DE_ASSERT(curScalarOffset == (int)data.size());
258 // \todo [2013-08-08 pyry] Make generic utility and move to glu?
263 HexFloat (const float value_) : value(value_) {}
266 std::ostream& operator<< (std::ostream& str, const HexFloat& v)
268 return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
273 const deUint32 value;
274 HexBool (const deUint32 value_) : value(value_) {}
277 std::ostream& operator<< (std::ostream& str, const HexBool& v)
279 return str << (v.value ? "true" : "false") << " / " << tcu::toHex(v.value);
284 const glu::VarType& type;
287 VarValue (const glu::VarType& type_, const void* value_) : type(type_), value(value_) {}
290 std::ostream& operator<< (std::ostream& str, const VarValue& varValue)
292 DE_ASSERT(varValue.type.isBasicType());
294 const glu::DataType basicType = varValue.type.getBasicType();
295 const glu::DataType scalarType = glu::getDataTypeScalarType(basicType);
296 const int numComponents = glu::getDataTypeScalarSize(basicType);
298 if (numComponents > 1)
299 str << glu::getDataTypeName(basicType) << "(";
301 for (int compNdx = 0; compNdx < numComponents; compNdx++)
308 case glu::TYPE_FLOAT: str << HexFloat(((const float*)varValue.value)[compNdx]); break;
309 case glu::TYPE_INT: str << ((const deInt32*)varValue.value)[compNdx]; break;
310 case glu::TYPE_UINT: str << tcu::toHex(((const deUint32*)varValue.value)[compNdx]); break;
311 case glu::TYPE_BOOL: str << HexBool(((const deUint32*)varValue.value)[compNdx]); break;
318 if (numComponents > 1)
324 CommonFunctionCase::IterateResult CommonFunctionCase::iterate (void)
326 const int numInputScalars = computeTotalScalarSize(m_spec.inputs);
327 const int numOutputScalars = computeTotalScalarSize(m_spec.outputs);
328 vector<deUint32> inputData (numInputScalars * m_numValues);
329 vector<deUint32> outputData (numOutputScalars * m_numValues);
330 const vector<void*> inputPointers = getInputOutputPointers(m_spec.inputs, inputData, m_numValues);
331 const vector<void*> outputPointers = getInputOutputPointers(m_spec.outputs, outputData, m_numValues);
333 // Initialize input data.
334 getInputValues(m_numValues, &inputPointers[0]);
337 m_executor->useProgram();
338 m_executor->execute(m_numValues, &inputPointers[0], &outputPointers[0]);
342 const vector<int> inScalarSizes = getScalarSizes(m_spec.inputs);
343 const vector<int> outScalarSizes = getScalarSizes(m_spec.outputs);
344 vector<void*> curInputPtr (inputPointers.size());
345 vector<void*> curOutputPtr (outputPointers.size());
348 for (int valNdx = 0; valNdx < m_numValues; valNdx++)
350 // Set up pointers for comparison.
351 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx)
352 curInputPtr[inNdx] = (deUint32*)inputPointers[inNdx] + inScalarSizes[inNdx]*valNdx;
354 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx)
355 curOutputPtr[outNdx] = (deUint32*)outputPointers[outNdx] + outScalarSizes[outNdx]*valNdx;
357 if (!compare(&curInputPtr[0], &curOutputPtr[0]))
359 // \todo [2013-08-08 pyry] We probably want to log reference value as well?
361 m_testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n " << m_failMsg.str() << TestLog::EndMessage;
363 m_testCtx.getLog() << TestLog::Message << " inputs:" << TestLog::EndMessage;
364 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++)
365 m_testCtx.getLog() << TestLog::Message << " " << m_spec.inputs[inNdx].name << " = "
366 << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx])
367 << TestLog::EndMessage;
369 m_testCtx.getLog() << TestLog::Message << " outputs:" << TestLog::EndMessage;
370 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++)
371 m_testCtx.getLog() << TestLog::Message << " " << m_spec.outputs[outNdx].name << " = "
372 << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx])
373 << TestLog::EndMessage;
381 m_testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage;
383 m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
384 numFailed == 0 ? "Pass" : "Result comparison failed");
390 static const char* getPrecisionPostfix (glu::Precision precision)
392 static const char* s_postfix[] =
398 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_postfix) == glu::PRECISION_LAST);
399 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
400 return s_postfix[precision];
403 static const char* getShaderTypePostfix (glu::ShaderType shaderType)
405 static const char* s_postfix[] =
410 DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
411 return s_postfix[shaderType];
414 static std::string getCommonFuncCaseName (glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
416 return string(glu::getDataTypeName(baseType)) + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType);
419 class AbsCase : public CommonFunctionCase
422 AbsCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
423 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "abs", shaderType)
425 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
426 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
427 m_spec.source = "out0 = abs(in0);";
430 void getInputValues (int numValues, void* const* values) const
432 const Vec2 floatRanges[] =
434 Vec2(-2.0f, 2.0f), // lowp
435 Vec2(-1e3f, 1e3f), // mediump
436 Vec2(-1e7f, 1e7f) // highp
438 const IVec2 intRanges[] =
440 IVec2(-(1<<7)+1, (1<<7)-1),
441 IVec2(-(1<<15)+1, (1<<15)-1),
442 IVec2(0x80000001, 0x7fffffff)
445 de::Random rnd (deStringHash(getName()) ^ 0x235facu);
446 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
447 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
448 const int scalarSize = glu::getDataTypeScalarSize(type);
450 if (glu::isDataTypeFloatOrVec(type))
451 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), values[0], numValues*scalarSize);
453 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize);
456 bool compare (const void* const* inputs, const void* const* outputs)
458 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
459 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
460 const int scalarSize = glu::getDataTypeScalarSize(type);
462 if (glu::isDataTypeFloatOrVec(type))
464 const int mantissaBits = getMinMantissaBits(precision);
465 const deUint32 maxUlpDiff = (1u<<(23-mantissaBits))-1u;
467 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
469 const float in0 = ((const float*)inputs[0])[compNdx];
470 const float out0 = ((const float*)outputs[0])[compNdx];
471 const float ref0 = de::abs(in0);
472 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
474 if (ulpDiff0 > maxUlpDiff)
476 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
483 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
485 const int in0 = ((const int*)inputs[0])[compNdx];
486 const int out0 = ((const int*)outputs[0])[compNdx];
487 const int ref0 = de::abs(in0);
491 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
501 class SignCase : public CommonFunctionCase
504 SignCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
505 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "sign", shaderType)
507 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
508 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
509 m_spec.source = "out0 = sign(in0);";
512 void getInputValues (int numValues, void* const* values) const
514 const Vec2 floatRanges[] =
516 Vec2(-2.0f, 2.0f), // lowp
517 Vec2(-1e4f, 1e4f), // mediump - note: may end up as inf
518 Vec2(-1e8f, 1e8f) // highp - note: may end up as inf
520 const IVec2 intRanges[] =
522 IVec2(-(1<<7), (1<<7)-1),
523 IVec2(-(1<<15), (1<<15)-1),
524 IVec2(0x80000000, 0x7fffffff)
527 de::Random rnd (deStringHash(getName()) ^ 0x324u);
528 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
529 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
530 const int scalarSize = glu::getDataTypeScalarSize(type);
532 if (glu::isDataTypeFloatOrVec(type))
535 std::fill((float*)values[0], (float*)values[0] + scalarSize, +1.0f);
536 std::fill((float*)values[0] + scalarSize*1, (float*)values[0] + scalarSize*2, -1.0f);
537 std::fill((float*)values[0] + scalarSize*2, (float*)values[0] + scalarSize*3, 0.0f);
538 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), (float*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
542 std::fill((int*)values[0], (int*)values[0] + scalarSize, +1);
543 std::fill((int*)values[0] + scalarSize*1, (int*)values[0] + scalarSize*2, -1);
544 std::fill((int*)values[0] + scalarSize*2, (int*)values[0] + scalarSize*3, 0);
545 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
549 bool compare (const void* const* inputs, const void* const* outputs)
551 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
552 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
553 const int scalarSize = glu::getDataTypeScalarSize(type);
555 if (glu::isDataTypeFloatOrVec(type))
557 // Both highp and mediump should be able to represent -1, 0, and +1 exactly
558 const deUint32 maxUlpDiff = precision == glu::PRECISION_LOWP ? getMaxUlpDiffFromBits(getMinMantissaBits(precision)) : 0;
560 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
562 const float in0 = ((const float*)inputs[0])[compNdx];
563 const float out0 = ((const float*)outputs[0])[compNdx];
564 const float ref0 = in0 < 0.0f ? -1.0f :
565 in0 > 0.0f ? +1.0f : 0.0f;
566 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
568 if (ulpDiff0 > maxUlpDiff)
570 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
577 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
579 const int in0 = ((const int*)inputs[0])[compNdx];
580 const int out0 = ((const int*)outputs[0])[compNdx];
581 const int ref0 = in0 < 0 ? -1 :
586 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
596 static float roundEven (float v)
598 const float q = deFloatFrac(v);
599 const int truncated = int(v-q);
600 const int rounded = (q > 0.5f) ? (truncated + 1) : // Rounded up
601 (q == 0.5f && (truncated % 2 != 0)) ? (truncated + 1) : // Round to nearest even at 0.5
602 truncated; // Rounded down
604 return float(rounded);
607 class RoundEvenCase : public CommonFunctionCase
610 RoundEvenCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
611 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "roundEven", shaderType)
613 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
614 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
615 m_spec.source = "out0 = roundEven(in0);";
618 void getInputValues (int numValues, void* const* values) const
620 const Vec2 ranges[] =
622 Vec2(-2.0f, 2.0f), // lowp
623 Vec2(-1e3f, 1e3f), // mediump
624 Vec2(-1e7f, 1e7f) // highp
627 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
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 int numSpecialCases = 0;
634 if (precision != glu::PRECISION_LOWP)
636 DE_ASSERT(numValues >= 20);
637 for (int ndx = 0; ndx < 20; ndx++)
639 const float v = de::clamp(float(ndx) - 10.5f, ranges[precision].x(), ranges[precision].y());
640 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
641 numSpecialCases += 1;
646 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
648 // If precision is mediump, make sure values can be represented in fp16 exactly
649 if (precision == glu::PRECISION_MEDIUMP)
651 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
652 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
656 bool compare (const void* const* inputs, const void* const* outputs)
658 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
659 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
660 const bool hasSignedZero = supportsSignedZero(precision);
661 const int scalarSize = glu::getDataTypeScalarSize(type);
663 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
665 // Require exact rounding result.
666 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
668 const float in0 = ((const float*)inputs[0])[compNdx];
669 const float out0 = ((const float*)outputs[0])[compNdx];
670 const float ref = roundEven(in0);
672 const deUint32 ulpDiff = hasSignedZero ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
676 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
683 const int mantissaBits = getMinMantissaBits(precision);
684 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
685 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
687 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
689 const float in0 = ((const float*)inputs[0])[compNdx];
690 const float out0 = ((const float*)outputs[0])[compNdx];
691 const int minRes = int(roundEven(in0-eps));
692 const int maxRes = int(roundEven(in0+eps));
695 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
697 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
699 if (ulpDiff <= maxUlpDiff)
708 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
718 class ModfCase : public CommonFunctionCase
721 ModfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
722 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "modf", shaderType)
724 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
725 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
726 m_spec.outputs.push_back(Symbol("out1", glu::VarType(baseType, precision)));
727 m_spec.source = "out0 = modf(in0, out1);";
730 void getInputValues (int numValues, void* const* values) const
732 const Vec2 ranges[] =
734 Vec2(-2.0f, 2.0f), // lowp
735 Vec2(-1e3f, 1e3f), // mediump
736 Vec2(-1e7f, 1e7f) // highp
739 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
740 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
741 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
742 const int scalarSize = glu::getDataTypeScalarSize(type);
744 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
747 bool compare (const void* const* inputs, const void* const* outputs)
749 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
750 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
751 const bool hasZeroSign = supportsSignedZero(precision);
752 const int scalarSize = glu::getDataTypeScalarSize(type);
754 const int mantissaBits = getMinMantissaBits(precision);
756 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
758 const float in0 = ((const float*)inputs[0])[compNdx];
759 const float out0 = ((const float*)outputs[0])[compNdx];
760 const float out1 = ((const float*)outputs[1])[compNdx];
762 const float refOut1 = float(int(in0));
763 const float refOut0 = in0 - refOut1;
765 const int bitsLost = precision != glu::PRECISION_HIGHP ? numBitsLostInOp(in0, refOut0) : 0;
766 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(mantissaBits - bitsLost, 0));
768 const float resSum = out0 + out1;
770 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(resSum, in0) : getUlpDiffIgnoreZeroSign(resSum, in0);
772 if (ulpDiff > maxUlpDiff)
774 m_failMsg << "Expected [" << compNdx << "] = (" << HexFloat(refOut0) << ") + (" << HexFloat(refOut1) << ") = " << HexFloat(in0) << " with ULP threshold "
775 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
784 class IsnanCase : public CommonFunctionCase
787 IsnanCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
788 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isnan", shaderType)
790 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
792 const int vecSize = glu::getDataTypeScalarSize(baseType);
793 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
795 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
796 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
797 m_spec.source = "out0 = isnan(in0);";
800 void getInputValues (int numValues, void* const* values) const
802 de::Random rnd (deStringHash(getName()) ^ 0xc2a39fu);
803 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
804 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
805 const int scalarSize = glu::getDataTypeScalarSize(type);
806 const int mantissaBits = getMinMantissaBits(precision);
807 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
809 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
811 const bool isNan = rnd.getFloat() > 0.3f;
812 const bool isInf = !isNan && rnd.getFloat() > 0.4f;
813 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
814 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
815 const deUint32 sign = rnd.getUint32() & 0x1u;
816 const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
818 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
820 ((deUint32*)values[0])[valNdx] = value;
824 bool compare (const void* const* inputs, const void* const* outputs)
826 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
827 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
828 const int scalarSize = glu::getDataTypeScalarSize(type);
830 if (precision == glu::PRECISION_HIGHP)
832 // Only highp is required to support inf/nan
833 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
835 const float in0 = ((const float*)inputs[0])[compNdx];
836 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
837 const deUint32 ref = tcu::Float32(in0).isNaN() ? 1u : 0u;
841 m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
848 // Value can be either 0 or 1
849 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
851 const int out0 = ((const int*)outputs[0])[compNdx];
853 if (out0 != 0 && out0 != 1)
855 m_failMsg << "Expected [" << compNdx << "] = 0 / 1";
865 class IsinfCase : public CommonFunctionCase
868 IsinfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
869 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isinf", shaderType)
871 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
873 const int vecSize = glu::getDataTypeScalarSize(baseType);
874 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
876 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
877 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
878 m_spec.source = "out0 = isinf(in0);";
881 void getInputValues (int numValues, void* const* values) const
883 de::Random rnd (deStringHash(getName()) ^ 0xc2a39fu);
884 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
885 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
886 const int scalarSize = glu::getDataTypeScalarSize(type);
887 const int mantissaBits = getMinMantissaBits(precision);
888 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
890 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
892 const bool isInf = rnd.getFloat() > 0.3f;
893 const bool isNan = !isInf && rnd.getFloat() > 0.4f;
894 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
895 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
896 const deUint32 sign = rnd.getUint32() & 0x1u;
897 const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
899 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
901 ((deUint32*)values[0])[valNdx] = value;
905 bool compare (const void* const* inputs, const void* const* outputs)
907 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
908 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
909 const int scalarSize = glu::getDataTypeScalarSize(type);
911 if (precision == glu::PRECISION_HIGHP)
913 // Only highp is required to support inf/nan
914 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
916 const float in0 = ((const float*)inputs[0])[compNdx];
917 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
918 const deUint32 ref = tcu::Float32(in0).isInf() ? 1u : 0u;
922 m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
929 // Value can be either 0 or 1
930 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
932 const int out0 = ((const int*)outputs[0])[compNdx];
934 if (out0 != 0 && out0 != 1)
936 m_failMsg << "Expected [" << compNdx << "] = 0 / 1";
946 class FloatBitsToUintIntCase : public CommonFunctionCase
949 FloatBitsToUintIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType, bool outIsSigned)
950 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), outIsSigned ? "floatBitsToInt" : "floatBitsToUint", shaderType)
952 const int vecSize = glu::getDataTypeScalarSize(baseType);
953 const glu::DataType intType = outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT)
954 : (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT);
956 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
957 m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP)));
958 m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);";
961 void getInputValues (int numValues, void* const* values) const
963 const Vec2 ranges[] =
965 Vec2(-2.0f, 2.0f), // lowp
966 Vec2(-1e3f, 1e3f), // mediump
967 Vec2(-1e7f, 1e7f) // highp
970 de::Random rnd (deStringHash(getName()) ^ 0x2790au);
971 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
972 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
973 const int scalarSize = glu::getDataTypeScalarSize(type);
975 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
978 bool compare (const void* const* inputs, const void* const* outputs)
980 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
981 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
982 const int scalarSize = glu::getDataTypeScalarSize(type);
984 const int mantissaBits = getMinMantissaBits(precision);
985 const int maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
987 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
989 const float in0 = ((const float*)inputs[0])[compNdx];
990 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
991 const deUint32 refOut0 = tcu::Float32(in0).bits();
992 const int ulpDiff = de::abs((int)out0 - (int)refOut0);
994 if (ulpDiff > maxUlpDiff)
996 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold "
997 << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1006 class FloatBitsToIntCase : public FloatBitsToUintIntCase
1009 FloatBitsToIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1010 : FloatBitsToUintIntCase(context, baseType, precision, shaderType, true)
1015 class FloatBitsToUintCase : public FloatBitsToUintIntCase
1018 FloatBitsToUintCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1019 : FloatBitsToUintIntCase(context, baseType, precision, shaderType, false)
1024 class BitsToFloatCase : public CommonFunctionCase
1027 BitsToFloatCase (Context& context, glu::DataType baseType, glu::ShaderType shaderType)
1028 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP, shaderType).c_str(), glu::isDataTypeIntOrIVec(baseType) ? "intBitsToFloat" : "uintBitsToFloat", shaderType)
1030 const bool inIsSigned = glu::isDataTypeIntOrIVec(baseType);
1031 const int vecSize = glu::getDataTypeScalarSize(baseType);
1032 const glu::DataType floatType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1034 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1035 m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP)));
1036 m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);";
1039 void getInputValues (int numValues, void* const* values) const
1041 de::Random rnd (deStringHash(getName()) ^ 0xbbb225u);
1042 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1043 const int scalarSize = glu::getDataTypeScalarSize(type);
1044 const Vec2 range (-1e8f, +1e8f);
1046 // \note Filled as floats.
1047 fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize);
1050 bool compare (const void* const* inputs, const void* const* outputs)
1052 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1053 const int scalarSize = glu::getDataTypeScalarSize(type);
1054 const deUint32 maxUlpDiff = 0;
1056 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1058 const float in0 = ((const float*)inputs[0])[compNdx];
1059 const float out0 = ((const float*)outputs[0])[compNdx];
1060 const deUint32 ulpDiff = getUlpDiff(in0, out0);
1062 if (ulpDiff > maxUlpDiff)
1064 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits()) << " with ULP threshold "
1065 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
1074 class FloorCase : public CommonFunctionCase
1077 FloorCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1078 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "floor", shaderType)
1080 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1081 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1082 m_spec.source = "out0 = floor(in0);";
1085 void getInputValues (int numValues, void* const* values) const
1087 const Vec2 ranges[] =
1089 Vec2(-2.0f, 2.0f), // lowp
1090 Vec2(-1e3f, 1e3f), // mediump
1091 Vec2(-1e7f, 1e7f) // highp
1094 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1095 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1096 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1097 const int scalarSize = glu::getDataTypeScalarSize(type);
1099 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1101 // If precision is mediump, make sure values can be represented in fp16 exactly
1102 if (precision == glu::PRECISION_MEDIUMP)
1104 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1105 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1109 bool compare (const void* const* inputs, const void* const* outputs)
1111 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1112 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1113 const int scalarSize = glu::getDataTypeScalarSize(type);
1115 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1117 // Require exact result.
1118 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1120 const float in0 = ((const float*)inputs[0])[compNdx];
1121 const float out0 = ((const float*)outputs[0])[compNdx];
1122 const float ref = deFloatFloor(in0);
1124 const deUint32 ulpDiff = getUlpDiff(out0, ref);
1128 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1135 const int mantissaBits = getMinMantissaBits(precision);
1136 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1137 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1139 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1141 const float in0 = ((const float*)inputs[0])[compNdx];
1142 const float out0 = ((const float*)outputs[0])[compNdx];
1143 const int minRes = int(deFloatFloor(in0-eps));
1144 const int maxRes = int(deFloatFloor(in0+eps));
1147 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1149 const deUint32 ulpDiff = getUlpDiff(out0, float(roundedVal));
1151 if (ulpDiff <= maxUlpDiff)
1160 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1170 class TruncCase : public CommonFunctionCase
1173 TruncCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1174 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "trunc", shaderType)
1176 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1177 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1178 m_spec.source = "out0 = trunc(in0);";
1181 void getInputValues (int numValues, void* const* values) const
1183 const Vec2 ranges[] =
1185 Vec2(-2.0f, 2.0f), // lowp
1186 Vec2(-1e3f, 1e3f), // mediump
1187 Vec2(-1e7f, 1e7f) // highp
1190 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1191 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1192 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1193 const int scalarSize = glu::getDataTypeScalarSize(type);
1194 const float specialCases[] = { 0.0f, -0.0f, -0.9f, 0.9f, 1.0f, -1.0f };
1195 const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
1198 for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
1200 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1201 ((float*)values[0])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx];
1205 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + scalarSize*numSpecialCases, (numValues-numSpecialCases)*scalarSize);
1207 // If precision is mediump, make sure values can be represented in fp16 exactly
1208 if (precision == glu::PRECISION_MEDIUMP)
1210 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1211 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1215 bool compare (const void* const* inputs, const void* const* outputs)
1217 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1218 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1219 const int scalarSize = glu::getDataTypeScalarSize(type);
1221 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1223 // Require exact result.
1224 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1226 const float in0 = ((const float*)inputs[0])[compNdx];
1227 const float out0 = ((const float*)outputs[0])[compNdx];
1228 const bool isNeg = tcu::Float32(in0).sign() < 0;
1229 const float ref = isNeg ? (-float(int(-in0))) : float(int(in0));
1231 // \note: trunc() function definition is a bit broad on negative zeros. Ignore result sign if zero.
1232 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1236 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1243 const int mantissaBits = getMinMantissaBits(precision);
1244 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1245 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1247 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1249 const float in0 = ((const float*)inputs[0])[compNdx];
1250 const float out0 = ((const float*)outputs[0])[compNdx];
1251 const int minRes = int(in0-eps);
1252 const int maxRes = int(in0+eps);
1255 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1257 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1259 if (ulpDiff <= maxUlpDiff)
1268 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1278 class RoundCase : public CommonFunctionCase
1281 RoundCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1282 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "round", shaderType)
1284 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1285 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1286 m_spec.source = "out0 = round(in0);";
1289 void getInputValues (int numValues, void* const* values) const
1291 const Vec2 ranges[] =
1293 Vec2(-2.0f, 2.0f), // lowp
1294 Vec2(-1e3f, 1e3f), // mediump
1295 Vec2(-1e7f, 1e7f) // highp
1298 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
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 int numSpecialCases = 0;
1305 if (precision != glu::PRECISION_LOWP)
1307 DE_ASSERT(numValues >= 10);
1308 for (int ndx = 0; ndx < 10; ndx++)
1310 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1311 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1312 numSpecialCases += 1;
1317 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1319 // If precision is mediump, make sure values can be represented in fp16 exactly
1320 if (precision == glu::PRECISION_MEDIUMP)
1322 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1323 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1327 bool compare (const void* const* inputs, const void* const* outputs)
1329 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1330 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1331 const bool hasZeroSign = supportsSignedZero(precision);
1332 const int scalarSize = glu::getDataTypeScalarSize(type);
1334 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1336 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1338 const float in0 = ((const float*)inputs[0])[compNdx];
1339 const float out0 = ((const float*)outputs[0])[compNdx];
1341 if (deFloatFrac(in0) == 0.5f)
1343 // Allow both ceil(in) and floor(in)
1344 const float ref0 = deFloatFloor(in0);
1345 const float ref1 = deFloatCeil(in0);
1346 const deUint32 ulpDiff0 = hasZeroSign ? getUlpDiff(out0, ref0) : getUlpDiffIgnoreZeroSign(out0, ref0);
1347 const deUint32 ulpDiff1 = hasZeroSign ? getUlpDiff(out0, ref1) : getUlpDiffIgnoreZeroSign(out0, ref1);
1349 if (ulpDiff0 > 0 && ulpDiff1 > 0)
1351 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " or " << HexFloat(ref1) << ", got ULP diff " << tcu::toHex(de::min(ulpDiff0, ulpDiff1));
1357 // Require exact result
1358 const float ref = roundEven(in0);
1359 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1363 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1371 const int mantissaBits = getMinMantissaBits(precision);
1372 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1373 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1375 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1377 const float in0 = ((const float*)inputs[0])[compNdx];
1378 const float out0 = ((const float*)outputs[0])[compNdx];
1379 const int minRes = int(roundEven(in0-eps));
1380 const int maxRes = int(roundEven(in0+eps));
1383 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1385 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1387 if (ulpDiff <= maxUlpDiff)
1396 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1406 class CeilCase : public CommonFunctionCase
1409 CeilCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1410 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ceil", shaderType)
1412 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1413 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1414 m_spec.source = "out0 = ceil(in0);";
1417 void getInputValues (int numValues, void* const* values) const
1419 const Vec2 ranges[] =
1421 Vec2(-2.0f, 2.0f), // lowp
1422 Vec2(-1e3f, 1e3f), // mediump
1423 Vec2(-1e7f, 1e7f) // highp
1426 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1427 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1428 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1429 const int scalarSize = glu::getDataTypeScalarSize(type);
1432 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1434 // If precision is mediump, make sure values can be represented in fp16 exactly
1435 if (precision == glu::PRECISION_MEDIUMP)
1437 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1438 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1442 bool compare (const void* const* inputs, const void* const* outputs)
1444 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1445 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1446 const bool hasZeroSign = supportsSignedZero(precision);
1447 const int scalarSize = glu::getDataTypeScalarSize(type);
1449 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1451 // Require exact result.
1452 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1454 const float in0 = ((const float*)inputs[0])[compNdx];
1455 const float out0 = ((const float*)outputs[0])[compNdx];
1456 const float ref = deFloatCeil(in0);
1458 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1462 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1469 const int mantissaBits = getMinMantissaBits(precision);
1470 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1471 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1473 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1475 const float in0 = ((const float*)inputs[0])[compNdx];
1476 const float out0 = ((const float*)outputs[0])[compNdx];
1477 const int minRes = int(deFloatCeil(in0-eps));
1478 const int maxRes = int(deFloatCeil(in0+eps));
1481 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1483 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1485 if (ulpDiff <= maxUlpDiff)
1492 if (!anyOk && de::inRange(0, minRes, maxRes))
1494 // Allow -0 as well.
1495 const int ulpDiff = de::abs((int)tcu::Float32(out0).bits() - (int)0x80000000u);
1496 anyOk = ((deUint32)ulpDiff <= maxUlpDiff);
1501 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1511 class FractCase : public CommonFunctionCase
1514 FractCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1515 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fract", shaderType)
1517 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1518 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1519 m_spec.source = "out0 = fract(in0);";
1522 void getInputValues (int numValues, void* const* values) const
1524 const Vec2 ranges[] =
1526 Vec2(-2.0f, 2.0f), // lowp
1527 Vec2(-1e3f, 1e3f), // mediump
1528 Vec2(-1e7f, 1e7f) // highp
1531 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1532 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1533 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1534 const int scalarSize = glu::getDataTypeScalarSize(type);
1535 int numSpecialCases = 0;
1538 if (precision != glu::PRECISION_LOWP)
1540 DE_ASSERT(numValues >= 10);
1541 for (int ndx = 0; ndx < 10; ndx++)
1543 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1544 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1545 numSpecialCases += 1;
1550 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1552 // If precision is mediump, make sure values can be represented in fp16 exactly
1553 if (precision == glu::PRECISION_MEDIUMP)
1555 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1556 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1560 bool compare (const void* const* inputs, const void* const* outputs)
1562 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1563 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1564 const bool hasZeroSign = supportsSignedZero(precision);
1565 const int scalarSize = glu::getDataTypeScalarSize(type);
1567 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1569 // Require exact result.
1570 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1572 const float in0 = ((const float*)inputs[0])[compNdx];
1573 const float out0 = ((const float*)outputs[0])[compNdx];
1574 const float ref = deFloatFrac(in0);
1576 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1580 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1587 const int mantissaBits = getMinMantissaBits(precision);
1588 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1590 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1592 const float in0 = ((const float*)inputs[0])[compNdx];
1593 const float out0 = ((const float*)outputs[0])[compNdx];
1595 if (int(deFloatFloor(in0-eps)) == int(deFloatFloor(in0+eps)))
1597 const float ref = deFloatFrac(in0);
1598 const int bitsLost = numBitsLostInOp(in0, ref);
1599 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(0, mantissaBits-bitsLost)); // ULP diff for rounded integer value.
1600 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1602 if (ulpDiff > maxUlpDiff)
1604 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << " with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1612 m_failMsg << "Expected [" << compNdx << "] < 1.0";
1623 ShaderCommonFunctionTests::ShaderCommonFunctionTests (Context& context)
1624 : TestCaseGroup(context, "common", "Common function tests")
1628 ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void)
1632 template<class TestClass>
1633 static void addFunctionCases (TestCaseGroup* parent, const char* functionName, bool floatTypes, bool intTypes, bool uintTypes)
1635 tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), functionName, functionName);
1636 parent->addChild(group);
1638 const glu::DataType scalarTypes[] =
1645 for (int scalarTypeNdx = 0; scalarTypeNdx < DE_LENGTH_OF_ARRAY(scalarTypes); scalarTypeNdx++)
1647 const glu::DataType scalarType = scalarTypes[scalarTypeNdx];
1649 if ((!floatTypes && scalarType == glu::TYPE_FLOAT) ||
1650 (!intTypes && scalarType == glu::TYPE_INT) ||
1651 (!uintTypes && scalarType == glu::TYPE_UINT))
1654 for (int vecSize = 1; vecSize <= 4; vecSize++)
1656 for (int prec = glu::PRECISION_LOWP; prec <= glu::PRECISION_HIGHP; prec++)
1658 for (int shaderType = glu::SHADERTYPE_VERTEX; shaderType <= glu::SHADERTYPE_FRAGMENT; shaderType++)
1659 group->addChild(new TestClass(parent->getContext(), glu::DataType(scalarType + vecSize - 1), glu::Precision(prec), glu::ShaderType(shaderType)));
1665 void ShaderCommonFunctionTests::init (void)
1667 // Float? Int? Uint?
1668 addFunctionCases<AbsCase> (this, "abs", true, true, false);
1669 addFunctionCases<SignCase> (this, "sign", true, true, false);
1670 addFunctionCases<FloorCase> (this, "floor", true, false, false);
1671 addFunctionCases<TruncCase> (this, "trunc", true, false, false);
1672 addFunctionCases<RoundCase> (this, "round", true, false, false);
1673 addFunctionCases<RoundEvenCase> (this, "roundeven", true, false, false);
1674 addFunctionCases<CeilCase> (this, "ceil", true, false, false);
1675 addFunctionCases<FractCase> (this, "fract", true, false, false);
1677 addFunctionCases<ModfCase> (this, "modf", true, false, false);
1684 addFunctionCases<IsnanCase> (this, "isnan", true, false, false);
1685 addFunctionCases<IsinfCase> (this, "isinf", true, false, false);
1686 addFunctionCases<FloatBitsToIntCase> (this, "floatbitstoint", true, false, false);
1687 addFunctionCases<FloatBitsToUintCase> (this, "floatbitstouint", true, false, false);
1689 // (u)intBitsToFloat()
1691 tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "intbitstofloat", "intBitsToFloat() Tests");
1692 tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat", "uintBitsToFloat() Tests");
1695 addChild(uintGroup);
1697 for (int vecSize = 1; vecSize < 4; vecSize++)
1699 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
1700 const glu::DataType uintType = vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT;
1702 for (int shaderType = glu::SHADERTYPE_VERTEX; shaderType <= glu::SHADERTYPE_FRAGMENT; shaderType++)
1704 intGroup->addChild(new BitsToFloatCase(m_context, intType, glu::ShaderType(shaderType)));
1705 uintGroup->addChild(new BitsToFloatCase(m_context, uintType, glu::ShaderType(shaderType)));