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 std::string getCommonFuncCaseName (glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
379 return string(glu::getDataTypeName(baseType)) + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType);
382 class AbsCase : public CommonFunctionCase
385 AbsCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
386 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "abs", shaderType)
388 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
389 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
390 m_spec.source = "out0 = abs(in0);";
393 void getInputValues (int numValues, void* const* values) const
395 const Vec2 floatRanges[] =
397 Vec2(-2.0f, 2.0f), // lowp
398 Vec2(-1e3f, 1e3f), // mediump
399 Vec2(-1e7f, 1e7f) // highp
401 const IVec2 intRanges[] =
403 IVec2(-(1<<7)+1, (1<<7)-1),
404 IVec2(-(1<<15)+1, (1<<15)-1),
405 IVec2(0x80000001, 0x7fffffff)
408 de::Random rnd (deStringHash(getName()) ^ 0x235facu);
409 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
410 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
411 const int scalarSize = glu::getDataTypeScalarSize(type);
413 if (glu::isDataTypeFloatOrVec(type))
414 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), values[0], numValues*scalarSize);
416 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize);
419 bool compare (const void* const* inputs, const void* const* outputs)
421 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
422 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
423 const int scalarSize = glu::getDataTypeScalarSize(type);
425 if (glu::isDataTypeFloatOrVec(type))
427 const int mantissaBits = getMinMantissaBits(precision);
428 const deUint32 maxUlpDiff = (1u<<(23-mantissaBits))-1u;
430 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
432 const float in0 = ((const float*)inputs[0])[compNdx];
433 const float out0 = ((const float*)outputs[0])[compNdx];
434 const float ref0 = de::abs(in0);
435 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
437 if (ulpDiff0 > maxUlpDiff)
439 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
446 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
448 const int in0 = ((const int*)inputs[0])[compNdx];
449 const int out0 = ((const int*)outputs[0])[compNdx];
450 const int ref0 = de::abs(in0);
454 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
464 class SignCase : public CommonFunctionCase
467 SignCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
468 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "sign", shaderType)
470 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
471 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
472 m_spec.source = "out0 = sign(in0);";
475 void getInputValues (int numValues, void* const* values) const
477 const Vec2 floatRanges[] =
479 Vec2(-2.0f, 2.0f), // lowp
480 Vec2(-1e4f, 1e4f), // mediump - note: may end up as inf
481 Vec2(-1e8f, 1e8f) // highp - note: may end up as inf
483 const IVec2 intRanges[] =
485 IVec2(-(1<<7), (1<<7)-1),
486 IVec2(-(1<<15), (1<<15)-1),
487 IVec2(0x80000000, 0x7fffffff)
490 de::Random rnd (deStringHash(getName()) ^ 0x324u);
491 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
492 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
493 const int scalarSize = glu::getDataTypeScalarSize(type);
495 if (glu::isDataTypeFloatOrVec(type))
498 std::fill((float*)values[0], (float*)values[0] + scalarSize, +1.0f);
499 std::fill((float*)values[0] + scalarSize*1, (float*)values[0] + scalarSize*2, -1.0f);
500 std::fill((float*)values[0] + scalarSize*2, (float*)values[0] + scalarSize*3, 0.0f);
501 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), (float*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
505 std::fill((int*)values[0], (int*)values[0] + scalarSize, +1);
506 std::fill((int*)values[0] + scalarSize*1, (int*)values[0] + scalarSize*2, -1);
507 std::fill((int*)values[0] + scalarSize*2, (int*)values[0] + scalarSize*3, 0);
508 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
512 bool compare (const void* const* inputs, const void* const* outputs)
514 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
515 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
516 const int scalarSize = glu::getDataTypeScalarSize(type);
518 if (glu::isDataTypeFloatOrVec(type))
520 // Both highp and mediump should be able to represent -1, 0, and +1 exactly
521 const deUint32 maxUlpDiff = precision == glu::PRECISION_LOWP ? getMaxUlpDiffFromBits(getMinMantissaBits(precision)) : 0;
523 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
525 const float in0 = ((const float*)inputs[0])[compNdx];
526 const float out0 = ((const float*)outputs[0])[compNdx];
527 const float ref0 = in0 < 0.0f ? -1.0f :
528 in0 > 0.0f ? +1.0f : 0.0f;
529 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
531 if (ulpDiff0 > maxUlpDiff)
533 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
540 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
542 const int in0 = ((const int*)inputs[0])[compNdx];
543 const int out0 = ((const int*)outputs[0])[compNdx];
544 const int ref0 = in0 < 0 ? -1 :
549 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
559 static float roundEven (float v)
561 const float q = deFloatFrac(v);
562 const int truncated = int(v-q);
563 const int rounded = (q > 0.5f) ? (truncated + 1) : // Rounded up
564 (q == 0.5f && (truncated % 2 != 0)) ? (truncated + 1) : // Round to nearest even at 0.5
565 truncated; // Rounded down
567 return float(rounded);
570 class RoundEvenCase : public CommonFunctionCase
573 RoundEvenCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
574 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "roundEven", shaderType)
576 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
577 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
578 m_spec.source = "out0 = roundEven(in0);";
581 void getInputValues (int numValues, void* const* values) const
583 const Vec2 ranges[] =
585 Vec2(-2.0f, 2.0f), // lowp
586 Vec2(-1e3f, 1e3f), // mediump
587 Vec2(-1e7f, 1e7f) // highp
590 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
591 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
592 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
593 const int scalarSize = glu::getDataTypeScalarSize(type);
594 int numSpecialCases = 0;
597 if (precision != glu::PRECISION_LOWP)
599 DE_ASSERT(numValues >= 20);
600 for (int ndx = 0; ndx < 20; ndx++)
602 const float v = de::clamp(float(ndx) - 10.5f, ranges[precision].x(), ranges[precision].y());
603 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
604 numSpecialCases += 1;
609 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
611 // If precision is mediump, make sure values can be represented in fp16 exactly
612 if (precision == glu::PRECISION_MEDIUMP)
614 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
615 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
619 bool compare (const void* const* inputs, const void* const* outputs)
621 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
622 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
623 const bool hasSignedZero = supportsSignedZero(precision);
624 const int scalarSize = glu::getDataTypeScalarSize(type);
626 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
628 // Require exact rounding result.
629 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
631 const float in0 = ((const float*)inputs[0])[compNdx];
632 const float out0 = ((const float*)outputs[0])[compNdx];
633 const float ref = roundEven(in0);
635 const deUint32 ulpDiff = hasSignedZero ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
639 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
646 const int mantissaBits = getMinMantissaBits(precision);
647 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
648 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
650 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
652 const float in0 = ((const float*)inputs[0])[compNdx];
653 const float out0 = ((const float*)outputs[0])[compNdx];
654 const int minRes = int(roundEven(in0-eps));
655 const int maxRes = int(roundEven(in0+eps));
658 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
660 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
662 if (ulpDiff <= maxUlpDiff)
671 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
681 class ModfCase : public CommonFunctionCase
684 ModfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
685 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "modf", shaderType)
687 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
688 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
689 m_spec.outputs.push_back(Symbol("out1", glu::VarType(baseType, precision)));
690 m_spec.source = "out0 = modf(in0, out1);";
693 void getInputValues (int numValues, void* const* values) const
695 const Vec2 ranges[] =
697 Vec2(-2.0f, 2.0f), // lowp
698 Vec2(-1e3f, 1e3f), // mediump
699 Vec2(-1e7f, 1e7f) // highp
702 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
703 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
704 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
705 const int scalarSize = glu::getDataTypeScalarSize(type);
707 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
710 bool compare (const void* const* inputs, const void* const* outputs)
712 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
713 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
714 const bool hasZeroSign = supportsSignedZero(precision);
715 const int scalarSize = glu::getDataTypeScalarSize(type);
717 const int mantissaBits = getMinMantissaBits(precision);
719 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
721 const float in0 = ((const float*)inputs[0])[compNdx];
722 const float out0 = ((const float*)outputs[0])[compNdx];
723 const float out1 = ((const float*)outputs[1])[compNdx];
725 const float refOut1 = float(int(in0));
726 const float refOut0 = in0 - refOut1;
728 const int bitsLost = precision != glu::PRECISION_HIGHP ? numBitsLostInOp(in0, refOut0) : 0;
729 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(mantissaBits - bitsLost, 0));
731 const float resSum = out0 + out1;
733 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(resSum, in0) : getUlpDiffIgnoreZeroSign(resSum, in0);
735 if (ulpDiff > maxUlpDiff)
737 m_failMsg << "Expected [" << compNdx << "] = (" << HexFloat(refOut0) << ") + (" << HexFloat(refOut1) << ") = " << HexFloat(in0) << " with ULP threshold "
738 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
747 class IsnanCase : public CommonFunctionCase
750 IsnanCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
751 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isnan", shaderType)
753 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
755 const int vecSize = glu::getDataTypeScalarSize(baseType);
756 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
758 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
759 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
760 m_spec.source = "out0 = isnan(in0);";
763 void getInputValues (int numValues, void* const* values) const
765 de::Random rnd (deStringHash(getName()) ^ 0xc2a39fu);
766 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
767 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
768 const int scalarSize = glu::getDataTypeScalarSize(type);
769 const int mantissaBits = getMinMantissaBits(precision);
770 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
772 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
774 const bool isNan = rnd.getFloat() > 0.3f;
775 const bool isInf = !isNan && rnd.getFloat() > 0.4f;
776 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
777 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
778 const deUint32 sign = rnd.getUint32() & 0x1u;
779 const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
781 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
783 ((deUint32*)values[0])[valNdx] = value;
787 bool compare (const void* const* inputs, const void* const* outputs)
789 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
790 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
791 const int scalarSize = glu::getDataTypeScalarSize(type);
793 if (precision == glu::PRECISION_HIGHP)
795 // Only highp is required to support inf/nan
796 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
798 const float in0 = ((const float*)inputs[0])[compNdx];
799 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
800 const deUint32 ref = tcu::Float32(in0).isNaN() ? 1u : 0u;
804 m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
811 // Value can be either 0 or 1
812 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
814 const int out0 = ((const int*)outputs[0])[compNdx];
816 if (out0 != 0 && out0 != 1)
818 m_failMsg << "Expected [" << compNdx << "] = 0 / 1";
828 class IsinfCase : public CommonFunctionCase
831 IsinfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
832 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isinf", shaderType)
834 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
836 const int vecSize = glu::getDataTypeScalarSize(baseType);
837 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
839 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
840 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
841 m_spec.source = "out0 = isinf(in0);";
844 void getInputValues (int numValues, void* const* values) const
846 de::Random rnd (deStringHash(getName()) ^ 0xc2a39fu);
847 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
848 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
849 const int scalarSize = glu::getDataTypeScalarSize(type);
850 const int mantissaBits = getMinMantissaBits(precision);
851 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
853 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
855 const bool isInf = rnd.getFloat() > 0.3f;
856 const bool isNan = !isInf && rnd.getFloat() > 0.4f;
857 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
858 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
859 const deUint32 sign = rnd.getUint32() & 0x1u;
860 const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
862 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
864 ((deUint32*)values[0])[valNdx] = value;
868 bool compare (const void* const* inputs, const void* const* outputs)
870 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
871 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
872 const int scalarSize = glu::getDataTypeScalarSize(type);
874 if (precision == glu::PRECISION_HIGHP)
876 // Only highp is required to support inf/nan
877 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
879 const float in0 = ((const float*)inputs[0])[compNdx];
880 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
881 const deUint32 ref = tcu::Float32(in0).isInf() ? 1u : 0u;
885 m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
892 // Value can be either 0 or 1
893 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
895 const int out0 = ((const int*)outputs[0])[compNdx];
897 if (out0 != 0 && out0 != 1)
899 m_failMsg << "Expected [" << compNdx << "] = 0 / 1";
909 class FloatBitsToUintIntCase : public CommonFunctionCase
912 FloatBitsToUintIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType, bool outIsSigned)
913 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), outIsSigned ? "floatBitsToInt" : "floatBitsToUint", shaderType)
915 const int vecSize = glu::getDataTypeScalarSize(baseType);
916 const glu::DataType intType = outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT)
917 : (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT);
919 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
920 m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP)));
921 m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);";
924 void getInputValues (int numValues, void* const* values) const
926 const Vec2 ranges[] =
928 Vec2(-2.0f, 2.0f), // lowp
929 Vec2(-1e3f, 1e3f), // mediump
930 Vec2(-1e7f, 1e7f) // highp
933 de::Random rnd (deStringHash(getName()) ^ 0x2790au);
934 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
935 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
936 const int scalarSize = glu::getDataTypeScalarSize(type);
938 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
941 bool compare (const void* const* inputs, const void* const* outputs)
943 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
944 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
945 const int scalarSize = glu::getDataTypeScalarSize(type);
947 const int mantissaBits = getMinMantissaBits(precision);
948 const int maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
950 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
952 const float in0 = ((const float*)inputs[0])[compNdx];
953 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
954 const deUint32 refOut0 = tcu::Float32(in0).bits();
955 const int ulpDiff = de::abs((int)out0 - (int)refOut0);
957 if (ulpDiff > maxUlpDiff)
959 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold "
960 << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
969 class FloatBitsToIntCase : public FloatBitsToUintIntCase
972 FloatBitsToIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
973 : FloatBitsToUintIntCase(context, baseType, precision, shaderType, true)
978 class FloatBitsToUintCase : public FloatBitsToUintIntCase
981 FloatBitsToUintCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
982 : FloatBitsToUintIntCase(context, baseType, precision, shaderType, false)
987 class BitsToFloatCase : public CommonFunctionCase
990 BitsToFloatCase (Context& context, glu::DataType baseType, glu::ShaderType shaderType)
991 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP, shaderType).c_str(), glu::isDataTypeIntOrIVec(baseType) ? "intBitsToFloat" : "uintBitsToFloat", shaderType)
993 const bool inIsSigned = glu::isDataTypeIntOrIVec(baseType);
994 const int vecSize = glu::getDataTypeScalarSize(baseType);
995 const glu::DataType floatType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
997 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
998 m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP)));
999 m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);";
1002 void getInputValues (int numValues, void* const* values) const
1004 de::Random rnd (deStringHash(getName()) ^ 0xbbb225u);
1005 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1006 const int scalarSize = glu::getDataTypeScalarSize(type);
1007 const Vec2 range (-1e8f, +1e8f);
1009 // \note Filled as floats.
1010 fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize);
1013 bool compare (const void* const* inputs, const void* const* outputs)
1015 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1016 const int scalarSize = glu::getDataTypeScalarSize(type);
1017 const deUint32 maxUlpDiff = 0;
1019 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1021 const float in0 = ((const float*)inputs[0])[compNdx];
1022 const float out0 = ((const float*)outputs[0])[compNdx];
1023 const deUint32 ulpDiff = getUlpDiff(in0, out0);
1025 if (ulpDiff > maxUlpDiff)
1027 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits()) << " with ULP threshold "
1028 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
1037 class FloorCase : public CommonFunctionCase
1040 FloorCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1041 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "floor", shaderType)
1043 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1044 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1045 m_spec.source = "out0 = floor(in0);";
1048 void getInputValues (int numValues, void* const* values) const
1050 const Vec2 ranges[] =
1052 Vec2(-2.0f, 2.0f), // lowp
1053 Vec2(-1e3f, 1e3f), // mediump
1054 Vec2(-1e7f, 1e7f) // highp
1057 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1058 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1059 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1060 const int scalarSize = glu::getDataTypeScalarSize(type);
1062 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1064 // If precision is mediump, make sure values can be represented in fp16 exactly
1065 if (precision == glu::PRECISION_MEDIUMP)
1067 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1068 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1072 bool compare (const void* const* inputs, const void* const* outputs)
1074 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1075 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1076 const int scalarSize = glu::getDataTypeScalarSize(type);
1078 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1080 // Require exact result.
1081 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1083 const float in0 = ((const float*)inputs[0])[compNdx];
1084 const float out0 = ((const float*)outputs[0])[compNdx];
1085 const float ref = deFloatFloor(in0);
1087 const deUint32 ulpDiff = getUlpDiff(out0, ref);
1091 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1098 const int mantissaBits = getMinMantissaBits(precision);
1099 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1100 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1102 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1104 const float in0 = ((const float*)inputs[0])[compNdx];
1105 const float out0 = ((const float*)outputs[0])[compNdx];
1106 const int minRes = int(deFloatFloor(in0-eps));
1107 const int maxRes = int(deFloatFloor(in0+eps));
1110 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1112 const deUint32 ulpDiff = getUlpDiff(out0, float(roundedVal));
1114 if (ulpDiff <= maxUlpDiff)
1123 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1133 class TruncCase : public CommonFunctionCase
1136 TruncCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1137 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "trunc", shaderType)
1139 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1140 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1141 m_spec.source = "out0 = trunc(in0);";
1144 void getInputValues (int numValues, void* const* values) const
1146 const Vec2 ranges[] =
1148 Vec2(-2.0f, 2.0f), // lowp
1149 Vec2(-1e3f, 1e3f), // mediump
1150 Vec2(-1e7f, 1e7f) // highp
1153 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1154 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1155 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1156 const int scalarSize = glu::getDataTypeScalarSize(type);
1157 const float specialCases[] = { 0.0f, -0.0f, -0.9f, 0.9f, 1.0f, -1.0f };
1158 const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
1161 for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
1163 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1164 ((float*)values[0])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx];
1168 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + scalarSize*numSpecialCases, (numValues-numSpecialCases)*scalarSize);
1170 // If precision is mediump, make sure values can be represented in fp16 exactly
1171 if (precision == glu::PRECISION_MEDIUMP)
1173 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1174 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1178 bool compare (const void* const* inputs, const void* const* outputs)
1180 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1181 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1182 const int scalarSize = glu::getDataTypeScalarSize(type);
1184 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1186 // Require exact result.
1187 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1189 const float in0 = ((const float*)inputs[0])[compNdx];
1190 const float out0 = ((const float*)outputs[0])[compNdx];
1191 const bool isNeg = tcu::Float32(in0).sign() < 0;
1192 const float ref = isNeg ? (-float(int(-in0))) : float(int(in0));
1194 // \note: trunc() function definition is a bit broad on negative zeros. Ignore result sign if zero.
1195 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1199 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1206 const int mantissaBits = getMinMantissaBits(precision);
1207 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1208 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1210 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1212 const float in0 = ((const float*)inputs[0])[compNdx];
1213 const float out0 = ((const float*)outputs[0])[compNdx];
1214 const int minRes = int(in0-eps);
1215 const int maxRes = int(in0+eps);
1218 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1220 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1222 if (ulpDiff <= maxUlpDiff)
1231 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1241 class RoundCase : public CommonFunctionCase
1244 RoundCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1245 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "round", shaderType)
1247 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1248 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1249 m_spec.source = "out0 = round(in0);";
1252 void getInputValues (int numValues, void* const* values) const
1254 const Vec2 ranges[] =
1256 Vec2(-2.0f, 2.0f), // lowp
1257 Vec2(-1e3f, 1e3f), // mediump
1258 Vec2(-1e7f, 1e7f) // highp
1261 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1262 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1263 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1264 const int scalarSize = glu::getDataTypeScalarSize(type);
1265 int numSpecialCases = 0;
1268 if (precision != glu::PRECISION_LOWP)
1270 DE_ASSERT(numValues >= 10);
1271 for (int ndx = 0; ndx < 10; ndx++)
1273 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1274 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1275 numSpecialCases += 1;
1280 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1282 // If precision is mediump, make sure values can be represented in fp16 exactly
1283 if (precision == glu::PRECISION_MEDIUMP)
1285 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1286 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1290 bool compare (const void* const* inputs, const void* const* outputs)
1292 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1293 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1294 const bool hasZeroSign = supportsSignedZero(precision);
1295 const int scalarSize = glu::getDataTypeScalarSize(type);
1297 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1299 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1301 const float in0 = ((const float*)inputs[0])[compNdx];
1302 const float out0 = ((const float*)outputs[0])[compNdx];
1304 if (deFloatFrac(in0) == 0.5f)
1306 // Allow both ceil(in) and floor(in)
1307 const float ref0 = deFloatFloor(in0);
1308 const float ref1 = deFloatCeil(in0);
1309 const deUint32 ulpDiff0 = hasZeroSign ? getUlpDiff(out0, ref0) : getUlpDiffIgnoreZeroSign(out0, ref0);
1310 const deUint32 ulpDiff1 = hasZeroSign ? getUlpDiff(out0, ref1) : getUlpDiffIgnoreZeroSign(out0, ref1);
1312 if (ulpDiff0 > 0 && ulpDiff1 > 0)
1314 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " or " << HexFloat(ref1) << ", got ULP diff " << tcu::toHex(de::min(ulpDiff0, ulpDiff1));
1320 // Require exact result
1321 const float ref = roundEven(in0);
1322 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1326 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1334 const int mantissaBits = getMinMantissaBits(precision);
1335 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1336 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1338 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1340 const float in0 = ((const float*)inputs[0])[compNdx];
1341 const float out0 = ((const float*)outputs[0])[compNdx];
1342 const int minRes = int(roundEven(in0-eps));
1343 const int maxRes = int(roundEven(in0+eps));
1346 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1348 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1350 if (ulpDiff <= maxUlpDiff)
1359 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1369 class CeilCase : public CommonFunctionCase
1372 CeilCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1373 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ceil", shaderType)
1375 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1376 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1377 m_spec.source = "out0 = ceil(in0);";
1380 void getInputValues (int numValues, void* const* values) const
1382 const Vec2 ranges[] =
1384 Vec2(-2.0f, 2.0f), // lowp
1385 Vec2(-1e3f, 1e3f), // mediump
1386 Vec2(-1e7f, 1e7f) // highp
1389 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1390 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1391 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1392 const int scalarSize = glu::getDataTypeScalarSize(type);
1395 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1397 // If precision is mediump, make sure values can be represented in fp16 exactly
1398 if (precision == glu::PRECISION_MEDIUMP)
1400 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1401 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1405 bool compare (const void* const* inputs, const void* const* outputs)
1407 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1408 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1409 const bool hasZeroSign = supportsSignedZero(precision);
1410 const int scalarSize = glu::getDataTypeScalarSize(type);
1412 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1414 // Require exact result.
1415 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1417 const float in0 = ((const float*)inputs[0])[compNdx];
1418 const float out0 = ((const float*)outputs[0])[compNdx];
1419 const float ref = deFloatCeil(in0);
1421 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1425 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1432 const int mantissaBits = getMinMantissaBits(precision);
1433 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1434 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1436 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1438 const float in0 = ((const float*)inputs[0])[compNdx];
1439 const float out0 = ((const float*)outputs[0])[compNdx];
1440 const int minRes = int(deFloatCeil(in0-eps));
1441 const int maxRes = int(deFloatCeil(in0+eps));
1444 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1446 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1448 if (ulpDiff <= maxUlpDiff)
1455 if (!anyOk && de::inRange(0, minRes, maxRes))
1457 // Allow -0 as well.
1458 const int ulpDiff = de::abs((int)tcu::Float32(out0).bits() - (int)0x80000000u);
1459 anyOk = ((deUint32)ulpDiff <= maxUlpDiff);
1464 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1474 class FractCase : public CommonFunctionCase
1477 FractCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1478 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fract", shaderType)
1480 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1481 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1482 m_spec.source = "out0 = fract(in0);";
1485 void getInputValues (int numValues, void* const* values) const
1487 const Vec2 ranges[] =
1489 Vec2(-2.0f, 2.0f), // lowp
1490 Vec2(-1e3f, 1e3f), // mediump
1491 Vec2(-1e7f, 1e7f) // highp
1494 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1495 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1496 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1497 const int scalarSize = glu::getDataTypeScalarSize(type);
1498 int numSpecialCases = 0;
1501 if (precision != glu::PRECISION_LOWP)
1503 DE_ASSERT(numValues >= 10);
1504 for (int ndx = 0; ndx < 10; ndx++)
1506 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1507 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1508 numSpecialCases += 1;
1513 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1515 // If precision is mediump, make sure values can be represented in fp16 exactly
1516 if (precision == glu::PRECISION_MEDIUMP)
1518 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1519 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1523 bool compare (const void* const* inputs, const void* const* outputs)
1525 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1526 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1527 const bool hasZeroSign = supportsSignedZero(precision);
1528 const int scalarSize = glu::getDataTypeScalarSize(type);
1530 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1532 // Require exact result.
1533 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1535 const float in0 = ((const float*)inputs[0])[compNdx];
1536 const float out0 = ((const float*)outputs[0])[compNdx];
1537 const float ref = deFloatFrac(in0);
1539 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1543 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1550 const int mantissaBits = getMinMantissaBits(precision);
1551 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1553 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1555 const float in0 = ((const float*)inputs[0])[compNdx];
1556 const float out0 = ((const float*)outputs[0])[compNdx];
1558 if (int(deFloatFloor(in0-eps)) == int(deFloatFloor(in0+eps)))
1560 const float ref = deFloatFrac(in0);
1561 const int bitsLost = numBitsLostInOp(in0, ref);
1562 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(0, mantissaBits-bitsLost)); // ULP diff for rounded integer value.
1563 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1565 if (ulpDiff > maxUlpDiff)
1567 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << " with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1575 m_failMsg << "Expected [" << compNdx << "] < 1.0";
1586 ShaderCommonFunctionTests::ShaderCommonFunctionTests (Context& context)
1587 : TestCaseGroup(context, "common", "Common function tests")
1591 ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void)
1595 template<class TestClass>
1596 static void addFunctionCases (TestCaseGroup* parent, const char* functionName, bool floatTypes, bool intTypes, bool uintTypes)
1598 tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), functionName, functionName);
1599 parent->addChild(group);
1601 const glu::DataType scalarTypes[] =
1608 for (int scalarTypeNdx = 0; scalarTypeNdx < DE_LENGTH_OF_ARRAY(scalarTypes); scalarTypeNdx++)
1610 const glu::DataType scalarType = scalarTypes[scalarTypeNdx];
1612 if ((!floatTypes && scalarType == glu::TYPE_FLOAT) ||
1613 (!intTypes && scalarType == glu::TYPE_INT) ||
1614 (!uintTypes && scalarType == glu::TYPE_UINT))
1617 for (int vecSize = 1; vecSize <= 4; vecSize++)
1619 for (int prec = glu::PRECISION_LOWP; prec <= glu::PRECISION_HIGHP; prec++)
1621 for (int shaderType = glu::SHADERTYPE_VERTEX; shaderType <= glu::SHADERTYPE_FRAGMENT; shaderType++)
1622 group->addChild(new TestClass(parent->getContext(), glu::DataType(scalarType + vecSize - 1), glu::Precision(prec), glu::ShaderType(shaderType)));
1628 void ShaderCommonFunctionTests::init (void)
1630 // Float? Int? Uint?
1631 addFunctionCases<AbsCase> (this, "abs", true, true, false);
1632 addFunctionCases<SignCase> (this, "sign", true, true, false);
1633 addFunctionCases<FloorCase> (this, "floor", true, false, false);
1634 addFunctionCases<TruncCase> (this, "trunc", true, false, false);
1635 addFunctionCases<RoundCase> (this, "round", true, false, false);
1636 addFunctionCases<RoundEvenCase> (this, "roundeven", true, false, false);
1637 addFunctionCases<CeilCase> (this, "ceil", true, false, false);
1638 addFunctionCases<FractCase> (this, "fract", true, false, false);
1640 addFunctionCases<ModfCase> (this, "modf", true, false, false);
1647 addFunctionCases<IsnanCase> (this, "isnan", true, false, false);
1648 addFunctionCases<IsinfCase> (this, "isinf", true, false, false);
1649 addFunctionCases<FloatBitsToIntCase> (this, "floatbitstoint", true, false, false);
1650 addFunctionCases<FloatBitsToUintCase> (this, "floatbitstouint", true, false, false);
1652 // (u)intBitsToFloat()
1654 tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "intbitstofloat", "intBitsToFloat() Tests");
1655 tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat", "uintBitsToFloat() Tests");
1658 addChild(uintGroup);
1660 for (int vecSize = 1; vecSize < 4; vecSize++)
1662 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
1663 const glu::DataType uintType = vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT;
1665 for (int shaderType = glu::SHADERTYPE_VERTEX; shaderType <= glu::SHADERTYPE_FRAGMENT; shaderType++)
1667 intGroup->addChild(new BitsToFloatCase(m_context, intType, glu::ShaderType(shaderType)));
1668 uintGroup->addChild(new BitsToFloatCase(m_context, uintType, glu::ShaderType(shaderType)));