1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.1 Module
3 * -------------------------------------------------
5 * Copyright 2014 The Android Open Source Project
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
21 * \brief Common built-in function tests.
22 *//*--------------------------------------------------------------------*/
24 #include "es31fShaderCommonFunctionTests.hpp"
25 #include "gluContextInfo.hpp"
26 #include "glsShaderExecUtil.hpp"
27 #include "tcuTestLog.hpp"
28 #include "tcuFormatUtil.hpp"
29 #include "tcuFloat.hpp"
30 #include "tcuInterval.hpp"
31 #include "tcuFloatFormat.hpp"
32 #include "tcuVectorUtil.hpp"
33 #include "deRandom.hpp"
36 #include "deArrayUtil.hpp"
48 using namespace gls::ShaderExecUtil;
59 template<typename T, int Size>
63 VecArrayAccess (const void* ptr) : m_array((tcu::Vector<T, Size>*)ptr) {}
64 ~VecArrayAccess (void) {}
66 const tcu::Vector<T, Size>& operator[] (size_t offset) const { return m_array[offset]; }
67 tcu::Vector<T, Size>& operator[] (size_t offset) { return m_array[offset]; }
70 tcu::Vector<T, Size>* m_array;
73 template<typename T, int Size>
74 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)
76 VecArrayAccess<T, Size> access(dst);
77 for (int ndx = 0; ndx < numValues; ndx++)
78 access[offset + ndx] = tcu::randomVector<T, Size>(rnd, minValue, maxValue);
82 static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0)
84 T* typedPtr = (T*)dst;
85 for (int ndx = 0; ndx < numValues; ndx++)
86 typedPtr[offset + ndx] = de::randomScalar<T>(rnd, minValue, maxValue);
89 inline int numBitsLostInOp (float input, float output)
91 const int inExp = tcu::Float32(input).exponent();
92 const int outExp = tcu::Float32(output).exponent();
94 return de::max(0, inExp-outExp); // Lost due to mantissa shift.
97 inline deUint32 getUlpDiff (float a, float b)
99 const deUint32 aBits = tcu::Float32(a).bits();
100 const deUint32 bBits = tcu::Float32(b).bits();
101 return aBits > bBits ? aBits - bBits : bBits - aBits;
104 inline deUint32 getUlpDiffIgnoreZeroSign (float a, float b)
106 if (tcu::Float32(a).isZero())
107 return getUlpDiff(tcu::Float32::construct(tcu::Float32(b).sign(), 0, 0).asFloat(), b);
108 else if (tcu::Float32(b).isZero())
109 return getUlpDiff(a, tcu::Float32::construct(tcu::Float32(a).sign(), 0, 0).asFloat());
111 return getUlpDiff(a, b);
114 inline bool supportsSignedZero (glu::Precision precision)
116 // \note GLSL ES 3.1 doesn't really require support for -0, but we require it for highp
117 // as it is very widely supported.
118 return precision == glu::PRECISION_HIGHP;
121 inline float getEpsFromMaxUlpDiff (float value, deUint32 ulpDiff)
123 const int exp = tcu::Float32(value).exponent();
124 return tcu::Float32::construct(+1, exp, (1u<<23) | ulpDiff).asFloat() - tcu::Float32::construct(+1, exp, 1u<<23).asFloat();
127 inline deUint32 getMaxUlpDiffFromBits (int numAccurateBits)
129 const int numGarbageBits = 23-numAccurateBits;
130 const deUint32 mask = (1u<<numGarbageBits)-1u;
135 inline float getEpsFromBits (float value, int numAccurateBits)
137 return getEpsFromMaxUlpDiff(value, getMaxUlpDiffFromBits(numAccurateBits));
140 static int getMinMantissaBits (glu::Precision precision)
148 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(bits) == glu::PRECISION_LAST);
149 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(bits)));
150 return bits[precision];
153 static int getMaxNormalizedValueExponent (glu::Precision precision)
155 const int exponent[] =
161 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(exponent) == glu::PRECISION_LAST);
162 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(exponent)));
163 return exponent[precision];
166 static int getMinNormalizedValueExponent (glu::Precision precision)
168 const int exponent[] =
174 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(exponent) == glu::PRECISION_LAST);
175 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(exponent)));
176 return exponent[precision];
179 static float makeFloatRepresentable (float f, glu::Precision precision)
181 if (precision == glu::PRECISION_HIGHP)
183 // \note: assuming f is not extended-precision
188 const int numMantissaBits = getMinMantissaBits(precision);
189 const int maxNormalizedValueExponent = getMaxNormalizedValueExponent(precision);
190 const int minNormalizedValueExponent = getMinNormalizedValueExponent(precision);
191 const deUint32 representableMantissaMask = ((deUint32(1) << numMantissaBits) - 1) << (23 - (deUint32)numMantissaBits);
192 const float largestRepresentableValue = tcu::Float32::constructBits(+1, maxNormalizedValueExponent, ((1u << numMantissaBits) - 1u) << (23u - (deUint32)numMantissaBits)).asFloat();
193 const bool zeroNotRepresentable = (precision == glu::PRECISION_LOWP);
195 // if zero is not required to be representable, use smallest positive non-subnormal value
196 const float zeroValue = (zeroNotRepresentable) ? (tcu::Float32::constructBits(+1, minNormalizedValueExponent, 1).asFloat()) : (0.0f);
198 const tcu::Float32 float32Representation (f);
200 if (float32Representation.exponent() < minNormalizedValueExponent)
202 // flush too small values to zero
205 else if (float32Representation.exponent() > maxNormalizedValueExponent)
207 // clamp too large values
208 return (float32Representation.sign() == +1) ? (largestRepresentableValue) : (-largestRepresentableValue);
212 // remove unrepresentable mantissa bits
213 const tcu::Float32 targetRepresentation(tcu::Float32::constructBits(float32Representation.sign(),
214 float32Representation.exponent(),
215 float32Representation.mantissaBits() & representableMantissaMask));
217 return targetRepresentation.asFloat();
222 // CommonFunctionCase
224 class CommonFunctionCase : public TestCase
227 CommonFunctionCase (Context& context, const char* name, const char* description, glu::ShaderType shaderType);
228 ~CommonFunctionCase (void);
232 IterateResult iterate (void);
235 CommonFunctionCase (const CommonFunctionCase& other);
236 CommonFunctionCase& operator= (const CommonFunctionCase& other);
238 virtual void getInputValues (int numValues, void* const* values) const = 0;
239 virtual bool compare (const void* const* inputs, const void* const* outputs) = 0;
241 glu::ShaderType m_shaderType;
245 std::ostringstream m_failMsg; //!< Comparison failure help message.
248 ShaderExecutor* m_executor;
251 CommonFunctionCase::CommonFunctionCase (Context& context, const char* name, const char* description, glu::ShaderType shaderType)
252 : TestCase (context, name, description)
253 , m_shaderType (shaderType)
255 , m_executor (DE_NULL)
259 CommonFunctionCase::~CommonFunctionCase (void)
261 CommonFunctionCase::deinit();
264 void CommonFunctionCase::init (void)
266 DE_ASSERT(!m_executor);
268 m_spec.version = contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) ? glu::GLSL_VERSION_320_ES : glu::GLSL_VERSION_310_ES;
270 m_executor = createExecutor(m_context.getRenderContext(), m_shaderType, m_spec);
271 m_testCtx.getLog() << m_executor;
273 if (!m_executor->isOk())
274 throw tcu::TestError("Compile failed");
277 void CommonFunctionCase::deinit (void)
280 m_executor = DE_NULL;
283 static vector<int> getScalarSizes (const vector<Symbol>& symbols)
285 vector<int> sizes(symbols.size());
286 for (int ndx = 0; ndx < (int)symbols.size(); ++ndx)
287 sizes[ndx] = symbols[ndx].varType.getScalarSize();
291 static int computeTotalScalarSize (const vector<Symbol>& symbols)
294 for (vector<Symbol>::const_iterator sym = symbols.begin(); sym != symbols.end(); ++sym)
295 totalSize += sym->varType.getScalarSize();
299 static vector<void*> getInputOutputPointers (const vector<Symbol>& symbols, vector<deUint32>& data, const int numValues)
301 vector<void*> pointers (symbols.size());
302 int curScalarOffset = 0;
304 for (int varNdx = 0; varNdx < (int)symbols.size(); ++varNdx)
306 const Symbol& var = symbols[varNdx];
307 const int scalarSize = var.varType.getScalarSize();
309 // Uses planar layout as input/output specs do not support strides.
310 pointers[varNdx] = &data[curScalarOffset];
311 curScalarOffset += scalarSize*numValues;
314 DE_ASSERT(curScalarOffset == (int)data.size());
319 // \todo [2013-08-08 pyry] Make generic utility and move to glu?
324 HexFloat (const float value_) : value(value_) {}
327 std::ostream& operator<< (std::ostream& str, const HexFloat& v)
329 return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
334 const deUint32 value;
335 HexBool (const deUint32 value_) : value(value_) {}
338 std::ostream& operator<< (std::ostream& str, const HexBool& v)
340 return str << (v.value ? "true" : "false") << " / " << tcu::toHex(v.value);
345 const glu::VarType& type;
348 VarValue (const glu::VarType& type_, const void* value_) : type(type_), value(value_) {}
351 std::ostream& operator<< (std::ostream& str, const VarValue& varValue)
353 DE_ASSERT(varValue.type.isBasicType());
355 const glu::DataType basicType = varValue.type.getBasicType();
356 const glu::DataType scalarType = glu::getDataTypeScalarType(basicType);
357 const int numComponents = glu::getDataTypeScalarSize(basicType);
359 if (numComponents > 1)
360 str << glu::getDataTypeName(basicType) << "(";
362 for (int compNdx = 0; compNdx < numComponents; compNdx++)
369 case glu::TYPE_FLOAT: str << HexFloat(((const float*)varValue.value)[compNdx]); break;
370 case glu::TYPE_INT: str << ((const deInt32*)varValue.value)[compNdx]; break;
371 case glu::TYPE_UINT: str << tcu::toHex(((const deUint32*)varValue.value)[compNdx]); break;
372 case glu::TYPE_BOOL: str << HexBool(((const deUint32*)varValue.value)[compNdx]); break;
379 if (numComponents > 1)
385 CommonFunctionCase::IterateResult CommonFunctionCase::iterate (void)
387 const int numInputScalars = computeTotalScalarSize(m_spec.inputs);
388 const int numOutputScalars = computeTotalScalarSize(m_spec.outputs);
389 vector<deUint32> inputData (numInputScalars * m_numValues);
390 vector<deUint32> outputData (numOutputScalars * m_numValues);
391 const vector<void*> inputPointers = getInputOutputPointers(m_spec.inputs, inputData, m_numValues);
392 const vector<void*> outputPointers = getInputOutputPointers(m_spec.outputs, outputData, m_numValues);
394 // Initialize input data.
395 getInputValues(m_numValues, &inputPointers[0]);
398 m_executor->useProgram();
399 m_executor->execute(m_numValues, &inputPointers[0], &outputPointers[0]);
403 const vector<int> inScalarSizes = getScalarSizes(m_spec.inputs);
404 const vector<int> outScalarSizes = getScalarSizes(m_spec.outputs);
405 vector<void*> curInputPtr (inputPointers.size());
406 vector<void*> curOutputPtr (outputPointers.size());
409 for (int valNdx = 0; valNdx < m_numValues; valNdx++)
411 // Set up pointers for comparison.
412 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx)
413 curInputPtr[inNdx] = (deUint32*)inputPointers[inNdx] + inScalarSizes[inNdx]*valNdx;
415 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx)
416 curOutputPtr[outNdx] = (deUint32*)outputPointers[outNdx] + outScalarSizes[outNdx]*valNdx;
418 if (!compare(&curInputPtr[0], &curOutputPtr[0]))
420 // \todo [2013-08-08 pyry] We probably want to log reference value as well?
422 m_testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n " << m_failMsg.str() << TestLog::EndMessage;
424 m_testCtx.getLog() << TestLog::Message << " inputs:" << TestLog::EndMessage;
425 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++)
426 m_testCtx.getLog() << TestLog::Message << " " << m_spec.inputs[inNdx].name << " = "
427 << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx])
428 << TestLog::EndMessage;
430 m_testCtx.getLog() << TestLog::Message << " outputs:" << TestLog::EndMessage;
431 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++)
432 m_testCtx.getLog() << TestLog::Message << " " << m_spec.outputs[outNdx].name << " = "
433 << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx])
434 << TestLog::EndMessage;
442 m_testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage;
444 m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
445 numFailed == 0 ? "Pass" : "Result comparison failed");
451 static const char* getPrecisionPostfix (glu::Precision precision)
453 static const char* s_postfix[] =
459 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_postfix) == glu::PRECISION_LAST);
460 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
461 return s_postfix[precision];
464 static const char* getShaderTypePostfix (glu::ShaderType shaderType)
466 static const char* s_postfix[] =
475 DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
476 return s_postfix[shaderType];
479 static std::string getCommonFuncCaseName (glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
481 return string(glu::getDataTypeName(baseType)) + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType);
484 class AbsCase : public CommonFunctionCase
487 AbsCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
488 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "abs", shaderType)
490 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
491 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
492 m_spec.source = "out0 = abs(in0);";
495 void getInputValues (int numValues, void* const* values) const
497 const Vec2 floatRanges[] =
499 Vec2(-2.0f, 2.0f), // lowp
500 Vec2(-1e3f, 1e3f), // mediump
501 Vec2(-1e7f, 1e7f) // highp
503 const IVec2 intRanges[] =
505 IVec2(-(1<<7)+1, (1<<7)-1),
506 IVec2(-(1<<15)+1, (1<<15)-1),
507 IVec2(0x80000001, 0x7fffffff)
510 de::Random rnd (deStringHash(getName()) ^ 0x235facu);
511 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
512 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
513 const int scalarSize = glu::getDataTypeScalarSize(type);
515 if (glu::isDataTypeFloatOrVec(type))
516 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), values[0], numValues*scalarSize);
518 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize);
521 bool compare (const void* const* inputs, const void* const* outputs)
523 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
524 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
525 const int scalarSize = glu::getDataTypeScalarSize(type);
527 if (glu::isDataTypeFloatOrVec(type))
529 const int mantissaBits = getMinMantissaBits(precision);
530 const deUint32 maxUlpDiff = (1u<<(23-mantissaBits))-1u;
532 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
534 const float in0 = ((const float*)inputs[0])[compNdx];
535 const float out0 = ((const float*)outputs[0])[compNdx];
536 const float ref0 = de::abs(in0);
537 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
539 if (ulpDiff0 > maxUlpDiff)
541 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
548 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
550 const int in0 = ((const int*)inputs[0])[compNdx];
551 const int out0 = ((const int*)outputs[0])[compNdx];
552 const int ref0 = de::abs(in0);
556 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
566 class SignCase : public CommonFunctionCase
569 SignCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
570 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "sign", shaderType)
572 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
573 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
574 m_spec.source = "out0 = sign(in0);";
577 void getInputValues (int numValues, void* const* values) const
579 const Vec2 floatRanges[] =
581 Vec2(-2.0f, 2.0f), // lowp
582 Vec2(-1e4f, 1e4f), // mediump - note: may end up as inf
583 Vec2(-1e8f, 1e8f) // highp - note: may end up as inf
585 const IVec2 intRanges[] =
587 IVec2(-(1<<7), (1<<7)-1),
588 IVec2(-(1<<15), (1<<15)-1),
589 IVec2(0x80000000, 0x7fffffff)
592 de::Random rnd (deStringHash(getName()) ^ 0x324u);
593 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
594 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
595 const int scalarSize = glu::getDataTypeScalarSize(type);
597 if (glu::isDataTypeFloatOrVec(type))
600 std::fill((float*)values[0], (float*)values[0] + scalarSize, +1.0f);
601 std::fill((float*)values[0] + scalarSize*1, (float*)values[0] + scalarSize*2, -1.0f);
602 std::fill((float*)values[0] + scalarSize*2, (float*)values[0] + scalarSize*3, 0.0f);
603 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), (float*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
607 std::fill((int*)values[0], (int*)values[0] + scalarSize, +1);
608 std::fill((int*)values[0] + scalarSize*1, (int*)values[0] + scalarSize*2, -1);
609 std::fill((int*)values[0] + scalarSize*2, (int*)values[0] + scalarSize*3, 0);
610 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
614 bool compare (const void* const* inputs, const void* const* outputs)
616 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
617 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
618 const int scalarSize = glu::getDataTypeScalarSize(type);
620 if (glu::isDataTypeFloatOrVec(type))
622 // Both highp and mediump should be able to represent -1, 0, and +1 exactly
623 const deUint32 maxUlpDiff = precision == glu::PRECISION_LOWP ? getMaxUlpDiffFromBits(getMinMantissaBits(precision)) : 0;
625 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
627 const float in0 = ((const float*)inputs[0])[compNdx];
628 const float out0 = ((const float*)outputs[0])[compNdx];
629 const float ref0 = in0 < 0.0f ? -1.0f :
630 in0 > 0.0f ? +1.0f : 0.0f;
631 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0);
633 if (ulpDiff0 > maxUlpDiff)
635 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
642 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
644 const int in0 = ((const int*)inputs[0])[compNdx];
645 const int out0 = ((const int*)outputs[0])[compNdx];
646 const int ref0 = in0 < 0 ? -1 :
651 m_failMsg << "Expected [" << compNdx << "] = " << ref0;
661 static float roundEven (float v)
663 const float q = deFloatFrac(v);
664 const int truncated = int(v-q);
665 const int rounded = (q > 0.5f) ? (truncated + 1) : // Rounded up
666 (q == 0.5f && (truncated % 2 != 0)) ? (truncated + 1) : // Round to nearest even at 0.5
667 truncated; // Rounded down
669 return float(rounded);
672 class RoundEvenCase : public CommonFunctionCase
675 RoundEvenCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
676 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "roundEven", shaderType)
678 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
679 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
680 m_spec.source = "out0 = roundEven(in0);";
683 void getInputValues (int numValues, void* const* values) const
685 const Vec2 ranges[] =
687 Vec2(-2.0f, 2.0f), // lowp
688 Vec2(-1e3f, 1e3f), // mediump
689 Vec2(-1e7f, 1e7f) // highp
692 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
693 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
694 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
695 const int scalarSize = glu::getDataTypeScalarSize(type);
696 int numSpecialCases = 0;
699 if (precision != glu::PRECISION_LOWP)
701 DE_ASSERT(numValues >= 20);
702 for (int ndx = 0; ndx < 20; ndx++)
704 const float v = de::clamp(float(ndx) - 10.5f, ranges[precision].x(), ranges[precision].y());
705 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
706 numSpecialCases += 1;
711 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
713 // If precision is mediump, make sure values can be represented in fp16 exactly
714 if (precision == glu::PRECISION_MEDIUMP)
716 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
717 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
721 bool compare (const void* const* inputs, const void* const* outputs)
723 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
724 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
725 const bool hasSignedZero = supportsSignedZero(precision);
726 const int scalarSize = glu::getDataTypeScalarSize(type);
728 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
730 // Require exact rounding result.
731 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
733 const float in0 = ((const float*)inputs[0])[compNdx];
734 const float out0 = ((const float*)outputs[0])[compNdx];
735 const float ref = roundEven(in0);
737 const deUint32 ulpDiff = hasSignedZero ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
741 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
748 const int mantissaBits = getMinMantissaBits(precision);
749 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
750 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
752 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
754 const float in0 = ((const float*)inputs[0])[compNdx];
755 const float out0 = ((const float*)outputs[0])[compNdx];
756 const int minRes = int(roundEven(in0-eps));
757 const int maxRes = int(roundEven(in0+eps));
760 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
762 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
764 if (ulpDiff <= maxUlpDiff)
773 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
783 class ModfCase : public CommonFunctionCase
786 ModfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
787 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "modf", shaderType)
789 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
790 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
791 m_spec.outputs.push_back(Symbol("out1", glu::VarType(baseType, precision)));
792 m_spec.source = "out0 = modf(in0, out1);";
795 void getInputValues (int numValues, void* const* values) const
797 const Vec2 ranges[] =
799 Vec2(-2.0f, 2.0f), // lowp
800 Vec2(-1e3f, 1e3f), // mediump
801 Vec2(-1e7f, 1e7f) // highp
804 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
805 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
806 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
807 const int scalarSize = glu::getDataTypeScalarSize(type);
809 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
812 bool compare (const void* const* inputs, const void* const* outputs)
814 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
815 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
816 const bool hasZeroSign = supportsSignedZero(precision);
817 const int scalarSize = glu::getDataTypeScalarSize(type);
819 const int mantissaBits = getMinMantissaBits(precision);
821 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
823 const float in0 = ((const float*)inputs[0])[compNdx];
824 const float out0 = ((const float*)outputs[0])[compNdx];
825 const float out1 = ((const float*)outputs[1])[compNdx];
827 const float refOut1 = float(int(in0));
828 const float refOut0 = in0 - refOut1;
830 const int bitsLost = precision != glu::PRECISION_HIGHP ? numBitsLostInOp(in0, refOut0) : 0;
831 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(mantissaBits - bitsLost, 0));
833 const float resSum = out0 + out1;
835 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(resSum, in0) : getUlpDiffIgnoreZeroSign(resSum, in0);
837 if (ulpDiff > maxUlpDiff)
839 m_failMsg << "Expected [" << compNdx << "] = (" << HexFloat(refOut0) << ") + (" << HexFloat(refOut1) << ") = " << HexFloat(in0) << " with ULP threshold "
840 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
849 class IsnanCase : public CommonFunctionCase
852 IsnanCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
853 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isnan", shaderType)
855 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
857 const int vecSize = glu::getDataTypeScalarSize(baseType);
858 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
860 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
861 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
862 m_spec.source = "out0 = isnan(in0);";
865 void getInputValues (int numValues, void* const* values) const
867 de::Random rnd (deStringHash(getName()) ^ 0xc2a39fu);
868 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
869 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
870 const int scalarSize = glu::getDataTypeScalarSize(type);
871 const int mantissaBits = getMinMantissaBits(precision);
872 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
874 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
876 const bool isNan = rnd.getFloat() > 0.3f;
877 const bool isInf = !isNan && rnd.getFloat() > 0.4f;
878 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
879 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
880 const deUint32 sign = rnd.getUint32() & 0x1u;
881 const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
883 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
885 ((deUint32*)values[0])[valNdx] = value;
889 bool compare (const void* const* inputs, const void* const* outputs)
891 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
892 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
893 const int scalarSize = glu::getDataTypeScalarSize(type);
895 if (precision == glu::PRECISION_HIGHP)
897 // Only highp is required to support inf/nan
898 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
900 const float in0 = ((const float*)inputs[0])[compNdx];
901 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
902 const bool ref = tcu::Float32(in0).isNaN();
906 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
911 else if (precision == glu::PRECISION_MEDIUMP || precision == glu::PRECISION_LOWP)
913 // NaN support is optional, check that inputs that are not NaN don't result in true.
914 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
916 const float in0 = ((const float*)inputs[0])[compNdx];
917 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
918 const bool ref = tcu::Float32(in0).isNaN();
922 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
932 class IsinfCase : public CommonFunctionCase
935 IsinfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
936 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isinf", shaderType)
938 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
940 const int vecSize = glu::getDataTypeScalarSize(baseType);
941 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
943 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
944 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
945 m_spec.source = "out0 = isinf(in0);";
948 void getInputValues (int numValues, void* const* values) const
950 de::Random rnd (deStringHash(getName()) ^ 0xc2a39fu);
951 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
952 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
953 const int scalarSize = glu::getDataTypeScalarSize(type);
954 const int mantissaBits = getMinMantissaBits(precision);
955 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
957 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
959 const bool isInf = rnd.getFloat() > 0.3f;
960 const bool isNan = !isInf && rnd.getFloat() > 0.4f;
961 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
962 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
963 const deUint32 sign = rnd.getUint32() & 0x1u;
964 const deUint32 value = (sign << 31) | (exp << 23) | mantissa;
966 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
968 ((deUint32*)values[0])[valNdx] = value;
972 bool compare (const void* const* inputs, const void* const* outputs)
974 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
975 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
976 const int scalarSize = glu::getDataTypeScalarSize(type);
978 if (precision == glu::PRECISION_HIGHP)
980 // Only highp is required to support inf/nan
981 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
983 const float in0 = ((const float*)inputs[0])[compNdx];
984 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
985 const bool ref = tcu::Float32(in0).isInf();
989 m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
994 else if (precision == glu::PRECISION_MEDIUMP)
996 // Inf support is optional, check that inputs that are not Inf in mediump don't result in true.
997 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
999 const float in0 = ((const float*)inputs[0])[compNdx];
1000 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0;
1001 const bool ref = tcu::Float16(in0).isInf();
1005 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
1010 // else: no verification can be performed
1016 class FloatBitsToUintIntCase : public CommonFunctionCase
1019 FloatBitsToUintIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType, bool outIsSigned)
1020 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), outIsSigned ? "floatBitsToInt" : "floatBitsToUint", shaderType)
1022 const int vecSize = glu::getDataTypeScalarSize(baseType);
1023 const glu::DataType intType = outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT)
1024 : (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT);
1026 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1027 m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP)));
1028 m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);";
1031 void getInputValues (int numValues, void* const* values) const
1033 const Vec2 ranges[] =
1035 Vec2(-2.0f, 2.0f), // lowp
1036 Vec2(-1e3f, 1e3f), // mediump
1037 Vec2(-1e7f, 1e7f) // highp
1040 de::Random rnd (deStringHash(getName()) ^ 0x2790au);
1041 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1042 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1043 const int scalarSize = glu::getDataTypeScalarSize(type);
1045 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
1048 bool compare (const void* const* inputs, const void* const* outputs)
1050 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1051 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1052 const int scalarSize = glu::getDataTypeScalarSize(type);
1054 const int mantissaBits = getMinMantissaBits(precision);
1055 const int maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
1057 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1059 const float in0 = ((const float*)inputs[0])[compNdx];
1060 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx];
1061 const deUint32 refOut0 = tcu::Float32(in0).bits();
1062 const int ulpDiff = de::abs((int)out0 - (int)refOut0);
1064 if (ulpDiff > maxUlpDiff)
1066 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold "
1067 << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1076 class FloatBitsToIntCase : public FloatBitsToUintIntCase
1079 FloatBitsToIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1080 : FloatBitsToUintIntCase(context, baseType, precision, shaderType, true)
1085 class FloatBitsToUintCase : public FloatBitsToUintIntCase
1088 FloatBitsToUintCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1089 : FloatBitsToUintIntCase(context, baseType, precision, shaderType, false)
1094 class BitsToFloatCase : public CommonFunctionCase
1097 BitsToFloatCase (Context& context, glu::DataType baseType, glu::ShaderType shaderType)
1098 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP, shaderType).c_str(), glu::isDataTypeIntOrIVec(baseType) ? "intBitsToFloat" : "uintBitsToFloat", shaderType)
1100 const bool inIsSigned = glu::isDataTypeIntOrIVec(baseType);
1101 const int vecSize = glu::getDataTypeScalarSize(baseType);
1102 const glu::DataType floatType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1104 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1105 m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP)));
1106 m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);";
1109 void getInputValues (int numValues, void* const* values) const
1111 de::Random rnd (deStringHash(getName()) ^ 0xbbb225u);
1112 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1113 const int scalarSize = glu::getDataTypeScalarSize(type);
1114 const Vec2 range (-1e8f, +1e8f);
1116 // \note Filled as floats.
1117 fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize);
1120 bool compare (const void* const* inputs, const void* const* outputs)
1122 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1123 const int scalarSize = glu::getDataTypeScalarSize(type);
1124 const deUint32 maxUlpDiff = 0;
1126 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1128 const float in0 = ((const float*)inputs[0])[compNdx];
1129 const float out0 = ((const float*)outputs[0])[compNdx];
1130 const deUint32 ulpDiff = getUlpDiff(in0, out0);
1132 if (ulpDiff > maxUlpDiff)
1134 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits()) << " with ULP threshold "
1135 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
1144 class FloorCase : public CommonFunctionCase
1147 FloorCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1148 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "floor", shaderType)
1150 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1151 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1152 m_spec.source = "out0 = floor(in0);";
1155 void getInputValues (int numValues, void* const* values) const
1157 const Vec2 ranges[] =
1159 Vec2(-2.0f, 2.0f), // lowp
1160 Vec2(-1e3f, 1e3f), // mediump
1161 Vec2(-1e7f, 1e7f) // highp
1164 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1165 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1166 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1167 const int scalarSize = glu::getDataTypeScalarSize(type);
1169 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1171 // If precision is mediump, make sure values can be represented in fp16 exactly
1172 if (precision == glu::PRECISION_MEDIUMP)
1174 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1175 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1179 bool compare (const void* const* inputs, const void* const* outputs)
1181 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1182 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1183 const int scalarSize = glu::getDataTypeScalarSize(type);
1185 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1187 // Require exact result.
1188 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1190 const float in0 = ((const float*)inputs[0])[compNdx];
1191 const float out0 = ((const float*)outputs[0])[compNdx];
1192 const float ref = deFloatFloor(in0);
1194 const deUint32 ulpDiff = getUlpDiff(out0, ref);
1198 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1205 const int mantissaBits = getMinMantissaBits(precision);
1206 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1207 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1209 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1211 const float in0 = ((const float*)inputs[0])[compNdx];
1212 const float out0 = ((const float*)outputs[0])[compNdx];
1213 const int minRes = int(deFloatFloor(in0-eps));
1214 const int maxRes = int(deFloatFloor(in0+eps));
1217 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1219 const deUint32 ulpDiff = getUlpDiff(out0, float(roundedVal));
1221 if (ulpDiff <= maxUlpDiff)
1230 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1240 class TruncCase : public CommonFunctionCase
1243 TruncCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1244 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "trunc", shaderType)
1246 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1247 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1248 m_spec.source = "out0 = trunc(in0);";
1251 void getInputValues (int numValues, void* const* values) const
1253 const Vec2 ranges[] =
1255 Vec2(-2.0f, 2.0f), // lowp
1256 Vec2(-1e3f, 1e3f), // mediump
1257 Vec2(-1e7f, 1e7f) // highp
1260 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1261 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1262 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1263 const int scalarSize = glu::getDataTypeScalarSize(type);
1264 const float specialCases[] = { 0.0f, -0.0f, -0.9f, 0.9f, 1.0f, -1.0f };
1265 const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
1268 for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
1270 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1271 ((float*)values[0])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx];
1275 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + scalarSize*numSpecialCases, (numValues-numSpecialCases)*scalarSize);
1277 // If precision is mediump, make sure values can be represented in fp16 exactly
1278 if (precision == glu::PRECISION_MEDIUMP)
1280 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1281 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1285 bool compare (const void* const* inputs, const void* const* outputs)
1287 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1288 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1289 const int scalarSize = glu::getDataTypeScalarSize(type);
1291 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1293 // Require exact result.
1294 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1296 const float in0 = ((const float*)inputs[0])[compNdx];
1297 const float out0 = ((const float*)outputs[0])[compNdx];
1298 const bool isNeg = tcu::Float32(in0).sign() < 0;
1299 const float ref = isNeg ? (-float(int(-in0))) : float(int(in0));
1301 // \note: trunc() function definition is a bit broad on negative zeros. Ignore result sign if zero.
1302 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1306 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1313 const int mantissaBits = getMinMantissaBits(precision);
1314 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1315 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1317 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1319 const float in0 = ((const float*)inputs[0])[compNdx];
1320 const float out0 = ((const float*)outputs[0])[compNdx];
1321 const int minRes = int(in0-eps);
1322 const int maxRes = int(in0+eps);
1325 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1327 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1329 if (ulpDiff <= maxUlpDiff)
1338 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1348 class RoundCase : public CommonFunctionCase
1351 RoundCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1352 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "round", shaderType)
1354 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1355 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1356 m_spec.source = "out0 = round(in0);";
1359 void getInputValues (int numValues, void* const* values) const
1361 const Vec2 ranges[] =
1363 Vec2(-2.0f, 2.0f), // lowp
1364 Vec2(-1e3f, 1e3f), // mediump
1365 Vec2(-1e7f, 1e7f) // highp
1368 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1369 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1370 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1371 const int scalarSize = glu::getDataTypeScalarSize(type);
1372 int numSpecialCases = 0;
1375 if (precision != glu::PRECISION_LOWP)
1377 DE_ASSERT(numValues >= 10);
1378 for (int ndx = 0; ndx < 10; ndx++)
1380 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1381 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1382 numSpecialCases += 1;
1387 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1389 // If precision is mediump, make sure values can be represented in fp16 exactly
1390 if (precision == glu::PRECISION_MEDIUMP)
1392 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1393 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1397 bool compare (const void* const* inputs, const void* const* outputs)
1399 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1400 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1401 const bool hasZeroSign = supportsSignedZero(precision);
1402 const int scalarSize = glu::getDataTypeScalarSize(type);
1404 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1406 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1408 const float in0 = ((const float*)inputs[0])[compNdx];
1409 const float out0 = ((const float*)outputs[0])[compNdx];
1411 if (deFloatFrac(in0) == 0.5f)
1413 // Allow both ceil(in) and floor(in)
1414 const float ref0 = deFloatFloor(in0);
1415 const float ref1 = deFloatCeil(in0);
1416 const deUint32 ulpDiff0 = hasZeroSign ? getUlpDiff(out0, ref0) : getUlpDiffIgnoreZeroSign(out0, ref0);
1417 const deUint32 ulpDiff1 = hasZeroSign ? getUlpDiff(out0, ref1) : getUlpDiffIgnoreZeroSign(out0, ref1);
1419 if (ulpDiff0 > 0 && ulpDiff1 > 0)
1421 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " or " << HexFloat(ref1) << ", got ULP diff " << tcu::toHex(de::min(ulpDiff0, ulpDiff1));
1427 // Require exact result
1428 const float ref = roundEven(in0);
1429 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1433 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1441 const int mantissaBits = getMinMantissaBits(precision);
1442 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1443 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1445 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1447 const float in0 = ((const float*)inputs[0])[compNdx];
1448 const float out0 = ((const float*)outputs[0])[compNdx];
1449 const int minRes = int(roundEven(in0-eps));
1450 const int maxRes = int(roundEven(in0+eps));
1453 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1455 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1457 if (ulpDiff <= maxUlpDiff)
1466 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1476 class CeilCase : public CommonFunctionCase
1479 CeilCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1480 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ceil", shaderType)
1482 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1483 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1484 m_spec.source = "out0 = ceil(in0);";
1487 void getInputValues (int numValues, void* const* values) const
1489 const Vec2 ranges[] =
1491 Vec2(-2.0f, 2.0f), // lowp
1492 Vec2(-1e3f, 1e3f), // mediump
1493 Vec2(-1e7f, 1e7f) // highp
1496 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1497 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1498 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1499 const int scalarSize = glu::getDataTypeScalarSize(type);
1502 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1504 // If precision is mediump, make sure values can be represented in fp16 exactly
1505 if (precision == glu::PRECISION_MEDIUMP)
1507 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1508 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1512 bool compare (const void* const* inputs, const void* const* outputs)
1514 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1515 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1516 const bool hasZeroSign = supportsSignedZero(precision);
1517 const int scalarSize = glu::getDataTypeScalarSize(type);
1519 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1521 // Require exact result.
1522 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1524 const float in0 = ((const float*)inputs[0])[compNdx];
1525 const float out0 = ((const float*)outputs[0])[compNdx];
1526 const float ref = deFloatCeil(in0);
1528 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1532 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1539 const int mantissaBits = getMinMantissaBits(precision);
1540 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1541 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1543 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1545 const float in0 = ((const float*)inputs[0])[compNdx];
1546 const float out0 = ((const float*)outputs[0])[compNdx];
1547 const int minRes = int(deFloatCeil(in0-eps));
1548 const int maxRes = int(deFloatCeil(in0+eps));
1551 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1553 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1555 if (ulpDiff <= maxUlpDiff)
1562 if (!anyOk && de::inRange(0, minRes, maxRes))
1564 // Allow -0 as well.
1565 const int ulpDiff = de::abs((int)tcu::Float32(out0).bits() - (int)0x80000000u);
1566 anyOk = ((deUint32)ulpDiff <= maxUlpDiff);
1571 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1581 class FractCase : public CommonFunctionCase
1584 FractCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1585 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fract", shaderType)
1587 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1588 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1589 m_spec.source = "out0 = fract(in0);";
1592 void getInputValues (int numValues, void* const* values) const
1594 const Vec2 ranges[] =
1596 Vec2(-2.0f, 2.0f), // lowp
1597 Vec2(-1e3f, 1e3f), // mediump
1598 Vec2(-1e7f, 1e7f) // highp
1601 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1602 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1603 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1604 const int scalarSize = glu::getDataTypeScalarSize(type);
1605 int numSpecialCases = 0;
1608 if (precision != glu::PRECISION_LOWP)
1610 DE_ASSERT(numValues >= 10);
1611 for (int ndx = 0; ndx < 10; ndx++)
1613 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1614 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1615 numSpecialCases += 1;
1620 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1622 // If precision is mediump, make sure values can be represented in fp16 exactly
1623 if (precision == glu::PRECISION_MEDIUMP)
1625 for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1626 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1630 bool compare (const void* const* inputs, const void* const* outputs)
1632 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1633 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1634 const bool hasZeroSign = supportsSignedZero(precision);
1635 const int scalarSize = glu::getDataTypeScalarSize(type);
1637 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1639 // Require exact result.
1640 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1642 const float in0 = ((const float*)inputs[0])[compNdx];
1643 const float out0 = ((const float*)outputs[0])[compNdx];
1644 const float ref = deFloatFrac(in0);
1646 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1650 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1657 const int mantissaBits = getMinMantissaBits(precision);
1658 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1660 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1662 const float in0 = ((const float*)inputs[0])[compNdx];
1663 const float out0 = ((const float*)outputs[0])[compNdx];
1665 if (int(deFloatFloor(in0-eps)) == int(deFloatFloor(in0+eps)))
1667 const float ref = deFloatFrac(in0);
1668 const int bitsLost = numBitsLostInOp(in0, ref);
1669 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(0, mantissaBits-bitsLost)); // ULP diff for rounded integer value.
1670 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1672 if (ulpDiff > maxUlpDiff)
1674 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << " with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1682 m_failMsg << "Expected [" << compNdx << "] < 1.0";
1693 static inline void frexp (float in, float* significand, int* exponent)
1695 const tcu::Float32 fpValue(in);
1697 if (!fpValue.isZero())
1699 // Construct float that has exactly the mantissa, and exponent of -1.
1700 *significand = tcu::Float32::construct(fpValue.sign(), -1, fpValue.mantissa()).asFloat();
1701 *exponent = fpValue.exponent()+1;
1705 *significand = fpValue.sign() < 0 ? -0.0f : 0.0f;
1710 static inline float ldexp (float significand, int exponent)
1712 const tcu::Float32 mant(significand);
1714 if (exponent == 0 && mant.isZero())
1716 return mant.sign() < 0 ? -0.0f : 0.0f;
1720 return tcu::Float32::construct(mant.sign(), exponent+mant.exponent(), mant.mantissa()).asFloat();
1724 class FrexpCase : public CommonFunctionCase
1727 FrexpCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1728 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "frexp", shaderType)
1730 const int vecSize = glu::getDataTypeScalarSize(baseType);
1731 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
1733 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1734 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1735 m_spec.outputs.push_back(Symbol("out1", glu::VarType(intType, glu::PRECISION_HIGHP)));
1736 m_spec.source = "out0 = frexp(in0, out1);";
1739 void getInputValues (int numValues, void* const* values) const
1741 const Vec2 ranges[] =
1743 Vec2(-2.0f, 2.0f), // lowp
1744 Vec2(-1e3f, 1e3f), // mediump
1745 Vec2(-1e7f, 1e7f) // highp
1748 de::Random rnd (deStringHash(getName()) ^ 0x2790au);
1749 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1750 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1751 const int scalarSize = glu::getDataTypeScalarSize(type);
1754 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1756 ((float*)values[0])[scalarSize*0 + compNdx] = 0.0f;
1757 ((float*)values[0])[scalarSize*1 + compNdx] = -0.0f;
1758 ((float*)values[0])[scalarSize*2 + compNdx] = 0.5f;
1759 ((float*)values[0])[scalarSize*3 + compNdx] = -0.5f;
1760 ((float*)values[0])[scalarSize*4 + compNdx] = 1.0f;
1761 ((float*)values[0])[scalarSize*5 + compNdx] = -1.0f;
1762 ((float*)values[0])[scalarSize*6 + compNdx] = 2.0f;
1763 ((float*)values[0])[scalarSize*7 + compNdx] = -2.0f;
1766 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + 8*scalarSize, (numValues-8)*scalarSize);
1768 // Make sure the values are representable in the target format
1769 for (int caseNdx = 0; caseNdx < numValues; ++caseNdx)
1771 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1773 float* const valuePtr = &((float*)values[0])[caseNdx * scalarSize + scalarNdx];
1775 *valuePtr = makeFloatRepresentable(*valuePtr, precision);
1780 bool compare (const void* const* inputs, const void* const* outputs)
1782 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1783 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1784 const int scalarSize = glu::getDataTypeScalarSize(type);
1785 const bool signedZero = false;
1787 const int mantissaBits = getMinMantissaBits(precision);
1788 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
1790 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1792 const float in0 = ((const float*)inputs[0])[compNdx];
1793 const float out0 = ((const float*)outputs[0])[compNdx];
1794 const int out1 = ((const int*)outputs[1])[compNdx];
1799 frexp(in0, &refOut0, &refOut1);
1801 const deUint32 ulpDiff0 = signedZero ? getUlpDiff(out0, refOut0) : getUlpDiffIgnoreZeroSign(out0, refOut0);
1803 if (ulpDiff0 > maxUlpDiff || out1 != refOut1)
1805 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", " << refOut1 << " with ULP threshold "
1806 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff0);
1815 class LdexpCase : public CommonFunctionCase
1818 LdexpCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1819 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ldexp", shaderType)
1821 const int vecSize = glu::getDataTypeScalarSize(baseType);
1822 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
1824 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1825 m_spec.inputs.push_back(Symbol("in1", glu::VarType(intType, glu::PRECISION_HIGHP)));
1826 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1827 m_spec.source = "out0 = ldexp(in0, in1);";
1830 void getInputValues (int numValues, void* const* values) const
1832 const Vec2 ranges[] =
1834 Vec2(-2.0f, 2.0f), // lowp
1835 Vec2(-1e3f, 1e3f), // mediump
1836 Vec2(-1e7f, 1e7f) // highp
1839 de::Random rnd (deStringHash(getName()) ^ 0x2790au);
1840 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1841 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1842 const int scalarSize = glu::getDataTypeScalarSize(type);
1846 const float easySpecialCases[] = { 0.0f, -0.0f, 0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f };
1848 DE_ASSERT(valueNdx + DE_LENGTH_OF_ARRAY(easySpecialCases) <= numValues);
1849 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(easySpecialCases); caseNdx++)
1854 frexp(easySpecialCases[caseNdx], &in0, &in1);
1856 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1858 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
1859 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
1867 // \note lowp and mediump can not necessarily fit the values in hard cases, so we'll use only easy ones.
1868 const int numEasyRandomCases = precision == glu::PRECISION_HIGHP ? 50 : (numValues-valueNdx);
1870 DE_ASSERT(valueNdx + numEasyRandomCases <= numValues);
1871 for (int caseNdx = 0; caseNdx < numEasyRandomCases; caseNdx++)
1873 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1875 const float in = rnd.getFloat(ranges[precision].x(), ranges[precision].y());
1879 frexp(in, &in0, &in1);
1881 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
1882 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
1890 const int numHardRandomCases = numValues-valueNdx;
1891 DE_ASSERT(numHardRandomCases >= 0 && valueNdx + numHardRandomCases <= numValues);
1893 for (int caseNdx = 0; caseNdx < numHardRandomCases; caseNdx++)
1895 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1897 const int fpExp = rnd.getInt(-126, 127);
1898 const int sign = rnd.getBool() ? -1 : +1;
1899 const deUint32 mantissa = (1u<<23) | (rnd.getUint32() & ((1u<<23)-1));
1900 const int in1 = rnd.getInt(de::max(-126, -126-fpExp), de::min(127, 127-fpExp));
1901 const float in0 = tcu::Float32::construct(sign, fpExp, mantissa).asFloat();
1903 DE_ASSERT(de::inRange(in1, -126, 127)); // See Khronos bug 11180
1904 DE_ASSERT(de::inRange(in1+fpExp, -126, 127));
1906 const float out = ldexp(in0, in1);
1908 DE_ASSERT(!tcu::Float32(out).isInf() && !tcu::Float32(out).isDenorm());
1911 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0;
1912 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1;
1920 bool compare (const void* const* inputs, const void* const* outputs)
1922 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1923 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1924 const int scalarSize = glu::getDataTypeScalarSize(type);
1926 const int mantissaBits = getMinMantissaBits(precision);
1927 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
1929 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1931 const float in0 = ((const float*)inputs[0])[compNdx];
1932 const int in1 = ((const int*)inputs[1])[compNdx];
1933 const float out0 = ((const float*)outputs[0])[compNdx];
1934 const float refOut0 = ldexp(in0, in1);
1935 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, refOut0);
1937 const int inExp = tcu::Float32(in0).exponent();
1939 if (ulpDiff > maxUlpDiff)
1941 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", (exp = " << inExp << ") with ULP threshold "
1942 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
1951 class FmaCase : public CommonFunctionCase
1954 FmaCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1955 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fma", shaderType)
1957 m_spec.inputs.push_back(Symbol("a", glu::VarType(baseType, precision)));
1958 m_spec.inputs.push_back(Symbol("b", glu::VarType(baseType, precision)));
1959 m_spec.inputs.push_back(Symbol("c", glu::VarType(baseType, precision)));
1960 m_spec.outputs.push_back(Symbol("res", glu::VarType(baseType, precision)));
1961 m_spec.source = "res = fma(a, b, c);";
1963 if (!glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::es(3, 2)))
1964 m_spec.globalDeclarations = "#extension GL_EXT_gpu_shader5 : require\n";
1969 if (!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2))
1970 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_gpu_shader5"))
1971 throw tcu::NotSupportedError("GL_EXT_gpu_shader5 not supported");
1973 CommonFunctionCase::init();
1976 void getInputValues (int numValues, void* const* values) const
1978 const Vec2 ranges[] =
1980 Vec2(-2.0f, 2.0f), // lowp
1981 Vec2(-127.f, 127.f), // mediump
1982 Vec2(-1e7f, 1e7f) // highp
1985 de::Random rnd (deStringHash(getName()) ^ 0xac23fu);
1986 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1987 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1988 const int scalarSize = glu::getDataTypeScalarSize(type);
1989 const float specialCases[][3] =
1992 { 0.0f, 0.0f, 0.0f },
1993 { 0.0f, 1.0f, 0.0f },
1994 { 0.0f, 0.0f, -1.0f },
1995 { 1.0f, 1.0f, 0.0f },
1996 { 1.0f, 1.0f, 1.0f },
1997 { -1.0f, 1.0f, 0.0f },
1998 { 1.0f, -1.0f, 0.0f },
1999 { -1.0f, -1.0f, 0.0f },
2000 { -0.0f, 1.0f, 0.0f },
2001 { 1.0f, -0.0f, 0.0f }
2003 const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
2006 for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
2008 for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2010 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
2011 ((float*)values[inputNdx])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx][inputNdx];
2017 const int numScalars = (numValues-numSpecialCases)*scalarSize;
2018 const int offs = scalarSize*numSpecialCases;
2020 for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2021 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[inputNdx] + offs, numScalars);
2024 // Make sure the values are representable in the target format
2025 for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2027 for (int caseNdx = 0; caseNdx < numValues; ++caseNdx)
2029 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
2031 float* const valuePtr = &((float*)values[inputNdx])[caseNdx * scalarSize + scalarNdx];
2033 *valuePtr = makeFloatRepresentable(*valuePtr, precision);
2039 static tcu::Interval fma (glu::Precision precision, float a, float b, float c)
2041 const tcu::FloatFormat formats[] =
2043 // minExp maxExp mantissa exact, subnormals infinities NaN
2044 tcu::FloatFormat(0, 0, 7, false, tcu::YES, tcu::MAYBE, tcu::MAYBE),
2045 tcu::FloatFormat(-13, 13, 9, false, tcu::MAYBE, tcu::MAYBE, tcu::MAYBE),
2046 tcu::FloatFormat(-126, 127, 23, true, tcu::MAYBE, tcu::YES, tcu::MAYBE)
2048 const tcu::FloatFormat& format = de::getSizedArrayElement<glu::PRECISION_LAST>(formats, precision);
2049 const tcu::Interval ia = format.convert(a);
2050 const tcu::Interval ib = format.convert(b);
2051 const tcu::Interval ic = format.convert(c);
2052 tcu::Interval prod0;
2053 tcu::Interval prod1;
2054 tcu::Interval prod2;
2055 tcu::Interval prod3;
2059 TCU_SET_INTERVAL(prod0, tmp, tmp = ia.lo() * ib.lo());
2060 TCU_SET_INTERVAL(prod1, tmp, tmp = ia.lo() * ib.hi());
2061 TCU_SET_INTERVAL(prod2, tmp, tmp = ia.hi() * ib.lo());
2062 TCU_SET_INTERVAL(prod3, tmp, tmp = ia.hi() * ib.hi());
2064 prod = format.convert(format.roundOut(prod0 | prod1 | prod2 | prod3, ia.isFinite() && ib.isFinite()));
2066 TCU_SET_INTERVAL_BOUNDS(res, tmp,
2067 tmp = prod.lo() + ic.lo(),
2068 tmp = prod.hi() + ic.hi());
2070 return format.convert(format.roundOut(res, prod.isFinite() && ic.isFinite()));
2073 bool compare (const void* const* inputs, const void* const* outputs)
2075 const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
2076 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2077 const int scalarSize = glu::getDataTypeScalarSize(type);
2079 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2081 const float a = ((const float*)inputs[0])[compNdx];
2082 const float b = ((const float*)inputs[1])[compNdx];
2083 const float c = ((const float*)inputs[2])[compNdx];
2084 const float res = ((const float*)outputs[0])[compNdx];
2085 const tcu::Interval ref = fma(precision, a, b, c);
2087 if (!ref.contains(res))
2089 m_failMsg << "Expected [" << compNdx << "] = " << ref;
2098 ShaderCommonFunctionTests::ShaderCommonFunctionTests (Context& context)
2099 : TestCaseGroup(context, "common", "Common function tests")
2103 ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void)
2107 template<class TestClass>
2108 static void addFunctionCases (TestCaseGroup* parent, const char* functionName, bool floatTypes, bool intTypes, bool uintTypes, deUint32 shaderBits)
2110 tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), functionName, functionName);
2111 parent->addChild(group);
2113 const glu::DataType scalarTypes[] =
2120 for (int scalarTypeNdx = 0; scalarTypeNdx < DE_LENGTH_OF_ARRAY(scalarTypes); scalarTypeNdx++)
2122 const glu::DataType scalarType = scalarTypes[scalarTypeNdx];
2124 if ((!floatTypes && scalarType == glu::TYPE_FLOAT) ||
2125 (!intTypes && scalarType == glu::TYPE_INT) ||
2126 (!uintTypes && scalarType == glu::TYPE_UINT))
2129 for (int vecSize = 1; vecSize <= 4; vecSize++)
2131 for (int prec = glu::PRECISION_LOWP; prec <= glu::PRECISION_HIGHP; prec++)
2133 for (int shaderTypeNdx = 0; shaderTypeNdx < glu::SHADERTYPE_LAST; shaderTypeNdx++)
2135 if (shaderBits & (1<<shaderTypeNdx))
2136 group->addChild(new TestClass(parent->getContext(), glu::DataType(scalarType + vecSize - 1), glu::Precision(prec), glu::ShaderType(shaderTypeNdx)));
2143 void ShaderCommonFunctionTests::init (void)
2147 VS = (1<<glu::SHADERTYPE_VERTEX),
2148 TC = (1<<glu::SHADERTYPE_TESSELLATION_CONTROL),
2149 TE = (1<<glu::SHADERTYPE_TESSELLATION_EVALUATION),
2150 GS = (1<<glu::SHADERTYPE_GEOMETRY),
2151 FS = (1<<glu::SHADERTYPE_FRAGMENT),
2152 CS = (1<<glu::SHADERTYPE_COMPUTE),
2154 ALL_SHADERS = VS|TC|TE|GS|FS|CS,
2155 NEW_SHADERS = TC|TE|GS|CS,
2158 // Float? Int? Uint? Shaders
2159 addFunctionCases<AbsCase> (this, "abs", true, true, false, NEW_SHADERS);
2160 addFunctionCases<SignCase> (this, "sign", true, true, false, NEW_SHADERS);
2161 addFunctionCases<FloorCase> (this, "floor", true, false, false, NEW_SHADERS);
2162 addFunctionCases<TruncCase> (this, "trunc", true, false, false, NEW_SHADERS);
2163 addFunctionCases<RoundCase> (this, "round", true, false, false, NEW_SHADERS);
2164 addFunctionCases<RoundEvenCase> (this, "roundeven", true, false, false, NEW_SHADERS);
2165 addFunctionCases<CeilCase> (this, "ceil", true, false, false, NEW_SHADERS);
2166 addFunctionCases<FractCase> (this, "fract", true, false, false, NEW_SHADERS);
2168 addFunctionCases<ModfCase> (this, "modf", true, false, false, NEW_SHADERS);
2175 addFunctionCases<IsnanCase> (this, "isnan", true, false, false, NEW_SHADERS);
2176 addFunctionCases<IsinfCase> (this, "isinf", true, false, false, NEW_SHADERS);
2177 addFunctionCases<FloatBitsToIntCase> (this, "floatbitstoint", true, false, false, NEW_SHADERS);
2178 addFunctionCases<FloatBitsToUintCase> (this, "floatbitstouint", true, false, false, NEW_SHADERS);
2180 addFunctionCases<FrexpCase> (this, "frexp", true, false, false, ALL_SHADERS);
2181 addFunctionCases<LdexpCase> (this, "ldexp", true, false, false, ALL_SHADERS);
2182 addFunctionCases<FmaCase> (this, "fma", true, false, false, ALL_SHADERS);
2184 // (u)intBitsToFloat()
2186 const deUint32 shaderBits = NEW_SHADERS;
2187 tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "intbitstofloat", "intBitsToFloat() Tests");
2188 tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat", "uintBitsToFloat() Tests");
2191 addChild(uintGroup);
2193 for (int vecSize = 1; vecSize < 4; vecSize++)
2195 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
2196 const glu::DataType uintType = vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT;
2198 for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
2200 if (shaderBits & (1<<shaderType))
2202 intGroup->addChild(new BitsToFloatCase(m_context, intType, glu::ShaderType(shaderType)));
2203 uintGroup->addChild(new BitsToFloatCase(m_context, uintType, glu::ShaderType(shaderType)));