1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
5 * Copyright (c) 2015 The Khronos Group Inc.
6 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
7 * Copyright (c) 2016 The Android Open Source Project
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
23 * \brief Common built-in function tests.
24 *//*--------------------------------------------------------------------*/
26 #include "vktShaderCommonFunctionTests.hpp"
27 #include "vktShaderExecutor.hpp"
28 #include "gluContextInfo.hpp"
29 #include "tcuTestLog.hpp"
30 #include "tcuFormatUtil.hpp"
31 #include "tcuFloat.hpp"
32 #include "tcuInterval.hpp"
33 #include "tcuFloatFormat.hpp"
34 #include "tcuVectorUtil.hpp"
35 #include "deRandom.hpp"
38 #include "deArrayUtil.hpp"
39 #include "deSharedPtr.hpp"
44 namespace shaderexecutor
64 template<typename T, int Size>
68 VecArrayAccess (const void* ptr) : m_array((tcu::Vector<T, Size>*)ptr) {}
69 ~VecArrayAccess (void) {}
71 const tcu::Vector<T, Size>& operator[] (size_t offset) const { return m_array[offset]; }
72 tcu::Vector<T, Size>& operator[] (size_t offset) { return m_array[offset]; }
75 tcu::Vector<T, Size>* m_array;
78 template<typename T, int Size>
79 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)
81 VecArrayAccess<T, Size> access(dst);
82 for (int ndx = 0; ndx < numValues; ndx++)
83 access[offset + ndx] = tcu::randomVector<T, Size>(rnd, minValue, maxValue);
87 static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0)
89 T* typedPtr = (T*)dst;
90 for (int ndx = 0; ndx < numValues; ndx++)
91 typedPtr[offset + ndx] = de::randomScalar<T>(rnd, minValue, maxValue);
94 inline int numBitsLostInOp (float input, float output)
96 const int inExp = tcu::Float32(input).exponent();
97 const int outExp = tcu::Float32(output).exponent();
99 return de::max(0, inExp-outExp); // Lost due to mantissa shift.
102 inline deUint32 getUlpDiff (float a, float b)
104 const deUint32 aBits = tcu::Float32(a).bits();
105 const deUint32 bBits = tcu::Float32(b).bits();
106 return aBits > bBits ? aBits - bBits : bBits - aBits;
109 inline deUint32 getUlpDiffIgnoreZeroSign (float a, float b)
111 if (tcu::Float32(a).isZero())
112 return getUlpDiff(tcu::Float32::construct(tcu::Float32(b).sign(), 0, 0).asFloat(), b);
113 else if (tcu::Float32(b).isZero())
114 return getUlpDiff(a, tcu::Float32::construct(tcu::Float32(a).sign(), 0, 0).asFloat());
116 return getUlpDiff(a, b);
119 inline bool supportsSignedZero (glu::Precision precision)
121 // \note GLSL ES 3.1 doesn't really require support for -0, but we require it for highp
122 // as it is very widely supported.
123 return precision == glu::PRECISION_HIGHP;
126 inline float getEpsFromMaxUlpDiff (float value, deUint32 ulpDiff)
128 const int exp = tcu::Float32(value).exponent();
129 return tcu::Float32::construct(+1, exp, (1u<<23) | ulpDiff).asFloat() - tcu::Float32::construct(+1, exp, 1u<<23).asFloat();
132 inline deUint32 getMaxUlpDiffFromBits (int numAccurateBits)
134 const int numGarbageBits = 23-numAccurateBits;
135 const deUint32 mask = (1u<<numGarbageBits)-1u;
140 inline float getEpsFromBits (float value, int numAccurateBits)
142 return getEpsFromMaxUlpDiff(value, getMaxUlpDiffFromBits(numAccurateBits));
145 static int getMinMantissaBits (glu::Precision precision)
153 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(bits) == glu::PRECISION_LAST);
154 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(bits)));
155 return bits[precision];
158 static int getMaxNormalizedValueExponent (glu::Precision precision)
160 const int exponent[] =
166 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(exponent) == glu::PRECISION_LAST);
167 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(exponent)));
168 return exponent[precision];
171 static int getMinNormalizedValueExponent (glu::Precision precision)
173 const int exponent[] =
179 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(exponent) == glu::PRECISION_LAST);
180 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(exponent)));
181 return exponent[precision];
184 static float makeFloatRepresentable (float f, glu::Precision precision)
186 if (precision == glu::PRECISION_HIGHP)
188 // \note: assuming f is not extended-precision
193 const int numMantissaBits = getMinMantissaBits(precision);
194 const int maxNormalizedValueExponent = getMaxNormalizedValueExponent(precision);
195 const int minNormalizedValueExponent = getMinNormalizedValueExponent(precision);
196 const deUint32 representableMantissaMask = ((deUint32(1) << numMantissaBits) - 1) << (23 - (deUint32)numMantissaBits);
197 const float largestRepresentableValue = tcu::Float32::constructBits(+1, maxNormalizedValueExponent, ((1u << numMantissaBits) - 1u) << (23u - (deUint32)numMantissaBits)).asFloat();
198 const bool zeroNotRepresentable = (precision == glu::PRECISION_LOWP);
200 // if zero is not required to be representable, use smallest positive non-subnormal value
201 const float zeroValue = (zeroNotRepresentable) ? (tcu::Float32::constructBits(+1, minNormalizedValueExponent, 1).asFloat()) : (0.0f);
203 const tcu::Float32 float32Representation (f);
205 if (float32Representation.exponent() < minNormalizedValueExponent)
207 // flush too small values to zero
210 else if (float32Representation.exponent() > maxNormalizedValueExponent)
212 // clamp too large values
213 return (float32Representation.sign() == +1) ? (largestRepresentableValue) : (-largestRepresentableValue);
217 // remove unrepresentable mantissa bits
218 const tcu::Float32 targetRepresentation(tcu::Float32::constructBits(float32Representation.sign(),
219 float32Representation.exponent(),
220 float32Representation.mantissaBits() & representableMantissaMask));
222 return targetRepresentation.asFloat();
227 static vector<int> getScalarSizes (const vector<Symbol>& symbols)
229 vector<int> sizes(symbols.size());
230 for (int ndx = 0; ndx < (int)symbols.size(); ++ndx)
231 sizes[ndx] = symbols[ndx].varType.getScalarSize();
235 static int computeTotalScalarSize (const vector<Symbol>& symbols)
238 for (vector<Symbol>::const_iterator sym = symbols.begin(); sym != symbols.end(); ++sym)
239 totalSize += sym->varType.getScalarSize();
243 static vector<void*> getInputOutputPointers (const vector<Symbol>& symbols, vector<deUint32>& data, const int numValues)
245 vector<void*> pointers (symbols.size());
246 int curScalarOffset = 0;
248 for (int varNdx = 0; varNdx < (int)symbols.size(); ++varNdx)
250 const Symbol& var = symbols[varNdx];
251 const int scalarSize = var.varType.getScalarSize();
253 // Uses planar layout as input/output specs do not support strides.
254 pointers[varNdx] = &data[curScalarOffset];
255 curScalarOffset += scalarSize*numValues;
258 DE_ASSERT(curScalarOffset == (int)data.size());
263 // \todo [2013-08-08 pyry] Make generic utility and move to glu?
268 HexFloat (const float value_) : value(value_) {}
271 std::ostream& operator<< (std::ostream& str, const HexFloat& v)
273 return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
278 const deUint32 value;
279 HexBool (const deUint32 value_) : value(value_) {}
282 std::ostream& operator<< (std::ostream& str, const HexBool& v)
284 return str << (v.value ? "true" : "false") << " / " << tcu::toHex(v.value);
289 const glu::VarType& type;
292 VarValue (const glu::VarType& type_, const void* value_) : type(type_), value(value_) {}
295 std::ostream& operator<< (std::ostream& str, const VarValue& varValue)
297 DE_ASSERT(varValue.type.isBasicType());
299 const glu::DataType basicType = varValue.type.getBasicType();
300 const glu::DataType scalarType = glu::getDataTypeScalarType(basicType);
301 const int numComponents = glu::getDataTypeScalarSize(basicType);
303 if (numComponents > 1)
304 str << glu::getDataTypeName(basicType) << "(";
306 for (int compNdx = 0; compNdx < numComponents; compNdx++)
313 case glu::TYPE_FLOAT: str << HexFloat(((const float*)varValue.value)[compNdx]); break;
314 case glu::TYPE_INT: str << ((const deInt32*)varValue.value)[compNdx]; break;
315 case glu::TYPE_UINT: str << tcu::toHex(((const deUint32*)varValue.value)[compNdx]); break;
316 case glu::TYPE_BOOL: str << HexBool(((const deUint32*)varValue.value)[compNdx]); break;
323 if (numComponents > 1)
329 static const char* getPrecisionPostfix (glu::Precision precision)
331 static const char* s_postfix[] =
337 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_postfix) == glu::PRECISION_LAST);
338 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
339 return s_postfix[precision];
342 static const char* getShaderTypePostfix (glu::ShaderType shaderType)
344 static const char* s_postfix[] =
353 DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
354 return s_postfix[shaderType];
357 static std::string getCommonFuncCaseName (glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
359 return string(glu::getDataTypeName(baseType)) + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType);
362 static inline void frexp (float in, float* significand, int* exponent)
364 const tcu::Float32 fpValue(in);
366 if (!fpValue.isZero())
368 // Construct float that has exactly the mantissa, and exponent of -1.
369 *significand = tcu::Float32::construct(fpValue.sign(), -1, fpValue.mantissa()).asFloat();
370 *exponent = fpValue.exponent()+1;
374 *significand = fpValue.sign() < 0 ? -0.0f : 0.0f;
379 static inline float ldexp (float significand, int exponent)
381 const tcu::Float32 mant(significand);
383 if (exponent == 0 && mant.isZero())
385 return mant.sign() < 0 ? -0.0f : 0.0f;
389 return tcu::Float32::construct(mant.sign(), exponent+mant.exponent(), mant.mantissa()).asFloat();
393 template<class TestClass>
394 static void addFunctionCases (tcu::TestCaseGroup* parent, const char* functionName, bool floatTypes, bool intTypes, bool uintTypes, deUint32 shaderBits)
396 tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), functionName, functionName);
397 parent->addChild(group);
399 const glu::DataType scalarTypes[] =
406 for (int scalarTypeNdx = 0; scalarTypeNdx < DE_LENGTH_OF_ARRAY(scalarTypes); scalarTypeNdx++)
408 const glu::DataType scalarType = scalarTypes[scalarTypeNdx];
410 if ((!floatTypes && scalarType == glu::TYPE_FLOAT) ||
411 (!intTypes && scalarType == glu::TYPE_INT) ||
412 (!uintTypes && scalarType == glu::TYPE_UINT))
415 for (int vecSize = 1; vecSize <= 4; vecSize++)
417 for (int prec = glu::PRECISION_MEDIUMP; prec <= glu::PRECISION_HIGHP; prec++)
419 for (int shaderTypeNdx = 0; shaderTypeNdx < glu::SHADERTYPE_LAST; shaderTypeNdx++)
421 if (shaderBits & (1<<shaderTypeNdx))
422 group->addChild(new TestClass(parent->getTestContext(), glu::DataType(scalarType + vecSize - 1), glu::Precision(prec), glu::ShaderType(shaderTypeNdx)));
429 // CommonFunctionCase
431 class CommonFunctionCase : public TestCase
434 CommonFunctionCase (tcu::TestContext& testCtx, const char* name, const char* description, glu::ShaderType shaderType);
435 ~CommonFunctionCase (void);
436 virtual void initPrograms (vk::SourceCollections& programCollection) const
438 generateSources(m_shaderType, m_spec, programCollection);
441 virtual TestInstance* createInstance (Context& context) const = 0;
444 CommonFunctionCase (const CommonFunctionCase&);
445 CommonFunctionCase& operator= (const CommonFunctionCase&);
447 const glu::ShaderType m_shaderType;
449 const int m_numValues;
452 CommonFunctionCase::CommonFunctionCase (tcu::TestContext& testCtx, const char* name, const char* description, glu::ShaderType shaderType)
453 : TestCase (testCtx, name, description)
454 , m_shaderType (shaderType)
459 CommonFunctionCase::~CommonFunctionCase (void)
463 // CommonFunctionTestInstance
465 class CommonFunctionTestInstance : public TestInstance
468 CommonFunctionTestInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
469 : TestInstance (context)
470 , m_shaderType (shaderType)
472 , m_numValues (numValues)
474 , m_executor (createExecutor(context, shaderType, spec))
477 virtual tcu::TestStatus iterate (void);
480 virtual void getInputValues (int numValues, void* const* values) const = 0;
481 virtual bool compare (const void* const* inputs, const void* const* outputs) = 0;
483 const glu::ShaderType m_shaderType;
484 const ShaderSpec m_spec;
485 const int m_numValues;
487 // \todo [2017-03-07 pyry] Hack used to generate seeds for test cases - get rid of this.
490 std::ostringstream m_failMsg; //!< Comparison failure help message.
492 de::UniquePtr<ShaderExecutor> m_executor;
495 tcu::TestStatus CommonFunctionTestInstance::iterate (void)
497 const int numInputScalars = computeTotalScalarSize(m_spec.inputs);
498 const int numOutputScalars = computeTotalScalarSize(m_spec.outputs);
499 vector<deUint32> inputData (numInputScalars * m_numValues);
500 vector<deUint32> outputData (numOutputScalars * m_numValues);
501 const vector<void*> inputPointers = getInputOutputPointers(m_spec.inputs, inputData, m_numValues);
502 const vector<void*> outputPointers = getInputOutputPointers(m_spec.outputs, outputData, m_numValues);
504 // Initialize input data.
505 getInputValues(m_numValues, &inputPointers[0]);
508 m_executor->execute(m_numValues, &inputPointers[0], &outputPointers[0]);
512 const vector<int> inScalarSizes = getScalarSizes(m_spec.inputs);
513 const vector<int> outScalarSizes = getScalarSizes(m_spec.outputs);
514 vector<void*> curInputPtr (inputPointers.size());
515 vector<void*> curOutputPtr (outputPointers.size());
517 tcu::TestContext& testCtx = m_context.getTestContext();
519 for (int valNdx = 0; valNdx < m_numValues; valNdx++)
521 // Set up pointers for comparison.
522 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx)
523 curInputPtr[inNdx] = (deUint32*)inputPointers[inNdx] + inScalarSizes[inNdx]*valNdx;
525 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx)
526 curOutputPtr[outNdx] = (deUint32*)outputPointers[outNdx] + outScalarSizes[outNdx]*valNdx;
528 if (!compare(&curInputPtr[0], &curOutputPtr[0]))
530 // \todo [2013-08-08 pyry] We probably want to log reference value as well?
532 testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n " << m_failMsg.str() << TestLog::EndMessage;
534 testCtx.getLog() << TestLog::Message << " inputs:" << TestLog::EndMessage;
535 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++)
536 testCtx.getLog() << TestLog::Message << " " << m_spec.inputs[inNdx].name << " = "
537 << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx])
538 << TestLog::EndMessage;
540 testCtx.getLog() << TestLog::Message << " outputs:" << TestLog::EndMessage;
541 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++)
542 testCtx.getLog() << TestLog::Message << " " << m_spec.outputs[outNdx].name << " = "
543 << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx])
544 << TestLog::EndMessage;
552 testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage;
555 return tcu::TestStatus::pass("Pass");
557 return tcu::TestStatus::fail("Result comparison failed");
563 class AbsCaseInstance : public CommonFunctionTestInstance
566 AbsCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
567 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
571 void getInputValues (int numValues, void* const* values) const
573 const Vec2 floatRanges[] =
575 Vec2(-2.0f, 2.0f), // lowp
576 Vec2(-1e3f, 1e3f), // mediump
577 Vec2(-1e7f, 1e7f) // highp
579 const IVec2 intRanges[] =
581 IVec2(-(1<<7)+1, (1<<7)-1),
582 IVec2(-(1<<15)+1, (1<<15)-1),
583 IVec2(0x80000001, 0x7fffffff)
586 de::Random rnd (deStringHash(m_name) ^ 0x235facu);
587 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
588 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
589 const int scalarSize = glu::getDataTypeScalarSize(type);
591 if (glu::isDataTypeFloatOrVec(type))
592 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), values[0], numValues*scalarSize);
594 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize);
597 bool compare (const void* const* inputs, const void* const* outputs)
599 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
600 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
601 const int scalarSize = glu::getDataTypeScalarSize(type);
603 if (glu::isDataTypeFloatOrVec(type))
605 const int mantissaBits = getMinMantissaBits(precision);
606 const deUint32 maxUlpDiff = (1u<<(23-mantissaBits))-1u;
608 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
610 const float in0 = ((const float*)inputs[0])[compNdx];
611 const float out0 = ((const float*)outputs[0])[compNdx];
612 const float ref0 = de::abs(in0);
613 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
615 if (ulpDiff0 > maxUlpDiff)
617 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
624 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
626 const int in0 = ((const int*)inputs[0])[compNdx];
627 const int out0 = ((const int*)outputs[0])[compNdx];
628 const int ref0 = de::abs(in0);
632 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
642 class AbsCase : public CommonFunctionCase
645 AbsCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
646 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "abs", shaderType)
648 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
649 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
650 m_spec.source = "out0 = abs(in0);";
653 TestInstance* createInstance (Context& ctx) const
655 return new AbsCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
659 class SignCaseInstance : public CommonFunctionTestInstance
662 SignCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
663 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
667 void getInputValues (int numValues, void* const* values) const
669 const Vec2 floatRanges[] =
671 Vec2(-2.0f, 2.0f), // lowp
672 Vec2(-1e4f, 1e4f), // mediump - note: may end up as inf
673 Vec2(-1e8f, 1e8f) // highp - note: may end up as inf
675 const IVec2 intRanges[] =
677 IVec2(-(1<<7), (1<<7)-1),
678 IVec2(-(1<<15), (1<<15)-1),
679 IVec2(0x80000000, 0x7fffffff)
682 de::Random rnd (deStringHash(m_name) ^ 0x324u);
683 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
684 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
685 const int scalarSize = glu::getDataTypeScalarSize(type);
687 if (glu::isDataTypeFloatOrVec(type))
690 std::fill((float*)values[0], (float*)values[0] + scalarSize, +1.0f);
691 std::fill((float*)values[0], (float*)values[0] + scalarSize, -1.0f);
692 std::fill((float*)values[0], (float*)values[0] + scalarSize, 0.0f);
693 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), (float*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
697 std::fill((int*)values[0], (int*)values[0] + scalarSize, +1);
698 std::fill((int*)values[0], (int*)values[0] + scalarSize, -1);
699 std::fill((int*)values[0], (int*)values[0] + scalarSize, 0);
700 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
704 bool compare (const void* const* inputs, const void* const* outputs)
706 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
707 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
708 const int scalarSize = glu::getDataTypeScalarSize(type);
710 if (glu::isDataTypeFloatOrVec(type))
712 // Both highp and mediump should be able to represent -1, 0, and +1 exactly
713 const deUint32 maxUlpDiff = precision == glu::PRECISION_LOWP ? getMaxUlpDiffFromBits(getMinMantissaBits(precision)) : 0;
715 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
717 const float in0 = ((const float*)inputs[0])[compNdx];
718 const float out0 = ((const float*)outputs[0])[compNdx];
719 const float ref0 = in0 < 0.0f ? -1.0f :
720 in0 > 0.0f ? +1.0f : 0.0f;
721 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
723 if (ulpDiff0 > maxUlpDiff)
725 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
732 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
734 const int in0 = ((const int*)inputs[0])[compNdx];
735 const int out0 = ((const int*)outputs[0])[compNdx];
736 const int ref0 = in0 < 0 ? -1 :
741 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
751 class SignCase : public CommonFunctionCase
754 SignCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
755 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "sign", shaderType)
757 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
758 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
759 m_spec.source = "out0 = sign(in0);";
762 TestInstance* createInstance (Context& ctx) const
764 return new SignCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
768 static float roundEven (float v)
770 const float q = deFloatFrac(v);
771 const int truncated = int(v-q);
772 const int rounded = (q > 0.5f) ? (truncated + 1) : // Rounded up
773 (q == 0.5f && (truncated % 2 != 0)) ? (truncated + 1) : // Round to nearest even at 0.5
774 truncated; // Rounded down
776 return float(rounded);
779 class RoundEvenCaseInstance : public CommonFunctionTestInstance
782 RoundEvenCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
783 : CommonFunctionTestInstance(context, shaderType, spec, numValues, name)
787 void getInputValues (int numValues, void* const* values) const
789 const Vec2 ranges[] =
791 Vec2(-2.0f, 2.0f), // lowp
792 Vec2(-1e3f, 1e3f), // mediump
793 Vec2(-1e7f, 1e7f) // highp
796 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
797 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
798 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
799 const int scalarSize = glu::getDataTypeScalarSize(type);
800 int numSpecialCases = 0;
803 if (precision != glu::PRECISION_LOWP)
805 DE_ASSERT(numValues >= 20);
806 for (int ndx = 0; ndx < 20; ndx++)
808 const float v = de::clamp(float(ndx) - 10.5f, ranges[precision].x(), ranges[precision].y());
809 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
810 numSpecialCases += 1;
815 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
817 // If precision is mediump, make sure values can be represented in fp16 exactly
818 if (precision == glu::PRECISION_MEDIUMP)
820 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
821 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
825 bool compare (const void* const* inputs, const void* const* outputs)
827 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
828 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
829 const bool hasSignedZero = supportsSignedZero(precision);
830 const int scalarSize = glu::getDataTypeScalarSize(type);
832 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
834 // Require exact rounding result.
835 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
837 const float in0 = ((const float*)inputs[0])[compNdx];
838 const float out0 = ((const float*)outputs[0])[compNdx];
839 const float ref = roundEven(in0);
841 const deUint32 ulpDiff = hasSignedZero ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
845 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
852 const int mantissaBits = getMinMantissaBits(precision);
853 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
854 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
856 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
858 const float in0 = ((const float*)inputs[0])[compNdx];
859 const float out0 = ((const float*)outputs[0])[compNdx];
860 const int minRes = int(roundEven(in0-eps));
861 const int maxRes = int(roundEven(in0+eps));
864 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
866 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
868 if (ulpDiff <= maxUlpDiff)
877 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
887 class RoundEvenCase : public CommonFunctionCase
890 RoundEvenCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
891 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "roundEven", shaderType)
893 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
894 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
895 m_spec.source = "out0 = roundEven(in0);";
898 TestInstance* createInstance (Context& ctx) const
900 return new RoundEvenCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
904 class ModfCaseInstance : public CommonFunctionTestInstance
907 ModfCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
908 : CommonFunctionTestInstance(context, shaderType, spec, numValues, name)
912 void getInputValues (int numValues, void* const* values) const
914 const Vec2 ranges[] =
916 Vec2(-2.0f, 2.0f), // lowp
917 Vec2(-1e3f, 1e3f), // mediump
918 Vec2(-1e7f, 1e7f) // highp
921 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
922 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
923 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
924 const int scalarSize = glu::getDataTypeScalarSize(type);
926 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
929 bool compare (const void* const* inputs, const void* const* outputs)
931 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
932 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
933 const bool hasZeroSign = supportsSignedZero(precision);
934 const int scalarSize = glu::getDataTypeScalarSize(type);
936 const int mantissaBits = getMinMantissaBits(precision);
938 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
940 const float in0 = ((const float*)inputs[0])[compNdx];
941 const float out0 = ((const float*)outputs[0])[compNdx];
942 const float out1 = ((const float*)outputs[1])[compNdx];
944 const float refOut1 = float(int(in0));
945 const float refOut0 = in0 - refOut1;
947 const int bitsLost = precision != glu::PRECISION_HIGHP ? numBitsLostInOp(in0, refOut0) : 0;
948 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(mantissaBits - bitsLost, 0));
950 const float resSum = out0 + out1;
952 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(resSum, in0) : getUlpDiffIgnoreZeroSign(resSum, in0);
954 if (ulpDiff > maxUlpDiff)
956 m_failMsg << "Expected [" << compNdx << "] = (" << HexFloat(refOut0) << ") + (" << HexFloat(refOut1) << ") = " << HexFloat(in0) << " with ULP threshold "
957 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
966 class ModfCase : public CommonFunctionCase
969 ModfCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
970 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "modf", shaderType)
972 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
973 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
974 m_spec.outputs.push_back(Symbol("out1", glu::VarType(baseType, precision)));
975 m_spec.source = "out0 = modf(in0, out1);";
978 TestInstance* createInstance (Context& ctx) const
980 return new ModfCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
984 class IsnanCaseInstance : public CommonFunctionTestInstance
987 IsnanCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
988 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
992 void getInputValues (int numValues, void* const* values) const
994 de::Random rnd (deStringHash(m_name) ^ 0xc2a39fu);
995 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
996 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
997 const int scalarSize = glu::getDataTypeScalarSize(type);
998 const int mantissaBits = getMinMantissaBits(precision);
999 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
1001 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
1003 const bool isNan = rnd.getFloat() > 0.3f;
1004 const bool isInf = !isNan && rnd.getFloat() > 0.4f;
1005 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
1006 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
1007 const deUint32 sign = rnd.getUint32() & 0x1u;
1008 const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
1010 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
1012 ((deUint32*)values[0])[valNdx] = value;
1016 bool compare (const void* const* inputs, const void* const* outputs)
1018 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1019 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1020 const int scalarSize = glu::getDataTypeScalarSize(type);
1022 if (precision == glu::PRECISION_HIGHP)
1024 // Only highp is required to support inf/nan
1025 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1027 const float in0 = ((const float*)inputs[0])[compNdx];
1028 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
1029 const bool ref = tcu::Float32(in0).isNaN();
1033 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
1038 else if (precision == glu::PRECISION_MEDIUMP || precision == glu::PRECISION_LOWP)
1040 // NaN support is optional, check that inputs that are not NaN don't result in true.
1041 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1043 const float in0 = ((const float*)inputs[0])[compNdx];
1044 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
1045 const bool ref = tcu::Float32(in0).isNaN();
1049 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
1059 class IsnanCase : public CommonFunctionCase
1062 IsnanCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1063 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isnan", shaderType)
1065 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
1067 const int vecSize = glu::getDataTypeScalarSize(baseType);
1068 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
1070 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1071 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
1072 m_spec.source = "out0 = isnan(in0);";
1075 TestInstance* createInstance (Context& ctx) const
1077 return new IsnanCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1081 class IsinfCaseInstance : public CommonFunctionTestInstance
1084 IsinfCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1085 : CommonFunctionTestInstance(context, shaderType, spec, numValues, name)
1089 void getInputValues (int numValues, void* const* values) const
1091 de::Random rnd (deStringHash(m_name) ^ 0xc2a39fu);
1092 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1093 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1094 const int scalarSize = glu::getDataTypeScalarSize(type);
1095 const int mantissaBits = getMinMantissaBits(precision);
1096 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
1098 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
1100 const bool isInf = rnd.getFloat() > 0.3f;
1101 const bool isNan = !isInf && rnd.getFloat() > 0.4f;
1102 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
1103 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
1104 const deUint32 sign = rnd.getUint32() & 0x1u;
1105 const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
1107 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
1109 ((deUint32*)values[0])[valNdx] = value;
1113 bool compare (const void* const* inputs, const void* const* outputs)
1115 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1116 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1117 const int scalarSize = glu::getDataTypeScalarSize(type);
1119 if (precision == glu::PRECISION_HIGHP)
1121 // Only highp is required to support inf/nan
1122 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1124 const float in0 = ((const float*)inputs[0])[compNdx];
1125 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
1126 const bool ref = tcu::Float32(in0).isInf();
1130 m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
1135 else if (precision == glu::PRECISION_MEDIUMP)
1137 // Inf support is optional, check that inputs that are not Inf in mediump don't result in true.
1138 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1140 const float in0 = ((const float*)inputs[0])[compNdx];
1141 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
1142 const bool ref = tcu::Float16(in0).isInf();
1146 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
1151 // else: no verification can be performed
1157 class IsinfCase : public CommonFunctionCase
1160 IsinfCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1161 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isinf", shaderType)
1163 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
1165 const int vecSize = glu::getDataTypeScalarSize(baseType);
1166 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
1168 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1169 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
1170 m_spec.source = "out0 = isinf(in0);";
1173 TestInstance* createInstance (Context& ctx) const
1175 return new IsinfCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1179 class FloatBitsToUintIntCaseInstance : public CommonFunctionTestInstance
1182 FloatBitsToUintIntCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1183 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
1187 void getInputValues (int numValues, void* const* values) const
1189 const Vec2 ranges[] =
1191 Vec2(-2.0f, 2.0f), // lowp
1192 Vec2(-1e3f, 1e3f), // mediump
1193 Vec2(-1e7f, 1e7f) // highp
1196 de::Random rnd (deStringHash(m_name) ^ 0x2790au);
1197 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1198 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1199 const int scalarSize = glu::getDataTypeScalarSize(type);
1201 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
1204 bool compare (const void* const* inputs, const void* const* outputs)
1206 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1207 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1208 const int scalarSize = glu::getDataTypeScalarSize(type);
1210 const int mantissaBits = getMinMantissaBits(precision);
1211 const int maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
1213 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1215 const float in0 = ((const float*)inputs[0])[compNdx];
1216 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
1217 const deUint32 refOut0 = tcu::Float32(in0).bits();
1218 const int ulpDiff = de::abs((int)out0 - (int)refOut0);
1220 if (ulpDiff > maxUlpDiff)
1222 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold "
1223 << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1232 class FloatBitsToUintIntCase : public CommonFunctionCase
1235 FloatBitsToUintIntCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType, bool outIsSigned)
1236 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), outIsSigned ? "floatBitsToInt" : "floatBitsToUint", shaderType)
1238 const int vecSize = glu::getDataTypeScalarSize(baseType);
1239 const glu::DataType intType = outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT)
1240 : (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT);
1242 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1243 m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP)));
1244 m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);";
1247 TestInstance* createInstance (Context& ctx) const
1249 return new FloatBitsToUintIntCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1253 class FloatBitsToIntCaseInstance : public FloatBitsToUintIntCaseInstance
1256 FloatBitsToIntCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1257 : FloatBitsToUintIntCaseInstance (context, shaderType, spec, numValues, name)
1262 class FloatBitsToIntCase : public FloatBitsToUintIntCase
1265 FloatBitsToIntCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1266 : FloatBitsToUintIntCase (testCtx, baseType, precision, shaderType, true)
1272 class FloatBitsToUintCaseInstance : public FloatBitsToUintIntCaseInstance
1275 FloatBitsToUintCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1276 : FloatBitsToUintIntCaseInstance (context, shaderType, spec, numValues, name)
1281 class FloatBitsToUintCase : public FloatBitsToUintIntCase
1284 FloatBitsToUintCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1285 : FloatBitsToUintIntCase (testCtx, baseType, precision, shaderType, false)
1290 class BitsToFloatCaseInstance : public CommonFunctionTestInstance
1293 BitsToFloatCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1294 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
1298 void getInputValues (int numValues, void* const* values) const
1300 de::Random rnd (deStringHash(m_name) ^ 0xbbb225u);
1301 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1302 const int scalarSize = glu::getDataTypeScalarSize(type);
1303 const Vec2 range (-1e8f, +1e8f);
1305 // \note Filled as floats.
1306 fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize);
1309 bool compare (const void* const* inputs, const void* const* outputs)
1311 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1312 const int scalarSize = glu::getDataTypeScalarSize(type);
1313 const deUint32 maxUlpDiff = 0;
1315 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1317 const float in0 = ((const float*)inputs[0])[compNdx];
1318 const float out0 = ((const float*)outputs[0])[compNdx];
1319 const deUint32 ulpDiff = getUlpDiff(in0, out0);
1321 if (ulpDiff > maxUlpDiff)
1323 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits()) << " with ULP threshold "
1324 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
1333 class BitsToFloatCase : public CommonFunctionCase
1336 BitsToFloatCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::ShaderType shaderType)
1337 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP, shaderType).c_str(), glu::isDataTypeIntOrIVec(baseType) ? "intBitsToFloat" : "uintBitsToFloat", shaderType)
1339 const bool inIsSigned = glu::isDataTypeIntOrIVec(baseType);
1340 const int vecSize = glu::getDataTypeScalarSize(baseType);
1341 const glu::DataType floatType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1343 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1344 m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP)));
1345 m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);";
1348 TestInstance* createInstance (Context& ctx) const
1350 return new BitsToFloatCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1354 class FloorCaseInstance : public CommonFunctionTestInstance
1357 FloorCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1358 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
1362 void getInputValues (int numValues, void* const* values) const
1364 const Vec2 ranges[] =
1366 Vec2(-2.0f, 2.0f), // lowp
1367 Vec2(-1e3f, 1e3f), // mediump
1368 Vec2(-1e7f, 1e7f) // highp
1371 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
1372 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1373 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1374 const int scalarSize = glu::getDataTypeScalarSize(type);
1376 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1378 // If precision is mediump, make sure values can be represented in fp16 exactly
1379 if (precision == glu::PRECISION_MEDIUMP)
1381 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1382 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1386 bool compare (const void* const* inputs, const void* const* outputs)
1388 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1389 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1390 const int scalarSize = glu::getDataTypeScalarSize(type);
1392 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1394 // Require exact result.
1395 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1397 const float in0 = ((const float*)inputs[0])[compNdx];
1398 const float out0 = ((const float*)outputs[0])[compNdx];
1399 const float ref = deFloatFloor(in0);
1401 const deUint32 ulpDiff = getUlpDiff(out0, ref);
1405 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1412 const int mantissaBits = getMinMantissaBits(precision);
1413 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1414 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1416 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1418 const float in0 = ((const float*)inputs[0])[compNdx];
1419 const float out0 = ((const float*)outputs[0])[compNdx];
1420 const int minRes = int(deFloatFloor(in0-eps));
1421 const int maxRes = int(deFloatFloor(in0+eps));
1424 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1426 const deUint32 ulpDiff = getUlpDiff(out0, float(roundedVal));
1428 if (ulpDiff <= maxUlpDiff)
1437 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1447 class FloorCase : public CommonFunctionCase
1450 FloorCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1451 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "floor", shaderType)
1453 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1454 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1455 m_spec.source = "out0 = floor(in0);";
1458 TestInstance* createInstance (Context& ctx) const
1460 return new FloorCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1464 class TruncCaseInstance : public CommonFunctionTestInstance
1467 TruncCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1468 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
1472 void getInputValues (int numValues, void* const* values) const
1474 const Vec2 ranges[] =
1476 Vec2(-2.0f, 2.0f), // lowp
1477 Vec2(-1e3f, 1e3f), // mediump
1478 Vec2(-1e7f, 1e7f) // highp
1481 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
1482 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1483 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1484 const int scalarSize = glu::getDataTypeScalarSize(type);
1485 const float specialCases[] = { 0.0f, -0.0f, -0.9f, 0.9f, 1.0f, -1.0f };
1486 const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
1489 for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
1491 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1492 ((float*)values[0])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx];
1496 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + scalarSize*numSpecialCases, (numValues-numSpecialCases)*scalarSize);
1498 // If precision is mediump, make sure values can be represented in fp16 exactly
1499 if (precision == glu::PRECISION_MEDIUMP)
1501 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1502 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1506 bool compare (const void* const* inputs, const void* const* outputs)
1508 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1509 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1510 const int scalarSize = glu::getDataTypeScalarSize(type);
1512 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1514 // Require exact result.
1515 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1517 const float in0 = ((const float*)inputs[0])[compNdx];
1518 const float out0 = ((const float*)outputs[0])[compNdx];
1519 const bool isNeg = tcu::Float32(in0).sign() < 0;
1520 const float ref = isNeg ? (-float(int(-in0))) : float(int(in0));
1522 // \note: trunc() function definition is a bit broad on negative zeros. Ignore result sign if zero.
1523 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1527 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1534 const int mantissaBits = getMinMantissaBits(precision);
1535 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1536 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1538 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1540 const float in0 = ((const float*)inputs[0])[compNdx];
1541 const float out0 = ((const float*)outputs[0])[compNdx];
1542 const int minRes = int(in0-eps);
1543 const int maxRes = int(in0+eps);
1546 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1548 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1550 if (ulpDiff <= maxUlpDiff)
1559 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1569 class TruncCase : public CommonFunctionCase
1572 TruncCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1573 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "trunc", shaderType)
1575 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1576 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1577 m_spec.source = "out0 = trunc(in0);";
1580 TestInstance* createInstance (Context& ctx) const
1582 return new TruncCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1586 class RoundCaseInstance : public CommonFunctionTestInstance
1589 RoundCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1590 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
1594 void getInputValues (int numValues, void* const* values) const
1596 const Vec2 ranges[] =
1598 Vec2(-2.0f, 2.0f), // lowp
1599 Vec2(-1e3f, 1e3f), // mediump
1600 Vec2(-1e7f, 1e7f) // highp
1603 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
1604 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1605 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1606 const int scalarSize = glu::getDataTypeScalarSize(type);
1607 int numSpecialCases = 0;
1610 if (precision != glu::PRECISION_LOWP)
1612 DE_ASSERT(numValues >= 10);
1613 for (int ndx = 0; ndx < 10; ndx++)
1615 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1616 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1617 numSpecialCases += 1;
1622 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1624 // If precision is mediump, make sure values can be represented in fp16 exactly
1625 if (precision == glu::PRECISION_MEDIUMP)
1627 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1628 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1632 bool compare (const void* const* inputs, const void* const* outputs)
1634 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1635 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1636 const bool hasZeroSign = supportsSignedZero(precision);
1637 const int scalarSize = glu::getDataTypeScalarSize(type);
1639 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1641 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1643 const float in0 = ((const float*)inputs[0])[compNdx];
1644 const float out0 = ((const float*)outputs[0])[compNdx];
1646 if (deFloatFrac(in0) == 0.5f)
1648 // Allow both ceil(in) and floor(in)
1649 const float ref0 = deFloatFloor(in0);
1650 const float ref1 = deFloatCeil(in0);
1651 const deUint32 ulpDiff0 = hasZeroSign ? getUlpDiff(out0, ref0) : getUlpDiffIgnoreZeroSign(out0, ref0);
1652 const deUint32 ulpDiff1 = hasZeroSign ? getUlpDiff(out0, ref1) : getUlpDiffIgnoreZeroSign(out0, ref1);
1654 if (ulpDiff0 > 0 && ulpDiff1 > 0)
1656 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " or " << HexFloat(ref1) << ", got ULP diff " << tcu::toHex(de::min(ulpDiff0, ulpDiff1));
1662 // Require exact result
1663 const float ref = roundEven(in0);
1664 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1668 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1676 const int mantissaBits = getMinMantissaBits(precision);
1677 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1678 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1680 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1682 const float in0 = ((const float*)inputs[0])[compNdx];
1683 const float out0 = ((const float*)outputs[0])[compNdx];
1684 const int minRes = int(roundEven(in0-eps));
1685 const int maxRes = int(roundEven(in0+eps));
1688 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1690 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1692 if (ulpDiff <= maxUlpDiff)
1701 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1711 class RoundCase : public CommonFunctionCase
1714 RoundCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1715 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "round", shaderType)
1717 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1718 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1719 m_spec.source = "out0 = round(in0);";
1722 TestInstance* createInstance (Context& ctx) const
1724 return new RoundCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1728 class CeilCaseInstance : public CommonFunctionTestInstance
1731 CeilCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1732 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
1736 void getInputValues (int numValues, void* const* values) const
1738 const Vec2 ranges[] =
1740 Vec2(-2.0f, 2.0f), // lowp
1741 Vec2(-1e3f, 1e3f), // mediump
1742 Vec2(-1e7f, 1e7f) // highp
1745 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
1746 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1747 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1748 const int scalarSize = glu::getDataTypeScalarSize(type);
1751 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1753 // If precision is mediump, make sure values can be represented in fp16 exactly
1754 if (precision == glu::PRECISION_MEDIUMP)
1756 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1757 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1761 bool compare (const void* const* inputs, const void* const* outputs)
1763 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1764 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1765 const bool hasZeroSign = supportsSignedZero(precision);
1766 const int scalarSize = glu::getDataTypeScalarSize(type);
1768 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1770 // Require exact result.
1771 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1773 const float in0 = ((const float*)inputs[0])[compNdx];
1774 const float out0 = ((const float*)outputs[0])[compNdx];
1775 const float ref = deFloatCeil(in0);
1777 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1781 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1788 const int mantissaBits = getMinMantissaBits(precision);
1789 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1790 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1792 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1794 const float in0 = ((const float*)inputs[0])[compNdx];
1795 const float out0 = ((const float*)outputs[0])[compNdx];
1796 const int minRes = int(deFloatCeil(in0-eps));
1797 const int maxRes = int(deFloatCeil(in0+eps));
1800 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1802 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1804 if (ulpDiff <= maxUlpDiff)
1811 if (!anyOk && de::inRange(0, minRes, maxRes))
1813 // Allow -0 as well.
1814 const int ulpDiff = de::abs((int)tcu::Float32(out0).bits() - (int)0x80000000u);
1815 anyOk = ((deUint32)ulpDiff <= maxUlpDiff);
1820 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1830 class CeilCase : public CommonFunctionCase
1833 CeilCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1834 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ceil", shaderType)
1836 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1837 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1838 m_spec.source = "out0 = ceil(in0);";
1841 TestInstance* createInstance (Context& ctx) const
1843 return new CeilCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1847 class FractCaseInstance : public CommonFunctionTestInstance
1850 FractCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1851 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
1855 void getInputValues (int numValues, void* const* values) const
1857 const Vec2 ranges[] =
1859 Vec2(-2.0f, 2.0f), // lowp
1860 Vec2(-1e3f, 1e3f), // mediump
1861 Vec2(-1e7f, 1e7f) // highp
1864 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
1865 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1866 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1867 const int scalarSize = glu::getDataTypeScalarSize(type);
1868 int numSpecialCases = 0;
1871 if (precision != glu::PRECISION_LOWP)
1873 DE_ASSERT(numValues >= 10);
1874 for (int ndx = 0; ndx < 10; ndx++)
1876 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1877 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1878 numSpecialCases += 1;
1883 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1885 // If precision is mediump, make sure values can be represented in fp16 exactly
1886 if (precision == glu::PRECISION_MEDIUMP)
1888 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1889 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1893 bool compare (const void* const* inputs, const void* const* outputs)
1895 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1896 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1897 const bool hasZeroSign = supportsSignedZero(precision);
1898 const int scalarSize = glu::getDataTypeScalarSize(type);
1900 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1902 // Require exact result.
1903 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1905 const float in0 = ((const float*)inputs[0])[compNdx];
1906 const float out0 = ((const float*)outputs[0])[compNdx];
1907 const float ref = deFloatFrac(in0);
1909 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1913 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1920 const int mantissaBits = getMinMantissaBits(precision);
1921 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1923 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1925 const float in0 = ((const float*)inputs[0])[compNdx];
1926 const float out0 = ((const float*)outputs[0])[compNdx];
1928 if (int(deFloatFloor(in0-eps)) == int(deFloatFloor(in0+eps)))
1930 const float ref = deFloatFrac(in0);
1931 const int bitsLost = numBitsLostInOp(in0, ref);
1932 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(0, mantissaBits-bitsLost)); // ULP diff for rounded integer value.
1933 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1935 if (ulpDiff > maxUlpDiff)
1937 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << " with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1945 m_failMsg << "Expected [" << compNdx << "] < 1.0";
1956 class FractCase : public CommonFunctionCase
1959 FractCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1960 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fract", shaderType)
1962 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1963 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1964 m_spec.source = "out0 = fract(in0);";
1967 TestInstance* createInstance (Context& ctx) const
1969 return new FractCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
1973 class FrexpCaseInstance : public CommonFunctionTestInstance
1976 FrexpCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
1977 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
1981 void getInputValues (int numValues, void* const* values) const
1983 const Vec2 ranges[] =
1985 Vec2(-2.0f, 2.0f), // lowp
1986 Vec2(-1e3f, 1e3f), // mediump
1987 Vec2(-1e7f, 1e7f) // highp
1990 de::Random rnd (deStringHash(m_name) ^ 0x2790au);
1991 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1992 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1993 const int scalarSize = glu::getDataTypeScalarSize(type);
1996 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1998 ((float*)values[0])[scalarSize*0 + compNdx] = 0.0f;
1999 ((float*)values[0])[scalarSize*1 + compNdx] = -0.0f;
2000 ((float*)values[0])[scalarSize*2 + compNdx] = 0.5f;
2001 ((float*)values[0])[scalarSize*3 + compNdx] = -0.5f;
2002 ((float*)values[0])[scalarSize*4 + compNdx] = 1.0f;
2003 ((float*)values[0])[scalarSize*5 + compNdx] = -1.0f;
2004 ((float*)values[0])[scalarSize*6 + compNdx] = 2.0f;
2005 ((float*)values[0])[scalarSize*7 + compNdx] = -2.0f;
2008 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + 8*scalarSize, (numValues-8)*scalarSize);
2010 // Make sure the values are representable in the target format
2011 for (int caseNdx = 0; caseNdx < numValues; ++caseNdx)
2013 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
2015 float* const valuePtr = &((float*)values[0])[caseNdx * scalarSize + scalarNdx];
2017 *valuePtr = makeFloatRepresentable(*valuePtr, precision);
2022 bool compare (const void* const* inputs, const void* const* outputs)
2024 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2025 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2026 const int scalarSize = glu::getDataTypeScalarSize(type);
2027 const bool transitSupportsSignedZero = (m_shaderType != glu::SHADERTYPE_FRAGMENT); // executor cannot reliably transit negative zero to fragment stage
2028 const bool signedZero = supportsSignedZero(precision) && transitSupportsSignedZero;
2030 const int mantissaBits = getMinMantissaBits(precision);
2031 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
2033 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2035 const float in0 = ((const float*)inputs[0])[compNdx];
2036 const float out0 = ((const float*)outputs[0])[compNdx];
2037 const int out1 = ((const int*)outputs[1])[compNdx];
2042 frexp(in0, &refOut0, &refOut1);
2044 const deUint32 ulpDiff0 = signedZero ? getUlpDiff(out0, refOut0) : getUlpDiffIgnoreZeroSign(out0, refOut0);
2046 if (ulpDiff0 > maxUlpDiff || out1 != refOut1)
2048 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", " << refOut1 << " with ULP threshold "
2049 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff0);
2058 class FrexpCase : public CommonFunctionCase
2061 FrexpCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
2062 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "frexp", shaderType)
2064 const int vecSize = glu::getDataTypeScalarSize(baseType);
2065 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
2067 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
2068 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
2069 m_spec.outputs.push_back(Symbol("out1", glu::VarType(intType, glu::PRECISION_HIGHP)));
2070 m_spec.source = "out0 = frexp(in0, out1);";
2073 TestInstance* createInstance (Context& ctx) const
2075 return new FrexpCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
2079 class LdexpCaseInstance : public CommonFunctionTestInstance
2082 LdexpCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
2083 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
2087 void getInputValues (int numValues, void* const* values) const
2089 const Vec2 ranges[] =
2091 Vec2(-2.0f, 2.0f), // lowp
2092 Vec2(-1e3f, 1e3f), // mediump
2093 Vec2(-1e7f, 1e7f) // highp
2096 de::Random rnd (deStringHash(m_name) ^ 0x2790au);
2097 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2098 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2099 const int scalarSize = glu::getDataTypeScalarSize(type);
2103 const float easySpecialCases[] = { 0.0f, -0.0f, 0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f };
2105 DE_ASSERT(valueNdx + DE_LENGTH_OF_ARRAY(easySpecialCases) <= numValues);
2106 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(easySpecialCases); caseNdx++)
2111 frexp(easySpecialCases[caseNdx], &in0, &in1);
2113 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2115 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
2116 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
2124 // \note lowp and mediump can not necessarily fit the values in hard cases, so we'll use only easy ones.
2125 const int numEasyRandomCases = precision == glu::PRECISION_HIGHP ? 50 : (numValues-valueNdx);
2127 DE_ASSERT(valueNdx + numEasyRandomCases <= numValues);
2128 for (int caseNdx = 0; caseNdx < numEasyRandomCases; caseNdx++)
2130 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2132 const float in = rnd.getFloat(ranges[precision].x(), ranges[precision].y());
2136 frexp(in, &in0, &in1);
2138 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
2139 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
2147 const int numHardRandomCases = numValues-valueNdx;
2148 DE_ASSERT(numHardRandomCases >= 0 && valueNdx + numHardRandomCases <= numValues);
2150 for (int caseNdx = 0; caseNdx < numHardRandomCases; caseNdx++)
2152 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2154 const int fpExp = rnd.getInt(-126, 127);
2155 const int sign = rnd.getBool() ? -1 : +1;
2156 const deUint32 mantissa = (1u<<23) | (rnd.getUint32() & ((1u<<23)-1));
2157 const int in1 = rnd.getInt(de::max(-126, -126-fpExp), de::min(127, 127-fpExp));
2158 const float in0 = tcu::Float32::construct(sign, fpExp, mantissa).asFloat();
2160 DE_ASSERT(de::inRange(in1, -126, 127)); // See Khronos bug 11180
2161 DE_ASSERT(de::inRange(in1+fpExp, -126, 127));
2163 const float out = ldexp(in0, in1);
2165 DE_ASSERT(!tcu::Float32(out).isInf() && !tcu::Float32(out).isDenorm());
2168 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
2169 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
2177 bool compare (const void* const* inputs, const void* const* outputs)
2179 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2180 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2181 const int scalarSize = glu::getDataTypeScalarSize(type);
2183 const int mantissaBits = getMinMantissaBits(precision);
2184 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
2186 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2188 const float in0 = ((const float*)inputs[0])[compNdx];
2189 const int in1 = ((const int*)inputs[1])[compNdx];
2190 const float out0 = ((const float*)outputs[0])[compNdx];
2191 const float refOut0 = ldexp(in0, in1);
2192 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, refOut0);
2194 const int inExp = tcu::Float32(in0).exponent();
2196 if (ulpDiff > maxUlpDiff)
2198 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", (exp = " << inExp << ") with ULP threshold "
2199 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
2208 class LdexpCase : public CommonFunctionCase
2211 LdexpCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
2212 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ldexp", shaderType)
2214 const int vecSize = glu::getDataTypeScalarSize(baseType);
2215 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
2217 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
2218 m_spec.inputs.push_back(Symbol("in1", glu::VarType(intType, glu::PRECISION_HIGHP)));
2219 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
2220 m_spec.source = "out0 = ldexp(in0, in1);";
2223 TestInstance* createInstance (Context& ctx) const
2225 return new LdexpCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
2229 class FmaCaseInstance : public CommonFunctionTestInstance
2232 FmaCaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, int numValues, const char* name)
2233 : CommonFunctionTestInstance (context, shaderType, spec, numValues, name)
2237 void getInputValues (int numValues, void* const* values) const
2239 const Vec2 ranges[] =
2241 Vec2(-2.0f, 2.0f), // lowp
2242 Vec2(-127.f, 127.f), // mediump
2243 Vec2(-1e7f, 1e7f) // highp
2246 de::Random rnd (deStringHash(m_name) ^ 0xac23fu);
2247 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2248 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2249 const int scalarSize = glu::getDataTypeScalarSize(type);
2250 const float specialCases[][3] =
2253 { 0.0f, 0.0f, 0.0f },
2254 { 0.0f, 1.0f, 0.0f },
2255 { 0.0f, 0.0f, -1.0f },
2256 { 1.0f, 1.0f, 0.0f },
2257 { 1.0f, 1.0f, 1.0f },
2258 { -1.0f, 1.0f, 0.0f },
2259 { 1.0f, -1.0f, 0.0f },
2260 { -1.0f, -1.0f, 0.0f },
2261 { -0.0f, 1.0f, 0.0f },
2262 { 1.0f, -0.0f, 0.0f }
2264 const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
2267 for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
2269 for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2271 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
2272 ((float*)values[inputNdx])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx][inputNdx];
2278 const int numScalars = (numValues-numSpecialCases)*scalarSize;
2279 const int offs = scalarSize*numSpecialCases;
2281 for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2282 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[inputNdx] + offs, numScalars);
2285 // Make sure the values are representable in the target format
2286 for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2288 for (int caseNdx = 0; caseNdx < numValues; ++caseNdx)
2290 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
2292 float* const valuePtr = &((float*)values[inputNdx])[caseNdx * scalarSize + scalarNdx];
2294 *valuePtr = makeFloatRepresentable(*valuePtr, precision);
2300 static tcu::Interval fma (glu::Precision precision, float a, float b, float c)
2302 const tcu::FloatFormat formats[] =
2304 // minExp maxExp mantissa exact, subnormals infinities NaN
2305 tcu::FloatFormat(0, 0, 7, false, tcu::YES, tcu::MAYBE, tcu::MAYBE),
2306 tcu::FloatFormat(-13, 13, 9, false, tcu::MAYBE, tcu::MAYBE, tcu::MAYBE),
2307 tcu::FloatFormat(-126, 127, 23, true, tcu::MAYBE, tcu::YES, tcu::MAYBE)
2309 const tcu::FloatFormat& format = de::getSizedArrayElement<glu::PRECISION_LAST>(formats, precision);
2310 const tcu::Interval ia = format.convert(a);
2311 const tcu::Interval ib = format.convert(b);
2312 const tcu::Interval ic = format.convert(c);
2313 tcu::Interval prod0;
2314 tcu::Interval prod1;
2315 tcu::Interval prod2;
2316 tcu::Interval prod3;
2320 TCU_SET_INTERVAL(prod0, tmp, tmp = ia.lo() * ib.lo());
2321 TCU_SET_INTERVAL(prod1, tmp, tmp = ia.lo() * ib.hi());
2322 TCU_SET_INTERVAL(prod2, tmp, tmp = ia.hi() * ib.lo());
2323 TCU_SET_INTERVAL(prod3, tmp, tmp = ia.hi() * ib.hi());
2325 prod = format.convert(format.roundOut(prod0 | prod1 | prod2 | prod3, ia.isFinite() && ib.isFinite()));
2327 TCU_SET_INTERVAL_BOUNDS(res, tmp,
2328 tmp = prod.lo() + ic.lo(),
2329 tmp = prod.hi() + ic.hi());
2331 return format.convert(format.roundOut(res, prod.isFinite() && ic.isFinite()));
2334 bool compare (const void* const* inputs, const void* const* outputs)
2336 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2337 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2338 const int scalarSize = glu::getDataTypeScalarSize(type);
2340 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2342 const float a = ((const float*)inputs[0])[compNdx];
2343 const float b = ((const float*)inputs[1])[compNdx];
2344 const float c = ((const float*)inputs[2])[compNdx];
2345 const float res = ((const float*)outputs[0])[compNdx];
2346 const tcu::Interval ref = fma(precision, a, b, c);
2348 if (!ref.contains(res))
2350 m_failMsg << "Expected [" << compNdx << "] = " << ref;
2359 class FmaCase : public CommonFunctionCase
2362 FmaCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
2363 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fma", shaderType)
2365 m_spec.inputs.push_back(Symbol("a", glu::VarType(baseType, precision)));
2366 m_spec.inputs.push_back(Symbol("b", glu::VarType(baseType, precision)));
2367 m_spec.inputs.push_back(Symbol("c", glu::VarType(baseType, precision)));
2368 m_spec.outputs.push_back(Symbol("res", glu::VarType(baseType, precision)));
2369 m_spec.source = "res = fma(a, b, c);";
2370 m_spec.globalDeclarations = "#extension GL_EXT_gpu_shader5 : require\n";
2373 TestInstance* createInstance (Context& ctx) const
2375 return new FmaCaseInstance(ctx, m_shaderType, m_spec, m_numValues, getName());
2381 ShaderCommonFunctionTests::ShaderCommonFunctionTests (tcu::TestContext& testCtx)
2382 : tcu::TestCaseGroup (testCtx, "common", "Common function tests")
2386 ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void)
2390 void ShaderCommonFunctionTests::init (void)
2394 VS = (1<<glu::SHADERTYPE_VERTEX),
2395 TC = (1<<glu::SHADERTYPE_TESSELLATION_CONTROL),
2396 TE = (1<<glu::SHADERTYPE_TESSELLATION_EVALUATION),
2397 GS = (1<<glu::SHADERTYPE_GEOMETRY),
2398 FS = (1<<glu::SHADERTYPE_FRAGMENT),
2399 CS = (1<<glu::SHADERTYPE_COMPUTE),
2401 ALL_SHADERS = VS|TC|TE|GS|FS|CS,
2402 NEW_SHADERS = TC|TE|GS|CS,
2405 // Float? Int? Uint? Shaders
2406 addFunctionCases<AbsCase> (this, "abs", true, true, false, ALL_SHADERS);
2407 addFunctionCases<SignCase> (this, "sign", true, true, false, ALL_SHADERS);
2408 addFunctionCases<FloorCase> (this, "floor", true, false, false, ALL_SHADERS);
2409 addFunctionCases<TruncCase> (this, "trunc", true, false, false, ALL_SHADERS);
2410 addFunctionCases<RoundCase> (this, "round", true, false, false, ALL_SHADERS);
2411 addFunctionCases<RoundEvenCase> (this, "roundeven", true, false, false, ALL_SHADERS);
2412 addFunctionCases<CeilCase> (this, "ceil", true, false, false, ALL_SHADERS);
2413 addFunctionCases<FractCase> (this, "fract", true, false, false, ALL_SHADERS);
2415 addFunctionCases<ModfCase> (this, "modf", true, false, false, ALL_SHADERS);
2422 addFunctionCases<IsnanCase> (this, "isnan", true, false, false, ALL_SHADERS);
2423 addFunctionCases<IsinfCase> (this, "isinf", true, false, false, ALL_SHADERS);
2424 addFunctionCases<FloatBitsToIntCase> (this, "floatbitstoint", true, false, false, ALL_SHADERS);
2425 addFunctionCases<FloatBitsToUintCase> (this, "floatbitstouint", true, false, false, ALL_SHADERS);
2427 addFunctionCases<FrexpCase> (this, "frexp", true, false, false, ALL_SHADERS);
2428 addFunctionCases<LdexpCase> (this, "ldexp", true, false, false, ALL_SHADERS);
2429 addFunctionCases<FmaCase> (this, "fma", true, false, false, ALL_SHADERS);
2431 // (u)intBitsToFloat()
2433 const deUint32 shaderBits = NEW_SHADERS;
2434 tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "intbitstofloat", "intBitsToFloat() Tests");
2435 tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat", "uintBitsToFloat() Tests");
2438 addChild(uintGroup);
2440 for (int vecSize = 1; vecSize < 4; vecSize++)
2442 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
2443 const glu::DataType uintType = vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT;
2445 for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
2447 if (shaderBits & (1<<shaderType))
2449 intGroup->addChild(new BitsToFloatCase(getTestContext(), intType, glu::ShaderType(shaderType)));
2450 uintGroup->addChild(new BitsToFloatCase(getTestContext(), uintType, glu::ShaderType(shaderType)));