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 "tcuVectorUtil.hpp"
29 #include "tcuFloat.hpp"
30 #include "deRandom.hpp"
44 using namespace gls::ShaderExecUtil;
55 template<typename T, int Size>
59 VecArrayAccess (const void* ptr) : m_array((tcu::Vector<T, Size>*)ptr) {}
60 ~VecArrayAccess (void) {}
62 const tcu::Vector<T, Size>& operator[] (size_t offset) const { return m_array[offset]; }
63 tcu::Vector<T, Size>& operator[] (size_t offset) { return m_array[offset]; }
66 tcu::Vector<T, Size>* m_array;
69 template<typename T, int Size>
70 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)
72 VecArrayAccess<T, Size> access(dst);
73 for (int ndx = 0; ndx < numValues; ndx++)
74 access[offset + ndx] = tcu::randomVector<T, Size>(rnd, minValue, maxValue);
78 static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0)
80 T* typedPtr = (T*)dst;
81 for (int ndx = 0; ndx < numValues; ndx++)
82 typedPtr[offset + ndx] = de::randomScalar<T>(rnd, minValue, maxValue);
85 inline int numBitsLostInOp (float input, float output)
87 const int inExp = tcu::Float32(input).exponent();
88 const int outExp = tcu::Float32(output).exponent();
90 return de::max(0, inExp-outExp); // Lost due to mantissa shift.
93 inline deUint32 getUlpDiff (float a, float b)
95 const deUint32 aBits = tcu::Float32(a).bits();
96 const deUint32 bBits = tcu::Float32(b).bits();
97 return aBits > bBits ? aBits - bBits : bBits - aBits;
100 inline deUint32 getUlpDiffIgnoreZeroSign (float a, float b)
102 if (tcu::Float32(a).isZero())
103 return getUlpDiff(tcu::Float32::construct(tcu::Float32(b).sign(), 0, 0).asFloat(), b);
104 else if (tcu::Float32(b).isZero())
105 return getUlpDiff(a, tcu::Float32::construct(tcu::Float32(a).sign(), 0, 0).asFloat());
107 return getUlpDiff(a, b);
110 inline bool supportsSignedZero (glu::Precision precision)
112 // \note GLSL ES 3.0 doesn't really require support for -0, but we require it for highp
113 // as it is very widely supported.
114 return precision == glu::PRECISION_HIGHP;
117 inline float getEpsFromMaxUlpDiff (float value, deUint32 ulpDiff)
119 const int exp = tcu::Float32(value).exponent();
120 return tcu::Float32::construct(+1, exp, (1u<<23) | ulpDiff).asFloat() - tcu::Float32::construct(+1, exp, 1u<<23).asFloat();
123 inline deUint32 getMaxUlpDiffFromBits (int numAccurateBits)
125 const int numGarbageBits = 23-numAccurateBits;
126 const deUint32 mask = (1u<<numGarbageBits)-1u;
131 inline float getEpsFromBits (float value, int numAccurateBits)
133 return getEpsFromMaxUlpDiff(value, getMaxUlpDiffFromBits(numAccurateBits));
136 static int getMinMantissaBits (glu::Precision precision)
144 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(bits) == glu::PRECISION_LAST);
145 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(bits)));
146 return bits[precision];
149 // CommonFunctionCase
151 class CommonFunctionCase : public TestCase
154 CommonFunctionCase (Context& context, const char* name, const char* description, glu::ShaderType shaderType);
155 ~CommonFunctionCase (void);
159 IterateResult iterate (void);
162 CommonFunctionCase (const CommonFunctionCase& other);
163 CommonFunctionCase& operator= (const CommonFunctionCase& other);
165 virtual void getInputValues (int numValues, void* const* values) const = 0;
166 virtual bool compare (const void* const* inputs, const void* const* outputs) = 0;
168 glu::ShaderType m_shaderType;
172 std::ostringstream m_failMsg; //!< Comparison failure help message.
175 ShaderExecutor* m_executor;
178 CommonFunctionCase::CommonFunctionCase (Context& context, const char* name, const char* description, glu::ShaderType shaderType)
179 : TestCase (context, name, description)
180 , m_shaderType (shaderType)
182 , m_executor (DE_NULL)
184 m_spec.version = glu::GLSL_VERSION_300_ES;
187 CommonFunctionCase::~CommonFunctionCase (void)
189 CommonFunctionCase::deinit();
192 void CommonFunctionCase::init (void)
194 DE_ASSERT(!m_executor);
196 m_executor = createExecutor(m_context.getRenderContext(), m_shaderType, m_spec);
197 m_testCtx.getLog() << m_executor;
199 if (!m_executor->isOk())
200 throw tcu::TestError("Compile failed");
203 void CommonFunctionCase::deinit (void)
206 m_executor = DE_NULL;
209 static vector<int> getScalarSizes (const vector<Symbol>& symbols)
211 vector<int> sizes(symbols.size());
212 for (int ndx = 0; ndx < (int)symbols.size(); ++ndx)
213 sizes[ndx] = symbols[ndx].varType.getScalarSize();
217 static int computeTotalScalarSize (const vector<Symbol>& symbols)
220 for (vector<Symbol>::const_iterator sym = symbols.begin(); sym != symbols.end(); ++sym)
221 totalSize += sym->varType.getScalarSize();
225 static vector<void*> getInputOutputPointers (const vector<Symbol>& symbols, vector<deUint32>& data, const int numValues)
227 vector<void*> pointers (symbols.size());
228 int curScalarOffset = 0;
230 for (int varNdx = 0; varNdx < (int)symbols.size(); ++varNdx)
232 const Symbol& var = symbols[varNdx];
233 const int scalarSize = var.varType.getScalarSize();
235 // Uses planar layout as input/output specs do not support strides.
236 pointers[varNdx] = &data[curScalarOffset];
237 curScalarOffset += scalarSize*numValues;
240 DE_ASSERT(curScalarOffset == (int)data.size());
245 // \todo [2013-08-08 pyry] Make generic utility and move to glu?
250 HexFloat (const float value_) : value(value_) {}
253 std::ostream& operator<< (std::ostream& str, const HexFloat& v)
255 return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
260 const deUint32 value;
261 HexBool (const deUint32 value_) : value(value_) {}
264 std::ostream& operator<< (std::ostream& str, const HexBool& v)
266 return str << (v.value ? "true" : "false") << " / " << tcu::toHex(v.value);
271 const glu::VarType& type;
274 VarValue (const glu::VarType& type_, const void* value_) : type(type_), value(value_) {}
277 std::ostream& operator<< (std::ostream& str, const VarValue& varValue)
279 DE_ASSERT(varValue.type.isBasicType());
281 const glu::DataType basicType = varValue.type.getBasicType();
282 const glu::DataType scalarType = glu::getDataTypeScalarType(basicType);
283 const int numComponents = glu::getDataTypeScalarSize(basicType);
285 if (numComponents > 1)
286 str << glu::getDataTypeName(basicType) << "(";
288 for (int compNdx = 0; compNdx < numComponents; compNdx++)
295 case glu::TYPE_FLOAT: str << HexFloat(((const float*)varValue.value)[compNdx]); break;
296 case glu::TYPE_INT: str << ((const deInt32*)varValue.value)[compNdx]; break;
297 case glu::TYPE_UINT: str << tcu::toHex(((const deUint32*)varValue.value)[compNdx]); break;
298 case glu::TYPE_BOOL: str << HexBool(((const deUint32*)varValue.value)[compNdx]); break;
305 if (numComponents > 1)
311 CommonFunctionCase::IterateResult CommonFunctionCase::iterate (void)
313 const int numInputScalars = computeTotalScalarSize(m_spec.inputs);
314 const int numOutputScalars = computeTotalScalarSize(m_spec.outputs);
315 vector<deUint32> inputData (numInputScalars * m_numValues);
316 vector<deUint32> outputData (numOutputScalars * m_numValues);
317 const vector<void*> inputPointers = getInputOutputPointers(m_spec.inputs, inputData, m_numValues);
318 const vector<void*> outputPointers = getInputOutputPointers(m_spec.outputs, outputData, m_numValues);
320 // Initialize input data.
321 getInputValues(m_numValues, &inputPointers[0]);
324 m_executor->useProgram();
325 m_executor->execute(m_numValues, &inputPointers[0], &outputPointers[0]);
329 const vector<int> inScalarSizes = getScalarSizes(m_spec.inputs);
330 const vector<int> outScalarSizes = getScalarSizes(m_spec.outputs);
331 vector<void*> curInputPtr (inputPointers.size());
332 vector<void*> curOutputPtr (outputPointers.size());
335 for (int valNdx = 0; valNdx < m_numValues; valNdx++)
337 // Set up pointers for comparison.
338 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx)
339 curInputPtr[inNdx] = (deUint32*)inputPointers[inNdx] + inScalarSizes[inNdx]*valNdx;
341 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx)
342 curOutputPtr[outNdx] = (deUint32*)outputPointers[outNdx] + outScalarSizes[outNdx]*valNdx;
344 if (!compare(&curInputPtr[0], &curOutputPtr[0]))
346 // \todo [2013-08-08 pyry] We probably want to log reference value as well?
348 m_testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n " << m_failMsg.str() << TestLog::EndMessage;
350 m_testCtx.getLog() << TestLog::Message << " inputs:" << TestLog::EndMessage;
351 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++)
352 m_testCtx.getLog() << TestLog::Message << " " << m_spec.inputs[inNdx].name << " = "
353 << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx])
354 << TestLog::EndMessage;
356 m_testCtx.getLog() << TestLog::Message << " outputs:" << TestLog::EndMessage;
357 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++)
358 m_testCtx.getLog() << TestLog::Message << " " << m_spec.outputs[outNdx].name << " = "
359 << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx])
360 << TestLog::EndMessage;
368 m_testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage;
370 m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
371 numFailed == 0 ? "Pass" : "Result comparison failed");
377 static const char* getPrecisionPostfix (glu::Precision precision)
379 static const char* s_postfix[] =
385 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_postfix) == glu::PRECISION_LAST);
386 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
387 return s_postfix[precision];
390 static const char* getShaderTypePostfix (glu::ShaderType shaderType)
392 static const char* s_postfix[] =
397 DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
398 return s_postfix[shaderType];
401 static std::string getCommonFuncCaseName (glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
403 return string(glu::getDataTypeName(baseType)) + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType);
406 class AbsCase : public CommonFunctionCase
409 AbsCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
410 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "abs", shaderType)
412 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
413 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
414 m_spec.source = "out0 = abs(in0);";
417 void getInputValues (int numValues, void* const* values) const
419 const Vec2 floatRanges[] =
421 Vec2(-2.0f, 2.0f), // lowp
422 Vec2(-1e3f, 1e3f), // mediump
423 Vec2(-1e7f, 1e7f) // highp
425 const IVec2 intRanges[] =
427 IVec2(-(1<<7)+1, (1<<7)-1),
428 IVec2(-(1<<15)+1, (1<<15)-1),
429 IVec2(0x80000001, 0x7fffffff)
432 de::Random rnd (deStringHash(getName()) ^ 0x235facu);
433 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
434 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
435 const int scalarSize = glu::getDataTypeScalarSize(type);
437 if (glu::isDataTypeFloatOrVec(type))
438 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), values[0], numValues*scalarSize);
440 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize);
443 bool compare (const void* const* inputs, const void* const* outputs)
445 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
446 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
447 const int scalarSize = glu::getDataTypeScalarSize(type);
449 if (glu::isDataTypeFloatOrVec(type))
451 const int mantissaBits = getMinMantissaBits(precision);
452 const deUint32 maxUlpDiff = (1u<<(23-mantissaBits))-1u;
454 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
456 const float in0 = ((const float*)inputs[0])[compNdx];
457 const float out0 = ((const float*)outputs[0])[compNdx];
458 const float ref0 = de::abs(in0);
459 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
461 if (ulpDiff0 > maxUlpDiff)
463 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
470 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
472 const int in0 = ((const int*)inputs[0])[compNdx];
473 const int out0 = ((const int*)outputs[0])[compNdx];
474 const int ref0 = de::abs(in0);
478 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
488 class SignCase : public CommonFunctionCase
491 SignCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
492 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "sign", shaderType)
494 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
495 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
496 m_spec.source = "out0 = sign(in0);";
499 void getInputValues (int numValues, void* const* values) const
501 const Vec2 floatRanges[] =
503 Vec2(-2.0f, 2.0f), // lowp
504 Vec2(-1e4f, 1e4f), // mediump - note: may end up as inf
505 Vec2(-1e8f, 1e8f) // highp - note: may end up as inf
507 const IVec2 intRanges[] =
509 IVec2(-(1<<7), (1<<7)-1),
510 IVec2(-(1<<15), (1<<15)-1),
511 IVec2(0x80000000, 0x7fffffff)
514 de::Random rnd (deStringHash(getName()) ^ 0x324u);
515 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
516 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
517 const int scalarSize = glu::getDataTypeScalarSize(type);
519 if (glu::isDataTypeFloatOrVec(type))
522 std::fill((float*)values[0], (float*)values[0] + scalarSize, +1.0f);
523 std::fill((float*)values[0] + scalarSize*1, (float*)values[0] + scalarSize*2, -1.0f);
524 std::fill((float*)values[0] + scalarSize*2, (float*)values[0] + scalarSize*3, 0.0f);
525 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), (float*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
529 std::fill((int*)values[0], (int*)values[0] + scalarSize, +1);
530 std::fill((int*)values[0] + scalarSize*1, (int*)values[0] + scalarSize*2, -1);
531 std::fill((int*)values[0] + scalarSize*2, (int*)values[0] + scalarSize*3, 0);
532 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
536 bool compare (const void* const* inputs, const void* const* outputs)
538 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
539 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
540 const int scalarSize = glu::getDataTypeScalarSize(type);
542 if (glu::isDataTypeFloatOrVec(type))
544 // Both highp and mediump should be able to represent -1, 0, and +1 exactly
545 const deUint32 maxUlpDiff = precision == glu::PRECISION_LOWP ? getMaxUlpDiffFromBits(getMinMantissaBits(precision)) : 0;
547 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
549 const float in0 = ((const float*)inputs[0])[compNdx];
550 const float out0 = ((const float*)outputs[0])[compNdx];
551 const float ref0 = in0 < 0.0f ? -1.0f :
552 in0 > 0.0f ? +1.0f : 0.0f;
553 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
555 if (ulpDiff0 > maxUlpDiff)
557 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
564 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
566 const int in0 = ((const int*)inputs[0])[compNdx];
567 const int out0 = ((const int*)outputs[0])[compNdx];
568 const int ref0 = in0 < 0 ? -1 :
573 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
583 static float roundEven (float v)
585 const float q = deFloatFrac(v);
586 const int truncated = int(v-q);
587 const int rounded = (q > 0.5f) ? (truncated + 1) : // Rounded up
588 (q == 0.5f && (truncated % 2 != 0)) ? (truncated + 1) : // Round to nearest even at 0.5
589 truncated; // Rounded down
591 return float(rounded);
594 class RoundEvenCase : public CommonFunctionCase
597 RoundEvenCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
598 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "roundEven", shaderType)
600 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
601 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
602 m_spec.source = "out0 = roundEven(in0);";
605 void getInputValues (int numValues, void* const* values) const
607 const Vec2 ranges[] =
609 Vec2(-2.0f, 2.0f), // lowp
610 Vec2(-1e3f, 1e3f), // mediump
611 Vec2(-1e7f, 1e7f) // highp
614 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
615 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
616 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
617 const int scalarSize = glu::getDataTypeScalarSize(type);
618 int numSpecialCases = 0;
621 if (precision != glu::PRECISION_LOWP)
623 DE_ASSERT(numValues >= 20);
624 for (int ndx = 0; ndx < 20; ndx++)
626 const float v = de::clamp(float(ndx) - 10.5f, ranges[precision].x(), ranges[precision].y());
627 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
628 numSpecialCases += 1;
633 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
635 // If precision is mediump, make sure values can be represented in fp16 exactly
636 if (precision == glu::PRECISION_MEDIUMP)
638 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
639 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
643 bool compare (const void* const* inputs, const void* const* outputs)
645 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
646 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
647 const bool hasSignedZero = supportsSignedZero(precision);
648 const int scalarSize = glu::getDataTypeScalarSize(type);
650 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
652 // Require exact rounding result.
653 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
655 const float in0 = ((const float*)inputs[0])[compNdx];
656 const float out0 = ((const float*)outputs[0])[compNdx];
657 const float ref = roundEven(in0);
659 const deUint32 ulpDiff = hasSignedZero ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
663 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
670 const int mantissaBits = getMinMantissaBits(precision);
671 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
672 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
674 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
676 const float in0 = ((const float*)inputs[0])[compNdx];
677 const float out0 = ((const float*)outputs[0])[compNdx];
678 const int minRes = int(roundEven(in0-eps));
679 const int maxRes = int(roundEven(in0+eps));
682 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
684 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
686 if (ulpDiff <= maxUlpDiff)
695 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
705 class ModfCase : public CommonFunctionCase
708 ModfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
709 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "modf", shaderType)
711 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
712 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
713 m_spec.outputs.push_back(Symbol("out1", glu::VarType(baseType, precision)));
714 m_spec.source = "out0 = modf(in0, out1);";
717 void getInputValues (int numValues, void* const* values) const
719 const Vec2 ranges[] =
721 Vec2(-2.0f, 2.0f), // lowp
722 Vec2(-1e3f, 1e3f), // mediump
723 Vec2(-1e7f, 1e7f) // highp
726 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
727 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
728 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
729 const int scalarSize = glu::getDataTypeScalarSize(type);
731 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
734 bool compare (const void* const* inputs, const void* const* outputs)
736 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
737 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
738 const bool hasZeroSign = supportsSignedZero(precision);
739 const int scalarSize = glu::getDataTypeScalarSize(type);
741 const int mantissaBits = getMinMantissaBits(precision);
743 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
745 const float in0 = ((const float*)inputs[0])[compNdx];
746 const float out0 = ((const float*)outputs[0])[compNdx];
747 const float out1 = ((const float*)outputs[1])[compNdx];
749 const float refOut1 = float(int(in0));
750 const float refOut0 = in0 - refOut1;
752 const int bitsLost = precision != glu::PRECISION_HIGHP ? numBitsLostInOp(in0, refOut0) : 0;
753 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(mantissaBits - bitsLost, 0));
755 const float resSum = out0 + out1;
757 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(resSum, in0) : getUlpDiffIgnoreZeroSign(resSum, in0);
759 if (ulpDiff > maxUlpDiff)
761 m_failMsg << "Expected [" << compNdx << "] = (" << HexFloat(refOut0) << ") + (" << HexFloat(refOut1) << ") = " << HexFloat(in0) << " with ULP threshold "
762 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
771 class IsnanCase : public CommonFunctionCase
774 IsnanCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
775 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isnan", shaderType)
777 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
779 const int vecSize = glu::getDataTypeScalarSize(baseType);
780 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
782 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
783 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
784 m_spec.source = "out0 = isnan(in0);";
787 void getInputValues (int numValues, void* const* values) const
789 de::Random rnd (deStringHash(getName()) ^ 0xc2a39fu);
790 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
791 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
792 const int scalarSize = glu::getDataTypeScalarSize(type);
793 const int mantissaBits = getMinMantissaBits(precision);
794 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
796 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
798 const bool isNan = rnd.getFloat() > 0.3f;
799 const bool isInf = !isNan && rnd.getFloat() > 0.4f;
800 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
801 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
802 const deUint32 sign = rnd.getUint32() & 0x1u;
803 const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
805 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
807 ((deUint32*)values[0])[valNdx] = value;
811 bool compare (const void* const* inputs, const void* const* outputs)
813 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
814 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
815 const int scalarSize = glu::getDataTypeScalarSize(type);
817 if (precision == glu::PRECISION_HIGHP)
819 // Only highp is required to support inf/nan
820 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
822 const float in0 = ((const float*)inputs[0])[compNdx];
823 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
824 const deUint32 ref = tcu::Float32(in0).isNaN() ? 1u : 0u;
828 m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
835 // Value can be either 0 or 1
836 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
838 const int out0 = ((const int*)outputs[0])[compNdx];
840 if (out0 != 0 && out0 != 1)
842 m_failMsg << "Expected [" << compNdx << "] = 0 / 1";
852 class IsinfCase : public CommonFunctionCase
855 IsinfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
856 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isinf", shaderType)
858 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
860 const int vecSize = glu::getDataTypeScalarSize(baseType);
861 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
863 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
864 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
865 m_spec.source = "out0 = isinf(in0);";
868 void getInputValues (int numValues, void* const* values) const
870 de::Random rnd (deStringHash(getName()) ^ 0xc2a39fu);
871 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
872 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
873 const int scalarSize = glu::getDataTypeScalarSize(type);
874 const int mantissaBits = getMinMantissaBits(precision);
875 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
877 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
879 const bool isInf = rnd.getFloat() > 0.3f;
880 const bool isNan = !isInf && rnd.getFloat() > 0.4f;
881 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
882 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
883 const deUint32 sign = rnd.getUint32() & 0x1u;
884 const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
886 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
888 ((deUint32*)values[0])[valNdx] = value;
892 bool compare (const void* const* inputs, const void* const* outputs)
894 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
895 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
896 const int scalarSize = glu::getDataTypeScalarSize(type);
898 if (precision == glu::PRECISION_HIGHP)
900 // Only highp is required to support inf/nan
901 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
903 const float in0 = ((const float*)inputs[0])[compNdx];
904 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
905 const deUint32 ref = tcu::Float32(in0).isInf() ? 1u : 0u;
909 m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
916 // Value can be either 0 or 1
917 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
919 const int out0 = ((const int*)outputs[0])[compNdx];
921 if (out0 != 0 && out0 != 1)
923 m_failMsg << "Expected [" << compNdx << "] = 0 / 1";
933 class FloatBitsToUintIntCase : public CommonFunctionCase
936 FloatBitsToUintIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType, bool outIsSigned)
937 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), outIsSigned ? "floatBitsToInt" : "floatBitsToUint", shaderType)
939 const int vecSize = glu::getDataTypeScalarSize(baseType);
940 const glu::DataType intType = outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT)
941 : (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT);
943 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
944 m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP)));
945 m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);";
948 void getInputValues (int numValues, void* const* values) const
950 const Vec2 ranges[] =
952 Vec2(-2.0f, 2.0f), // lowp
953 Vec2(-1e3f, 1e3f), // mediump
954 Vec2(-1e7f, 1e7f) // highp
957 de::Random rnd (deStringHash(getName()) ^ 0x2790au);
958 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
959 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
960 const int scalarSize = glu::getDataTypeScalarSize(type);
962 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
965 bool compare (const void* const* inputs, const void* const* outputs)
967 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
968 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
969 const int scalarSize = glu::getDataTypeScalarSize(type);
971 const int mantissaBits = getMinMantissaBits(precision);
972 const int maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
974 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
976 const float in0 = ((const float*)inputs[0])[compNdx];
977 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
978 const deUint32 refOut0 = tcu::Float32(in0).bits();
979 const int ulpDiff = de::abs((int)out0 - (int)refOut0);
981 if (ulpDiff > maxUlpDiff)
983 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold "
984 << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
993 class FloatBitsToIntCase : public FloatBitsToUintIntCase
996 FloatBitsToIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
997 : FloatBitsToUintIntCase(context, baseType, precision, shaderType, true)
1002 class FloatBitsToUintCase : public FloatBitsToUintIntCase
1005 FloatBitsToUintCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1006 : FloatBitsToUintIntCase(context, baseType, precision, shaderType, false)
1011 class BitsToFloatCase : public CommonFunctionCase
1014 BitsToFloatCase (Context& context, glu::DataType baseType, glu::ShaderType shaderType)
1015 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP, shaderType).c_str(), glu::isDataTypeIntOrIVec(baseType) ? "intBitsToFloat" : "uintBitsToFloat", shaderType)
1017 const bool inIsSigned = glu::isDataTypeIntOrIVec(baseType);
1018 const int vecSize = glu::getDataTypeScalarSize(baseType);
1019 const glu::DataType floatType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1021 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1022 m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP)));
1023 m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);";
1026 void getInputValues (int numValues, void* const* values) const
1028 de::Random rnd (deStringHash(getName()) ^ 0xbbb225u);
1029 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1030 const int scalarSize = glu::getDataTypeScalarSize(type);
1031 const Vec2 range (-1e8f, +1e8f);
1033 // \note Filled as floats.
1034 fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize);
1037 bool compare (const void* const* inputs, const void* const* outputs)
1039 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1040 const int scalarSize = glu::getDataTypeScalarSize(type);
1041 const deUint32 maxUlpDiff = 0;
1043 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1045 const float in0 = ((const float*)inputs[0])[compNdx];
1046 const float out0 = ((const float*)outputs[0])[compNdx];
1047 const deUint32 ulpDiff = getUlpDiff(in0, out0);
1049 if (ulpDiff > maxUlpDiff)
1051 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits()) << " with ULP threshold "
1052 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
1061 class FloorCase : public CommonFunctionCase
1064 FloorCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1065 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "floor", shaderType)
1067 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1068 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1069 m_spec.source = "out0 = floor(in0);";
1072 void getInputValues (int numValues, void* const* values) const
1074 const Vec2 ranges[] =
1076 Vec2(-2.0f, 2.0f), // lowp
1077 Vec2(-1e3f, 1e3f), // mediump
1078 Vec2(-1e7f, 1e7f) // highp
1081 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1082 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1083 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1084 const int scalarSize = glu::getDataTypeScalarSize(type);
1086 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1088 // If precision is mediump, make sure values can be represented in fp16 exactly
1089 if (precision == glu::PRECISION_MEDIUMP)
1091 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1092 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1096 bool compare (const void* const* inputs, const void* const* outputs)
1098 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1099 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1100 const int scalarSize = glu::getDataTypeScalarSize(type);
1102 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1104 // Require exact result.
1105 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1107 const float in0 = ((const float*)inputs[0])[compNdx];
1108 const float out0 = ((const float*)outputs[0])[compNdx];
1109 const float ref = deFloatFloor(in0);
1111 const deUint32 ulpDiff = getUlpDiff(out0, ref);
1115 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1122 const int mantissaBits = getMinMantissaBits(precision);
1123 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1124 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1126 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1128 const float in0 = ((const float*)inputs[0])[compNdx];
1129 const float out0 = ((const float*)outputs[0])[compNdx];
1130 const int minRes = int(deFloatFloor(in0-eps));
1131 const int maxRes = int(deFloatFloor(in0+eps));
1134 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1136 const deUint32 ulpDiff = getUlpDiff(out0, float(roundedVal));
1138 if (ulpDiff <= maxUlpDiff)
1147 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1157 class TruncCase : public CommonFunctionCase
1160 TruncCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1161 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "trunc", shaderType)
1163 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1164 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1165 m_spec.source = "out0 = trunc(in0);";
1168 void getInputValues (int numValues, void* const* values) const
1170 const Vec2 ranges[] =
1172 Vec2(-2.0f, 2.0f), // lowp
1173 Vec2(-1e3f, 1e3f), // mediump
1174 Vec2(-1e7f, 1e7f) // highp
1177 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1178 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1179 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1180 const int scalarSize = glu::getDataTypeScalarSize(type);
1181 const float specialCases[] = { 0.0f, -0.0f, -0.9f, 0.9f, 1.0f, -1.0f };
1182 const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
1185 for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
1187 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1188 ((float*)values[0])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx];
1192 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + scalarSize*numSpecialCases, (numValues-numSpecialCases)*scalarSize);
1194 // If precision is mediump, make sure values can be represented in fp16 exactly
1195 if (precision == glu::PRECISION_MEDIUMP)
1197 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1198 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1202 bool compare (const void* const* inputs, const void* const* outputs)
1204 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1205 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1206 const int scalarSize = glu::getDataTypeScalarSize(type);
1208 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1210 // Require exact result.
1211 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1213 const float in0 = ((const float*)inputs[0])[compNdx];
1214 const float out0 = ((const float*)outputs[0])[compNdx];
1215 const bool isNeg = tcu::Float32(in0).sign() < 0;
1216 const float ref = isNeg ? (-float(int(-in0))) : float(int(in0));
1218 // \note: trunc() function definition is a bit broad on negative zeros. Ignore result sign if zero.
1219 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1223 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1230 const int mantissaBits = getMinMantissaBits(precision);
1231 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1232 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1234 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1236 const float in0 = ((const float*)inputs[0])[compNdx];
1237 const float out0 = ((const float*)outputs[0])[compNdx];
1238 const int minRes = int(in0-eps);
1239 const int maxRes = int(in0+eps);
1242 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1244 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1246 if (ulpDiff <= maxUlpDiff)
1255 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1265 class RoundCase : public CommonFunctionCase
1268 RoundCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1269 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "round", shaderType)
1271 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1272 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1273 m_spec.source = "out0 = round(in0);";
1276 void getInputValues (int numValues, void* const* values) const
1278 const Vec2 ranges[] =
1280 Vec2(-2.0f, 2.0f), // lowp
1281 Vec2(-1e3f, 1e3f), // mediump
1282 Vec2(-1e7f, 1e7f) // highp
1285 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1286 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1287 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1288 const int scalarSize = glu::getDataTypeScalarSize(type);
1289 int numSpecialCases = 0;
1292 if (precision != glu::PRECISION_LOWP)
1294 DE_ASSERT(numValues >= 10);
1295 for (int ndx = 0; ndx < 10; ndx++)
1297 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1298 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1299 numSpecialCases += 1;
1304 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1306 // If precision is mediump, make sure values can be represented in fp16 exactly
1307 if (precision == glu::PRECISION_MEDIUMP)
1309 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1310 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1314 bool compare (const void* const* inputs, const void* const* outputs)
1316 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1317 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1318 const bool hasZeroSign = supportsSignedZero(precision);
1319 const int scalarSize = glu::getDataTypeScalarSize(type);
1321 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1323 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1325 const float in0 = ((const float*)inputs[0])[compNdx];
1326 const float out0 = ((const float*)outputs[0])[compNdx];
1328 if (deFloatFrac(in0) == 0.5f)
1330 // Allow both ceil(in) and floor(in)
1331 const float ref0 = deFloatFloor(in0);
1332 const float ref1 = deFloatCeil(in0);
1333 const deUint32 ulpDiff0 = hasZeroSign ? getUlpDiff(out0, ref0) : getUlpDiffIgnoreZeroSign(out0, ref0);
1334 const deUint32 ulpDiff1 = hasZeroSign ? getUlpDiff(out0, ref1) : getUlpDiffIgnoreZeroSign(out0, ref1);
1336 if (ulpDiff0 > 0 && ulpDiff1 > 0)
1338 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " or " << HexFloat(ref1) << ", got ULP diff " << tcu::toHex(de::min(ulpDiff0, ulpDiff1));
1344 // Require exact result
1345 const float ref = roundEven(in0);
1346 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1350 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1358 const int mantissaBits = getMinMantissaBits(precision);
1359 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1360 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1362 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1364 const float in0 = ((const float*)inputs[0])[compNdx];
1365 const float out0 = ((const float*)outputs[0])[compNdx];
1366 const int minRes = int(roundEven(in0-eps));
1367 const int maxRes = int(roundEven(in0+eps));
1370 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1372 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1374 if (ulpDiff <= maxUlpDiff)
1383 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1393 class CeilCase : public CommonFunctionCase
1396 CeilCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1397 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ceil", shaderType)
1399 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1400 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1401 m_spec.source = "out0 = ceil(in0);";
1404 void getInputValues (int numValues, void* const* values) const
1406 const Vec2 ranges[] =
1408 Vec2(-2.0f, 2.0f), // lowp
1409 Vec2(-1e3f, 1e3f), // mediump
1410 Vec2(-1e7f, 1e7f) // highp
1413 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1414 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1415 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1416 const int scalarSize = glu::getDataTypeScalarSize(type);
1419 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1421 // If precision is mediump, make sure values can be represented in fp16 exactly
1422 if (precision == glu::PRECISION_MEDIUMP)
1424 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1425 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1429 bool compare (const void* const* inputs, const void* const* outputs)
1431 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1432 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1433 const bool hasZeroSign = supportsSignedZero(precision);
1434 const int scalarSize = glu::getDataTypeScalarSize(type);
1436 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1438 // Require exact result.
1439 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1441 const float in0 = ((const float*)inputs[0])[compNdx];
1442 const float out0 = ((const float*)outputs[0])[compNdx];
1443 const float ref = deFloatCeil(in0);
1445 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1449 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1456 const int mantissaBits = getMinMantissaBits(precision);
1457 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1458 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1460 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1462 const float in0 = ((const float*)inputs[0])[compNdx];
1463 const float out0 = ((const float*)outputs[0])[compNdx];
1464 const int minRes = int(deFloatCeil(in0-eps));
1465 const int maxRes = int(deFloatCeil(in0+eps));
1468 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1470 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1472 if (ulpDiff <= maxUlpDiff)
1479 if (!anyOk && de::inRange(0, minRes, maxRes))
1481 // Allow -0 as well.
1482 const int ulpDiff = de::abs((int)tcu::Float32(out0).bits() - (int)0x80000000u);
1483 anyOk = ((deUint32)ulpDiff <= maxUlpDiff);
1488 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1498 class FractCase : public CommonFunctionCase
1501 FractCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1502 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fract", shaderType)
1504 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1505 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1506 m_spec.source = "out0 = fract(in0);";
1509 void getInputValues (int numValues, void* const* values) const
1511 const Vec2 ranges[] =
1513 Vec2(-2.0f, 2.0f), // lowp
1514 Vec2(-1e3f, 1e3f), // mediump
1515 Vec2(-1e7f, 1e7f) // highp
1518 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1519 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1520 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1521 const int scalarSize = glu::getDataTypeScalarSize(type);
1522 int numSpecialCases = 0;
1525 if (precision != glu::PRECISION_LOWP)
1527 DE_ASSERT(numValues >= 10);
1528 for (int ndx = 0; ndx < 10; ndx++)
1530 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1531 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1532 numSpecialCases += 1;
1537 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1539 // If precision is mediump, make sure values can be represented in fp16 exactly
1540 if (precision == glu::PRECISION_MEDIUMP)
1542 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1543 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1547 bool compare (const void* const* inputs, const void* const* outputs)
1549 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1550 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1551 const bool hasZeroSign = supportsSignedZero(precision);
1552 const int scalarSize = glu::getDataTypeScalarSize(type);
1554 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1556 // Require exact result.
1557 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1559 const float in0 = ((const float*)inputs[0])[compNdx];
1560 const float out0 = ((const float*)outputs[0])[compNdx];
1561 const float ref = deFloatFrac(in0);
1563 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1567 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1574 const int mantissaBits = getMinMantissaBits(precision);
1575 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1577 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1579 const float in0 = ((const float*)inputs[0])[compNdx];
1580 const float out0 = ((const float*)outputs[0])[compNdx];
1582 if (int(deFloatFloor(in0-eps)) == int(deFloatFloor(in0+eps)))
1584 const float ref = deFloatFrac(in0);
1585 const int bitsLost = numBitsLostInOp(in0, ref);
1586 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(0, mantissaBits-bitsLost)); // ULP diff for rounded integer value.
1587 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1589 if (ulpDiff > maxUlpDiff)
1591 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << " with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1599 m_failMsg << "Expected [" << compNdx << "] < 1.0";
1610 ShaderCommonFunctionTests::ShaderCommonFunctionTests (Context& context)
1611 : TestCaseGroup(context, "common", "Common function tests")
1615 ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void)
1619 template<class TestClass>
1620 static void addFunctionCases (TestCaseGroup* parent, const char* functionName, bool floatTypes, bool intTypes, bool uintTypes)
1622 tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), functionName, functionName);
1623 parent->addChild(group);
1625 const glu::DataType scalarTypes[] =
1632 for (int scalarTypeNdx = 0; scalarTypeNdx < DE_LENGTH_OF_ARRAY(scalarTypes); scalarTypeNdx++)
1634 const glu::DataType scalarType = scalarTypes[scalarTypeNdx];
1636 if ((!floatTypes && scalarType == glu::TYPE_FLOAT) ||
1637 (!intTypes && scalarType == glu::TYPE_INT) ||
1638 (!uintTypes && scalarType == glu::TYPE_UINT))
1641 for (int vecSize = 1; vecSize <= 4; vecSize++)
1643 for (int prec = glu::PRECISION_LOWP; prec <= glu::PRECISION_HIGHP; prec++)
1645 for (int shaderType = glu::SHADERTYPE_VERTEX; shaderType <= glu::SHADERTYPE_FRAGMENT; shaderType++)
1646 group->addChild(new TestClass(parent->getContext(), glu::DataType(scalarType + vecSize - 1), glu::Precision(prec), glu::ShaderType(shaderType)));
1652 void ShaderCommonFunctionTests::init (void)
1654 // Float? Int? Uint?
1655 addFunctionCases<AbsCase> (this, "abs", true, true, false);
1656 addFunctionCases<SignCase> (this, "sign", true, true, false);
1657 addFunctionCases<FloorCase> (this, "floor", true, false, false);
1658 addFunctionCases<TruncCase> (this, "trunc", true, false, false);
1659 addFunctionCases<RoundCase> (this, "round", true, false, false);
1660 addFunctionCases<RoundEvenCase> (this, "roundeven", true, false, false);
1661 addFunctionCases<CeilCase> (this, "ceil", true, false, false);
1662 addFunctionCases<FractCase> (this, "fract", true, false, false);
1664 addFunctionCases<ModfCase> (this, "modf", true, false, false);
1671 addFunctionCases<IsnanCase> (this, "isnan", true, false, false);
1672 addFunctionCases<IsinfCase> (this, "isinf", true, false, false);
1673 addFunctionCases<FloatBitsToIntCase> (this, "floatbitstoint", true, false, false);
1674 addFunctionCases<FloatBitsToUintCase> (this, "floatbitstouint", true, false, false);
1676 // (u)intBitsToFloat()
1678 tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "intbitstofloat", "intBitsToFloat() Tests");
1679 tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat", "uintBitsToFloat() Tests");
1682 addChild(uintGroup);
1684 for (int vecSize = 1; vecSize < 4; vecSize++)
1686 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
1687 const glu::DataType uintType = vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT;
1689 for (int shaderType = glu::SHADERTYPE_VERTEX; shaderType <= glu::SHADERTYPE_FRAGMENT; shaderType++)
1691 intGroup->addChild(new BitsToFloatCase(m_context, intType, glu::ShaderType(shaderType)));
1692 uintGroup->addChild(new BitsToFloatCase(m_context, uintType, glu::ShaderType(shaderType)));