xref: /aosp_15_r20/external/deqp/modules/gles31/functional/es31fShaderCommonFunctionTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.1 Module
3  * -------------------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  *
19  *//*!
20  * \file
21  * \brief Common built-in function tests.
22  *//*--------------------------------------------------------------------*/
23 
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"
34 #include "deMath.h"
35 #include "deString.h"
36 #include "deArrayUtil.hpp"
37 
38 namespace deqp
39 {
40 namespace gles31
41 {
42 namespace Functional
43 {
44 
45 using std::string;
46 using std::vector;
47 using tcu::TestLog;
48 using namespace gls::ShaderExecUtil;
49 
50 using tcu::IVec2;
51 using tcu::IVec3;
52 using tcu::IVec4;
53 using tcu::Vec2;
54 using tcu::Vec3;
55 using tcu::Vec4;
56 
57 // Utilities
58 
59 template <typename T, int Size>
60 struct VecArrayAccess
61 {
62 public:
VecArrayAccessdeqp::gles31::Functional::VecArrayAccess63     VecArrayAccess(const void *ptr) : m_array((tcu::Vector<T, Size> *)ptr)
64     {
65     }
~VecArrayAccessdeqp::gles31::Functional::VecArrayAccess66     ~VecArrayAccess(void)
67     {
68     }
69 
operator []deqp::gles31::Functional::VecArrayAccess70     const tcu::Vector<T, Size> &operator[](size_t offset) const
71     {
72         return m_array[offset];
73     }
operator []deqp::gles31::Functional::VecArrayAccess74     tcu::Vector<T, Size> &operator[](size_t offset)
75     {
76         return m_array[offset];
77     }
78 
79 private:
80     tcu::Vector<T, Size> *m_array;
81 };
82 
83 template <typename T, int Size>
fillRandomVectors(de::Random & rnd,const tcu::Vector<T,Size> & minValue,const tcu::Vector<T,Size> & maxValue,void * dst,int numValues,int offset=0)84 static void fillRandomVectors(de::Random &rnd, const tcu::Vector<T, Size> &minValue,
85                               const tcu::Vector<T, Size> &maxValue, void *dst, int numValues, int offset = 0)
86 {
87     VecArrayAccess<T, Size> access(dst);
88     for (int ndx = 0; ndx < numValues; ndx++)
89         access[offset + ndx] = tcu::randomVector<T, Size>(rnd, minValue, maxValue);
90 }
91 
92 template <typename T>
fillRandomScalars(de::Random & rnd,T minValue,T maxValue,void * dst,int numValues,int offset=0)93 static void fillRandomScalars(de::Random &rnd, T minValue, T maxValue, void *dst, int numValues, int offset = 0)
94 {
95     T *typedPtr = (T *)dst;
96     for (int ndx = 0; ndx < numValues; ndx++)
97         typedPtr[offset + ndx] = de::randomScalar<T>(rnd, minValue, maxValue);
98 }
99 
numBitsLostInOp(float input,float output)100 inline int numBitsLostInOp(float input, float output)
101 {
102     const int inExp  = tcu::Float32(input).exponent();
103     const int outExp = tcu::Float32(output).exponent();
104 
105     return de::max(0, inExp - outExp); // Lost due to mantissa shift.
106 }
107 
getUlpDiff(float a,float b)108 inline uint32_t getUlpDiff(float a, float b)
109 {
110     const uint32_t aBits = tcu::Float32(a).bits();
111     const uint32_t bBits = tcu::Float32(b).bits();
112     return aBits > bBits ? aBits - bBits : bBits - aBits;
113 }
114 
getUlpDiffIgnoreZeroSign(float a,float b)115 inline uint32_t getUlpDiffIgnoreZeroSign(float a, float b)
116 {
117     if (tcu::Float32(a).isZero())
118         return getUlpDiff(tcu::Float32::construct(tcu::Float32(b).sign(), 0, 0).asFloat(), b);
119     else if (tcu::Float32(b).isZero())
120         return getUlpDiff(a, tcu::Float32::construct(tcu::Float32(a).sign(), 0, 0).asFloat());
121     else
122         return getUlpDiff(a, b);
123 }
124 
supportsSignedZero(glu::Precision precision)125 inline bool supportsSignedZero(glu::Precision precision)
126 {
127     // \note GLSL ES 3.1 doesn't really require support for -0, but we require it for highp
128     //         as it is very widely supported.
129     return precision == glu::PRECISION_HIGHP;
130 }
131 
getEpsFromMaxUlpDiff(float value,uint32_t ulpDiff)132 inline float getEpsFromMaxUlpDiff(float value, uint32_t ulpDiff)
133 {
134     const int exp = tcu::Float32(value).exponent();
135     return tcu::Float32::construct(+1, exp, (1u << 23) | ulpDiff).asFloat() -
136            tcu::Float32::construct(+1, exp, 1u << 23).asFloat();
137 }
138 
getMaxUlpDiffFromBits(int numAccurateBits)139 inline uint32_t getMaxUlpDiffFromBits(int numAccurateBits)
140 {
141     const int numGarbageBits = 23 - numAccurateBits;
142     const uint32_t mask      = (1u << numGarbageBits) - 1u;
143 
144     return mask;
145 }
146 
getEpsFromBits(float value,int numAccurateBits)147 inline float getEpsFromBits(float value, int numAccurateBits)
148 {
149     return getEpsFromMaxUlpDiff(value, getMaxUlpDiffFromBits(numAccurateBits));
150 }
151 
getMinMantissaBits(glu::Precision precision)152 static int getMinMantissaBits(glu::Precision precision)
153 {
154     const int bits[] = {
155         7,  // lowp
156         10, // mediump
157         23  // highp
158     };
159     DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(bits) == glu::PRECISION_LAST);
160     DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(bits)));
161     return bits[precision];
162 }
163 
getMaxNormalizedValueExponent(glu::Precision precision)164 static int getMaxNormalizedValueExponent(glu::Precision precision)
165 {
166     const int exponent[] = {
167         0,  // lowp
168         13, // mediump
169         127 // highp
170     };
171     DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(exponent) == glu::PRECISION_LAST);
172     DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(exponent)));
173     return exponent[precision];
174 }
175 
getMinNormalizedValueExponent(glu::Precision precision)176 static int getMinNormalizedValueExponent(glu::Precision precision)
177 {
178     const int exponent[] = {
179         -7,  // lowp
180         -13, // mediump
181         -126 // highp
182     };
183     DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(exponent) == glu::PRECISION_LAST);
184     DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(exponent)));
185     return exponent[precision];
186 }
187 
makeFloatRepresentable(float f,glu::Precision precision)188 static float makeFloatRepresentable(float f, glu::Precision precision)
189 {
190     if (precision == glu::PRECISION_HIGHP)
191     {
192         // \note: assuming f is not extended-precision
193         return f;
194     }
195     else
196     {
197         const int numMantissaBits                = getMinMantissaBits(precision);
198         const int maxNormalizedValueExponent     = getMaxNormalizedValueExponent(precision);
199         const int minNormalizedValueExponent     = getMinNormalizedValueExponent(precision);
200         const uint32_t representableMantissaMask = ((uint32_t(1) << numMantissaBits) - 1)
201                                                    << (23 - (uint32_t)numMantissaBits);
202         const float largestRepresentableValue =
203             tcu::Float32::constructBits(+1, maxNormalizedValueExponent,
204                                         ((1u << numMantissaBits) - 1u) << (23u - (uint32_t)numMantissaBits))
205                 .asFloat();
206         const bool zeroNotRepresentable = (precision == glu::PRECISION_LOWP);
207 
208         // if zero is not required to be representable, use smallest positive non-subnormal value
209         const float zeroValue = (zeroNotRepresentable) ?
210                                     (tcu::Float32::constructBits(+1, minNormalizedValueExponent, 1).asFloat()) :
211                                     (0.0f);
212 
213         const tcu::Float32 float32Representation(f);
214 
215         if (float32Representation.exponent() < minNormalizedValueExponent)
216         {
217             // flush too small values to zero
218             return zeroValue;
219         }
220         else if (float32Representation.exponent() > maxNormalizedValueExponent)
221         {
222             // clamp too large values
223             return (float32Representation.sign() == +1) ? (largestRepresentableValue) : (-largestRepresentableValue);
224         }
225         else
226         {
227             // remove unrepresentable mantissa bits
228             const tcu::Float32 targetRepresentation(
229                 tcu::Float32::constructBits(float32Representation.sign(), float32Representation.exponent(),
230                                             float32Representation.mantissaBits() & representableMantissaMask));
231 
232             return targetRepresentation.asFloat();
233         }
234     }
235 }
236 
237 // CommonFunctionCase
238 
239 class CommonFunctionCase : public TestCase
240 {
241 public:
242     CommonFunctionCase(Context &context, const char *name, const char *description, glu::ShaderType shaderType);
243     ~CommonFunctionCase(void);
244 
245     void init(void);
246     void deinit(void);
247     IterateResult iterate(void);
248 
249 protected:
250     CommonFunctionCase(const CommonFunctionCase &other);
251     CommonFunctionCase &operator=(const CommonFunctionCase &other);
252 
253     virtual void getInputValues(int numValues, void *const *values) const       = 0;
254     virtual bool compare(const void *const *inputs, const void *const *outputs) = 0;
255 
256     glu::ShaderType m_shaderType;
257     ShaderSpec m_spec;
258     int m_numValues;
259 
260     std::ostringstream m_failMsg; //!< Comparison failure help message.
261 
262 private:
263     ShaderExecutor *m_executor;
264 };
265 
CommonFunctionCase(Context & context,const char * name,const char * description,glu::ShaderType shaderType)266 CommonFunctionCase::CommonFunctionCase(Context &context, const char *name, const char *description,
267                                        glu::ShaderType shaderType)
268     : TestCase(context, name, description)
269     , m_shaderType(shaderType)
270     , m_numValues(100)
271     , m_executor(DE_NULL)
272 {
273 }
274 
~CommonFunctionCase(void)275 CommonFunctionCase::~CommonFunctionCase(void)
276 {
277     CommonFunctionCase::deinit();
278 }
279 
init(void)280 void CommonFunctionCase::init(void)
281 {
282     DE_ASSERT(!m_executor);
283 
284     glu::ContextType contextType = m_context.getRenderContext().getType();
285     m_spec.version               = glu::getContextTypeGLSLVersion(contextType);
286 
287     m_executor = createExecutor(m_context.getRenderContext(), m_shaderType, m_spec);
288     m_testCtx.getLog() << m_executor;
289 
290     if (!m_executor->isOk())
291         throw tcu::TestError("Compile failed");
292 }
293 
deinit(void)294 void CommonFunctionCase::deinit(void)
295 {
296     delete m_executor;
297     m_executor = DE_NULL;
298 }
299 
getScalarSizes(const vector<Symbol> & symbols)300 static vector<int> getScalarSizes(const vector<Symbol> &symbols)
301 {
302     vector<int> sizes(symbols.size());
303     for (int ndx = 0; ndx < (int)symbols.size(); ++ndx)
304         sizes[ndx] = symbols[ndx].varType.getScalarSize();
305     return sizes;
306 }
307 
computeTotalScalarSize(const vector<Symbol> & symbols)308 static int computeTotalScalarSize(const vector<Symbol> &symbols)
309 {
310     int totalSize = 0;
311     for (vector<Symbol>::const_iterator sym = symbols.begin(); sym != symbols.end(); ++sym)
312         totalSize += sym->varType.getScalarSize();
313     return totalSize;
314 }
315 
getInputOutputPointers(const vector<Symbol> & symbols,vector<uint32_t> & data,const int numValues)316 static vector<void *> getInputOutputPointers(const vector<Symbol> &symbols, vector<uint32_t> &data, const int numValues)
317 {
318     vector<void *> pointers(symbols.size());
319     int curScalarOffset = 0;
320 
321     for (int varNdx = 0; varNdx < (int)symbols.size(); ++varNdx)
322     {
323         const Symbol &var    = symbols[varNdx];
324         const int scalarSize = var.varType.getScalarSize();
325 
326         // Uses planar layout as input/output specs do not support strides.
327         pointers[varNdx] = &data[curScalarOffset];
328         curScalarOffset += scalarSize * numValues;
329     }
330 
331     DE_ASSERT(curScalarOffset == (int)data.size());
332 
333     return pointers;
334 }
335 
336 // \todo [2013-08-08 pyry] Make generic utility and move to glu?
337 
338 struct HexFloat
339 {
340     const float value;
HexFloatdeqp::gles31::Functional::HexFloat341     HexFloat(const float value_) : value(value_)
342     {
343     }
344 };
345 
operator <<(std::ostream & str,const HexFloat & v)346 std::ostream &operator<<(std::ostream &str, const HexFloat &v)
347 {
348     return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
349 }
350 
351 struct HexBool
352 {
353     const uint32_t value;
HexBooldeqp::gles31::Functional::HexBool354     HexBool(const uint32_t value_) : value(value_)
355     {
356     }
357 };
358 
operator <<(std::ostream & str,const HexBool & v)359 std::ostream &operator<<(std::ostream &str, const HexBool &v)
360 {
361     return str << (v.value ? "true" : "false") << " / " << tcu::toHex(v.value);
362 }
363 
364 struct VarValue
365 {
366     const glu::VarType &type;
367     const void *value;
368 
VarValuedeqp::gles31::Functional::VarValue369     VarValue(const glu::VarType &type_, const void *value_) : type(type_), value(value_)
370     {
371     }
372 };
373 
operator <<(std::ostream & str,const VarValue & varValue)374 std::ostream &operator<<(std::ostream &str, const VarValue &varValue)
375 {
376     DE_ASSERT(varValue.type.isBasicType());
377 
378     const glu::DataType basicType  = varValue.type.getBasicType();
379     const glu::DataType scalarType = glu::getDataTypeScalarType(basicType);
380     const int numComponents        = glu::getDataTypeScalarSize(basicType);
381 
382     if (numComponents > 1)
383         str << glu::getDataTypeName(basicType) << "(";
384 
385     for (int compNdx = 0; compNdx < numComponents; compNdx++)
386     {
387         if (compNdx != 0)
388             str << ", ";
389 
390         switch (scalarType)
391         {
392         case glu::TYPE_FLOAT:
393             str << HexFloat(((const float *)varValue.value)[compNdx]);
394             break;
395         case glu::TYPE_INT:
396             str << ((const int32_t *)varValue.value)[compNdx];
397             break;
398         case glu::TYPE_UINT:
399             str << tcu::toHex(((const uint32_t *)varValue.value)[compNdx]);
400             break;
401         case glu::TYPE_BOOL:
402             str << HexBool(((const uint32_t *)varValue.value)[compNdx]);
403             break;
404 
405         default:
406             DE_ASSERT(false);
407         }
408     }
409 
410     if (numComponents > 1)
411         str << ")";
412 
413     return str;
414 }
415 
iterate(void)416 CommonFunctionCase::IterateResult CommonFunctionCase::iterate(void)
417 {
418     const int numInputScalars  = computeTotalScalarSize(m_spec.inputs);
419     const int numOutputScalars = computeTotalScalarSize(m_spec.outputs);
420     vector<uint32_t> inputData(numInputScalars * m_numValues);
421     vector<uint32_t> outputData(numOutputScalars * m_numValues);
422     const vector<void *> inputPointers  = getInputOutputPointers(m_spec.inputs, inputData, m_numValues);
423     const vector<void *> outputPointers = getInputOutputPointers(m_spec.outputs, outputData, m_numValues);
424 
425     // Initialize input data.
426     getInputValues(m_numValues, &inputPointers[0]);
427 
428     // Execute shader.
429     m_executor->useProgram();
430     m_executor->execute(m_numValues, &inputPointers[0], &outputPointers[0]);
431 
432     // Compare results.
433     {
434         const vector<int> inScalarSizes  = getScalarSizes(m_spec.inputs);
435         const vector<int> outScalarSizes = getScalarSizes(m_spec.outputs);
436         vector<void *> curInputPtr(inputPointers.size());
437         vector<void *> curOutputPtr(outputPointers.size());
438         int numFailed = 0;
439 
440         for (int valNdx = 0; valNdx < m_numValues; valNdx++)
441         {
442             // Set up pointers for comparison.
443             for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx)
444                 curInputPtr[inNdx] = (uint32_t *)inputPointers[inNdx] + inScalarSizes[inNdx] * valNdx;
445 
446             for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx)
447                 curOutputPtr[outNdx] = (uint32_t *)outputPointers[outNdx] + outScalarSizes[outNdx] * valNdx;
448 
449             if (!compare(&curInputPtr[0], &curOutputPtr[0]))
450             {
451                 // \todo [2013-08-08 pyry] We probably want to log reference value as well?
452 
453                 m_testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n  "
454                                    << m_failMsg.str() << TestLog::EndMessage;
455 
456                 m_testCtx.getLog() << TestLog::Message << "  inputs:" << TestLog::EndMessage;
457                 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++)
458                     m_testCtx.getLog() << TestLog::Message << "    " << m_spec.inputs[inNdx].name << " = "
459                                        << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx])
460                                        << TestLog::EndMessage;
461 
462                 m_testCtx.getLog() << TestLog::Message << "  outputs:" << TestLog::EndMessage;
463                 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++)
464                     m_testCtx.getLog() << TestLog::Message << "    " << m_spec.outputs[outNdx].name << " = "
465                                        << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx])
466                                        << TestLog::EndMessage;
467 
468                 m_failMsg.str("");
469                 m_failMsg.clear();
470                 numFailed += 1;
471             }
472         }
473 
474         m_testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed"
475                            << TestLog::EndMessage;
476 
477         m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
478                                 numFailed == 0 ? "Pass" : "Result comparison failed");
479     }
480 
481     return STOP;
482 }
483 
getCommonFuncCaseName(glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)484 static std::string getCommonFuncCaseName(glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
485 {
486     return string(glu::getDataTypeName(baseType)) + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType);
487 }
488 
489 class AbsCase : public CommonFunctionCase
490 {
491 public:
AbsCase(Context & context,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)492     AbsCase(Context &context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
493         : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "abs", shaderType)
494     {
495         m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
496         m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
497         m_spec.source = "out0 = abs(in0);";
498     }
499 
getInputValues(int numValues,void * const * values) const500     void getInputValues(int numValues, void *const *values) const
501     {
502         const Vec2 floatRanges[] = {
503             Vec2(-2.0f, 2.0f), // lowp
504             Vec2(-1e3f, 1e3f), // mediump
505             Vec2(-1e7f, 1e7f)  // highp
506         };
507         const IVec2 intRanges[] = {IVec2(-(1 << 7) + 1, (1 << 7) - 1), IVec2(-(1 << 15) + 1, (1 << 15) - 1),
508                                    IVec2(0x80000001, 0x7fffffff)};
509 
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);
514 
515         if (glu::isDataTypeFloatOrVec(type))
516             fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), values[0],
517                               numValues * scalarSize);
518         else
519             fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0],
520                               numValues * scalarSize);
521     }
522 
compare(const void * const * inputs,const void * const * outputs)523     bool compare(const void *const *inputs, const void *const *outputs)
524     {
525         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
526         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
527         const int scalarSize           = glu::getDataTypeScalarSize(type);
528 
529         if (glu::isDataTypeFloatOrVec(type))
530         {
531             const int mantissaBits    = getMinMantissaBits(precision);
532             const uint32_t maxUlpDiff = (1u << (23 - mantissaBits)) - 1u;
533 
534             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
535             {
536                 const float in0         = ((const float *)inputs[0])[compNdx];
537                 const float out0        = ((const float *)outputs[0])[compNdx];
538                 const float ref0        = de::abs(in0);
539                 const uint32_t ulpDiff0 = getUlpDiff(out0, ref0);
540 
541                 if (ulpDiff0 > maxUlpDiff)
542                 {
543                     m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold "
544                               << maxUlpDiff << ", got ULP diff " << ulpDiff0;
545                     return false;
546                 }
547             }
548         }
549         else
550         {
551             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
552             {
553                 const int in0  = ((const int *)inputs[0])[compNdx];
554                 const int out0 = ((const int *)outputs[0])[compNdx];
555                 const int ref0 = de::abs(in0);
556 
557                 if (out0 != ref0)
558                 {
559                     m_failMsg << "Expected [" << compNdx << "] = " << ref0;
560                     return false;
561                 }
562             }
563         }
564 
565         return true;
566     }
567 };
568 
569 class SignCase : public CommonFunctionCase
570 {
571 public:
SignCase(Context & context,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)572     SignCase(Context &context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
573         : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "sign",
574                              shaderType)
575     {
576         m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
577         m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
578         m_spec.source = "out0 = sign(in0);";
579     }
580 
getInputValues(int numValues,void * const * values) const581     void getInputValues(int numValues, void *const *values) const
582     {
583         const Vec2 floatRanges[] = {
584             Vec2(-2.0f, 2.0f), // lowp
585             Vec2(-1e4f, 1e4f), // mediump    - note: may end up as inf
586             Vec2(-1e8f, 1e8f)  // highp    - note: may end up as inf
587         };
588         const IVec2 intRanges[] = {IVec2(-(1 << 7), (1 << 7) - 1), IVec2(-(1 << 15), (1 << 15) - 1),
589                                    IVec2(0x80000000, 0x7fffffff)};
590 
591         de::Random rnd(deStringHash(getName()) ^ 0x324u);
592         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
593         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
594         const int scalarSize           = glu::getDataTypeScalarSize(type);
595 
596         if (glu::isDataTypeFloatOrVec(type))
597         {
598             // Special cases.
599             std::fill((float *)values[0], (float *)values[0] + scalarSize, +1.0f);
600             std::fill((float *)values[0] + scalarSize * 1, (float *)values[0] + scalarSize * 2, -1.0f);
601             std::fill((float *)values[0] + scalarSize * 2, (float *)values[0] + scalarSize * 3, 0.0f);
602             fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(),
603                               (float *)values[0] + scalarSize * 3, (numValues - 3) * scalarSize);
604         }
605         else
606         {
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(),
611                               (int *)values[0] + scalarSize * 3, (numValues - 3) * scalarSize);
612         }
613     }
614 
compare(const void * const * inputs,const void * const * outputs)615     bool compare(const void *const *inputs, const void *const *outputs)
616     {
617         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
618         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
619         const int scalarSize           = glu::getDataTypeScalarSize(type);
620 
621         if (glu::isDataTypeFloatOrVec(type))
622         {
623             // Both highp and mediump should be able to represent -1, 0, and +1 exactly
624             const uint32_t maxUlpDiff =
625                 precision == glu::PRECISION_LOWP ? getMaxUlpDiffFromBits(getMinMantissaBits(precision)) : 0;
626 
627             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
628             {
629                 const float in0         = ((const float *)inputs[0])[compNdx];
630                 const float out0        = ((const float *)outputs[0])[compNdx];
631                 const float ref0        = in0 < 0.0f ? -1.0f : in0 > 0.0f ? +1.0f : 0.0f;
632                 const uint32_t ulpDiff0 = getUlpDiff(out0, ref0);
633 
634                 if (ulpDiff0 > maxUlpDiff)
635                 {
636                     m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold "
637                               << maxUlpDiff << ", got ULP diff " << ulpDiff0;
638                     return false;
639                 }
640             }
641         }
642         else
643         {
644             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
645             {
646                 const int in0  = ((const int *)inputs[0])[compNdx];
647                 const int out0 = ((const int *)outputs[0])[compNdx];
648                 const int ref0 = in0 < 0 ? -1 : in0 > 0 ? +1 : 0;
649 
650                 if (out0 != ref0)
651                 {
652                     m_failMsg << "Expected [" << compNdx << "] = " << ref0;
653                     return false;
654                 }
655             }
656         }
657 
658         return true;
659     }
660 };
661 
roundEven(float v)662 static float roundEven(float v)
663 {
664     const float q       = deFloatFrac(v);
665     const int truncated = int(v - q);
666     const int        rounded = (q > 0.5f)                            ? (truncated + 1) : // Rounded up
667                                     (q == 0.5f && (truncated % 2 != 0))    ? (truncated + 1) : // Round to nearest even at 0.5
668                                     truncated;                                                // Rounded down
669 
670     return float(rounded);
671 }
672 
673 class RoundEvenCase : public CommonFunctionCase
674 {
675 public:
RoundEvenCase(Context & context,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)676     RoundEvenCase(Context &context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
677         : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "roundEven",
678                              shaderType)
679     {
680         m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
681         m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
682         m_spec.source = "out0 = roundEven(in0);";
683     }
684 
getInputValues(int numValues,void * const * values) const685     void getInputValues(int numValues, void *const *values) const
686     {
687         const Vec2 ranges[] = {
688             Vec2(-2.0f, 2.0f), // lowp
689             Vec2(-1e3f, 1e3f), // mediump
690             Vec2(-1e7f, 1e7f)  // highp
691         };
692 
693         de::Random rnd(deStringHash(getName()) ^ 0xac23fu);
694         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
695         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
696         const int scalarSize           = glu::getDataTypeScalarSize(type);
697         int numSpecialCases            = 0;
698 
699         // Special cases.
700         if (precision != glu::PRECISION_LOWP)
701         {
702             DE_ASSERT(numValues >= 20);
703             for (int ndx = 0; ndx < 20; ndx++)
704             {
705                 const float v = de::clamp(float(ndx) - 10.5f, ranges[precision].x(), ranges[precision].y());
706                 std::fill((float *)values[0], (float *)values[0] + scalarSize, v);
707                 numSpecialCases += 1;
708             }
709         }
710 
711         // Random cases.
712         fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(),
713                           (float *)values[0] + numSpecialCases * scalarSize,
714                           (numValues - numSpecialCases) * scalarSize);
715 
716         // If precision is mediump, make sure values can be represented in fp16 exactly
717         if (precision == glu::PRECISION_MEDIUMP)
718         {
719             for (int ndx = 0; ndx < numValues * scalarSize; ndx++)
720                 ((float *)values[0])[ndx] = tcu::Float16(((float *)values[0])[ndx]).asFloat();
721         }
722     }
723 
compare(const void * const * inputs,const void * const * outputs)724     bool compare(const void *const *inputs, const void *const *outputs)
725     {
726         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
727         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
728         const bool hasSignedZero       = supportsSignedZero(precision);
729         const int scalarSize           = glu::getDataTypeScalarSize(type);
730 
731         if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
732         {
733             // Require exact rounding result.
734             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
735             {
736                 const float in0  = ((const float *)inputs[0])[compNdx];
737                 const float out0 = ((const float *)outputs[0])[compNdx];
738                 const float ref  = roundEven(in0);
739 
740                 const uint32_t ulpDiff = hasSignedZero ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
741 
742                 if (ulpDiff > 0)
743                 {
744                     m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff "
745                               << tcu::toHex(ulpDiff);
746                     return false;
747                 }
748             }
749         }
750         else
751         {
752             const int mantissaBits    = getMinMantissaBits(precision);
753             const uint32_t maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
754             const float eps           = getEpsFromBits(1.0f, mantissaBits);  // epsilon for rounding bounds
755 
756             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
757             {
758                 const float in0  = ((const float *)inputs[0])[compNdx];
759                 const float out0 = ((const float *)outputs[0])[compNdx];
760                 const int minRes = int(roundEven(in0 - eps));
761                 const int maxRes = int(roundEven(in0 + eps));
762                 bool anyOk       = false;
763 
764                 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
765                 {
766                     const uint32_t ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
767 
768                     if (ulpDiff <= maxUlpDiff)
769                     {
770                         anyOk = true;
771                         break;
772                     }
773                 }
774 
775                 if (!anyOk)
776                 {
777                     m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes
778                               << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
779                     return false;
780                 }
781             }
782         }
783 
784         return true;
785     }
786 };
787 
788 class ModfCase : public CommonFunctionCase
789 {
790 public:
ModfCase(Context & context,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)791     ModfCase(Context &context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
792         : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "modf",
793                              shaderType)
794     {
795         m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
796         m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
797         m_spec.outputs.push_back(Symbol("out1", glu::VarType(baseType, precision)));
798         m_spec.source = "out0 = modf(in0, out1);";
799     }
800 
getInputValues(int numValues,void * const * values) const801     void getInputValues(int numValues, void *const *values) const
802     {
803         const Vec2 ranges[] = {
804             Vec2(-2.0f, 2.0f), // lowp
805             Vec2(-1e3f, 1e3f), // mediump
806             Vec2(-1e7f, 1e7f)  // highp
807         };
808 
809         de::Random rnd(deStringHash(getName()) ^ 0xac23fu);
810         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
811         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
812         const int scalarSize           = glu::getDataTypeScalarSize(type);
813 
814         fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues * scalarSize);
815     }
816 
compare(const void * const * inputs,const void * const * outputs)817     bool compare(const void *const *inputs, const void *const *outputs)
818     {
819         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
820         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
821         const bool hasZeroSign         = supportsSignedZero(precision);
822         const int scalarSize           = glu::getDataTypeScalarSize(type);
823 
824         const int mantissaBits = getMinMantissaBits(precision);
825 
826         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
827         {
828             const float in0  = ((const float *)inputs[0])[compNdx];
829             const float out0 = ((const float *)outputs[0])[compNdx];
830             const float out1 = ((const float *)outputs[1])[compNdx];
831 
832             const float refOut1 = float(int(in0));
833             const float refOut0 = in0 - refOut1;
834 
835             const int bitsLost        = precision != glu::PRECISION_HIGHP ? numBitsLostInOp(in0, refOut0) : 0;
836             const uint32_t maxUlpDiff = getMaxUlpDiffFromBits(de::max(mantissaBits - bitsLost, 0));
837 
838             const float resSum = out0 + out1;
839 
840             const uint32_t ulpDiff = hasZeroSign ? getUlpDiff(resSum, in0) : getUlpDiffIgnoreZeroSign(resSum, in0);
841 
842             if (ulpDiff > maxUlpDiff)
843             {
844                 m_failMsg << "Expected [" << compNdx << "] = (" << HexFloat(refOut0) << ") + (" << HexFloat(refOut1)
845                           << ") = " << HexFloat(in0) << " with ULP threshold " << tcu::toHex(maxUlpDiff)
846                           << ", got ULP diff " << tcu::toHex(ulpDiff);
847                 return false;
848             }
849         }
850 
851         return true;
852     }
853 };
854 
855 class IsnanCase : public CommonFunctionCase
856 {
857 public:
IsnanCase(Context & context,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)858     IsnanCase(Context &context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
859         : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isnan",
860                              shaderType)
861     {
862         DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
863 
864         const int vecSize            = glu::getDataTypeScalarSize(baseType);
865         const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
866 
867         m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
868         m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
869         m_spec.source = "out0 = isnan(in0);";
870     }
871 
getInputValues(int numValues,void * const * values) const872     void getInputValues(int numValues, void *const *values) const
873     {
874         de::Random rnd(deStringHash(getName()) ^ 0xc2a39fu);
875         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
876         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
877         const int scalarSize           = glu::getDataTypeScalarSize(type);
878         const int mantissaBits         = getMinMantissaBits(precision);
879         const uint32_t mantissaMask    = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u << 23) - 1u);
880 
881         for (int valNdx = 0; valNdx < numValues * scalarSize; valNdx++)
882         {
883             const bool isNan        = rnd.getFloat() > 0.3f;
884             const bool isInf        = !isNan && rnd.getFloat() > 0.4f;
885             const uint32_t mantissa = !isInf ? ((1u << 22) | (rnd.getUint32() & mantissaMask)) : 0;
886             const uint32_t exp      = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
887             const uint32_t sign     = rnd.getUint32() & 0x1u;
888             const uint32_t value    = (sign << 31) | (exp << 23) | mantissa;
889 
890             DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
891 
892             ((uint32_t *)values[0])[valNdx] = value;
893         }
894     }
895 
compare(const void * const * inputs,const void * const * outputs)896     bool compare(const void *const *inputs, const void *const *outputs)
897     {
898         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
899         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
900         const int scalarSize           = glu::getDataTypeScalarSize(type);
901 
902         if (precision == glu::PRECISION_HIGHP)
903         {
904             // Only highp is required to support inf/nan
905             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
906             {
907                 const float in0 = ((const float *)inputs[0])[compNdx];
908                 const bool out0 = ((const uint32_t *)outputs[0])[compNdx] != 0;
909                 const bool ref  = tcu::Float32(in0).isNaN();
910 
911                 if (out0 != ref)
912                 {
913                     m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
914                     return false;
915                 }
916             }
917         }
918         else if (precision == glu::PRECISION_MEDIUMP || precision == glu::PRECISION_LOWP)
919         {
920             // NaN support is optional, check that inputs that are not NaN don't result in true.
921             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
922             {
923                 const float in0 = ((const float *)inputs[0])[compNdx];
924                 const bool out0 = ((const uint32_t *)outputs[0])[compNdx] != 0;
925                 const bool ref  = tcu::Float32(in0).isNaN();
926 
927                 if (!ref && out0)
928                 {
929                     m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
930                     return false;
931                 }
932             }
933         }
934 
935         return true;
936     }
937 };
938 
939 class IsinfCase : public CommonFunctionCase
940 {
941 public:
IsinfCase(Context & context,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)942     IsinfCase(Context &context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
943         : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isinf",
944                              shaderType)
945     {
946         DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
947 
948         const int vecSize            = glu::getDataTypeScalarSize(baseType);
949         const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
950 
951         m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
952         m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
953         m_spec.source = "out0 = isinf(in0);";
954     }
955 
getInputValues(int numValues,void * const * values) const956     void getInputValues(int numValues, void *const *values) const
957     {
958         de::Random rnd(deStringHash(getName()) ^ 0xc2a39fu);
959         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
960         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
961         const int scalarSize           = glu::getDataTypeScalarSize(type);
962         const int mantissaBits         = getMinMantissaBits(precision);
963         const uint32_t mantissaMask    = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u << 23) - 1u);
964 
965         for (int valNdx = 0; valNdx < numValues * scalarSize; valNdx++)
966         {
967             const bool isInf        = rnd.getFloat() > 0.3f;
968             const bool isNan        = !isInf && rnd.getFloat() > 0.4f;
969             const uint32_t mantissa = !isInf ? ((1u << 22) | (rnd.getUint32() & mantissaMask)) : 0;
970             const uint32_t exp      = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
971             const uint32_t sign     = rnd.getUint32() & 0x1u;
972             const uint32_t value    = (sign << 31) | (exp << 23) | mantissa;
973 
974             DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
975 
976             ((uint32_t *)values[0])[valNdx] = value;
977         }
978     }
979 
compare(const void * const * inputs,const void * const * outputs)980     bool compare(const void *const *inputs, const void *const *outputs)
981     {
982         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
983         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
984         const int scalarSize           = glu::getDataTypeScalarSize(type);
985 
986         if (precision == glu::PRECISION_HIGHP)
987         {
988             // Only highp is required to support inf/nan
989             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
990             {
991                 const float in0 = ((const float *)inputs[0])[compNdx];
992                 const bool out0 = ((const uint32_t *)outputs[0])[compNdx] != 0;
993                 const bool ref  = tcu::Float32(in0).isInf();
994 
995                 if (out0 != ref)
996                 {
997                     m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
998                     return false;
999                 }
1000             }
1001         }
1002         else if (precision == glu::PRECISION_MEDIUMP)
1003         {
1004             // Inf support is optional, check that inputs that are not Inf in mediump don't result in true.
1005             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1006             {
1007                 const float in0 = ((const float *)inputs[0])[compNdx];
1008                 const bool out0 = ((const uint32_t *)outputs[0])[compNdx] != 0;
1009                 const bool ref  = tcu::Float16(in0).isInf();
1010 
1011                 if (!ref && out0)
1012                 {
1013                     m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false");
1014                     return false;
1015                 }
1016             }
1017         }
1018         // else: no verification can be performed
1019 
1020         return true;
1021     }
1022 };
1023 
1024 class FloatBitsToUintIntCase : public CommonFunctionCase
1025 {
1026 public:
FloatBitsToUintIntCase(Context & context,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType,bool outIsSigned)1027     FloatBitsToUintIntCase(Context &context, glu::DataType baseType, glu::Precision precision,
1028                            glu::ShaderType shaderType, bool outIsSigned)
1029         : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(),
1030                              outIsSigned ? "floatBitsToInt" : "floatBitsToUint", shaderType)
1031     {
1032         const int vecSize           = glu::getDataTypeScalarSize(baseType);
1033         const glu::DataType intType = outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT) :
1034                                                     (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT);
1035 
1036         m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1037         m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP)));
1038         m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);";
1039     }
1040 
getInputValues(int numValues,void * const * values) const1041     void getInputValues(int numValues, void *const *values) const
1042     {
1043         const Vec2 ranges[] = {
1044             Vec2(-2.0f, 2.0f), // lowp
1045             Vec2(-1e3f, 1e3f), // mediump
1046             Vec2(-1e7f, 1e7f)  // highp
1047         };
1048 
1049         de::Random rnd(deStringHash(getName()) ^ 0x2790au);
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);
1053 
1054         fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues * scalarSize);
1055     }
1056 
compare(const void * const * inputs,const void * const * outputs)1057     bool compare(const void *const *inputs, const void *const *outputs)
1058     {
1059         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
1060         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1061         const int scalarSize           = glu::getDataTypeScalarSize(type);
1062 
1063         const int mantissaBits = getMinMantissaBits(precision);
1064         const int maxUlpDiff   = getMaxUlpDiffFromBits(mantissaBits);
1065 
1066         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1067         {
1068             const float in0        = ((const float *)inputs[0])[compNdx];
1069             const uint32_t out0    = ((const uint32_t *)outputs[0])[compNdx];
1070             const uint32_t refOut0 = tcu::Float32(in0).bits();
1071             const int ulpDiff      = de::abs((int)out0 - (int)refOut0);
1072 
1073             if (ulpDiff > maxUlpDiff)
1074             {
1075                 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold "
1076                           << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1077                 return false;
1078             }
1079         }
1080 
1081         return true;
1082     }
1083 };
1084 
1085 class FloatBitsToIntCase : public FloatBitsToUintIntCase
1086 {
1087 public:
FloatBitsToIntCase(Context & context,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1088     FloatBitsToIntCase(Context &context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1089         : FloatBitsToUintIntCase(context, baseType, precision, shaderType, true)
1090     {
1091     }
1092 };
1093 
1094 class FloatBitsToUintCase : public FloatBitsToUintIntCase
1095 {
1096 public:
FloatBitsToUintCase(Context & context,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1097     FloatBitsToUintCase(Context &context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1098         : FloatBitsToUintIntCase(context, baseType, precision, shaderType, false)
1099     {
1100     }
1101 };
1102 
1103 class BitsToFloatCase : public CommonFunctionCase
1104 {
1105 public:
BitsToFloatCase(Context & context,glu::DataType baseType,glu::ShaderType shaderType)1106     BitsToFloatCase(Context &context, glu::DataType baseType, glu::ShaderType shaderType)
1107         : CommonFunctionCase(context, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP, shaderType).c_str(),
1108                              glu::isDataTypeIntOrIVec(baseType) ? "intBitsToFloat" : "uintBitsToFloat", shaderType)
1109     {
1110         const bool inIsSigned         = glu::isDataTypeIntOrIVec(baseType);
1111         const int vecSize             = glu::getDataTypeScalarSize(baseType);
1112         const glu::DataType floatType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1113 
1114         m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1115         m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP)));
1116         m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);";
1117     }
1118 
getInputValues(int numValues,void * const * values) const1119     void getInputValues(int numValues, void *const *values) const
1120     {
1121         de::Random rnd(deStringHash(getName()) ^ 0xbbb225u);
1122         const glu::DataType type = m_spec.inputs[0].varType.getBasicType();
1123         const int scalarSize     = glu::getDataTypeScalarSize(type);
1124         const Vec2 range(-1e8f, +1e8f);
1125 
1126         // \note Filled as floats.
1127         fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues * scalarSize);
1128     }
1129 
compare(const void * const * inputs,const void * const * outputs)1130     bool compare(const void *const *inputs, const void *const *outputs)
1131     {
1132         const glu::DataType type  = m_spec.inputs[0].varType.getBasicType();
1133         const int scalarSize      = glu::getDataTypeScalarSize(type);
1134         const uint32_t maxUlpDiff = 0;
1135 
1136         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1137         {
1138             const float in0        = ((const float *)inputs[0])[compNdx];
1139             const float out0       = ((const float *)outputs[0])[compNdx];
1140             const uint32_t ulpDiff = getUlpDiff(in0, out0);
1141 
1142             if (ulpDiff > maxUlpDiff)
1143             {
1144                 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits())
1145                           << " with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got ULP diff "
1146                           << tcu::toHex(ulpDiff);
1147                 return false;
1148             }
1149         }
1150 
1151         return true;
1152     }
1153 };
1154 
1155 class FloorCase : public CommonFunctionCase
1156 {
1157 public:
FloorCase(Context & context,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1158     FloorCase(Context &context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1159         : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "floor",
1160                              shaderType)
1161     {
1162         m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1163         m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1164         m_spec.source = "out0 = floor(in0);";
1165     }
1166 
getInputValues(int numValues,void * const * values) const1167     void getInputValues(int numValues, void *const *values) const
1168     {
1169         const Vec2 ranges[] = {
1170             Vec2(-2.0f, 2.0f), // lowp
1171             Vec2(-1e3f, 1e3f), // mediump
1172             Vec2(-1e7f, 1e7f)  // highp
1173         };
1174 
1175         de::Random rnd(deStringHash(getName()) ^ 0xac23fu);
1176         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
1177         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1178         const int scalarSize           = glu::getDataTypeScalarSize(type);
1179         // Random cases.
1180         fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float *)values[0],
1181                           numValues * scalarSize);
1182 
1183         // If precision is mediump, make sure values can be represented in fp16 exactly
1184         if (precision == glu::PRECISION_MEDIUMP)
1185         {
1186             for (int ndx = 0; ndx < numValues * scalarSize; ndx++)
1187                 ((float *)values[0])[ndx] = tcu::Float16(((float *)values[0])[ndx]).asFloat();
1188         }
1189     }
1190 
compare(const void * const * inputs,const void * const * outputs)1191     bool compare(const void *const *inputs, const void *const *outputs)
1192     {
1193         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
1194         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1195         const int scalarSize           = glu::getDataTypeScalarSize(type);
1196 
1197         if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1198         {
1199             // Require exact result.
1200             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1201             {
1202                 const float in0  = ((const float *)inputs[0])[compNdx];
1203                 const float out0 = ((const float *)outputs[0])[compNdx];
1204                 const float ref  = deFloatFloor(in0);
1205 
1206                 const uint32_t ulpDiff = getUlpDiff(out0, ref);
1207 
1208                 if (ulpDiff > 0)
1209                 {
1210                     m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff "
1211                               << tcu::toHex(ulpDiff);
1212                     return false;
1213                 }
1214             }
1215         }
1216         else
1217         {
1218             const int mantissaBits    = getMinMantissaBits(precision);
1219             const uint32_t maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1220             const float eps           = getEpsFromBits(1.0f, mantissaBits);  // epsilon for rounding bounds
1221 
1222             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1223             {
1224                 const float in0  = ((const float *)inputs[0])[compNdx];
1225                 const float out0 = ((const float *)outputs[0])[compNdx];
1226                 const int minRes = int(deFloatFloor(in0 - eps));
1227                 const int maxRes = int(deFloatFloor(in0 + eps));
1228                 bool anyOk       = false;
1229 
1230                 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1231                 {
1232                     const uint32_t ulpDiff = getUlpDiff(out0, float(roundedVal));
1233 
1234                     if (ulpDiff <= maxUlpDiff)
1235                     {
1236                         anyOk = true;
1237                         break;
1238                     }
1239                 }
1240 
1241                 if (!anyOk)
1242                 {
1243                     m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes
1244                               << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1245                     return false;
1246                 }
1247             }
1248         }
1249 
1250         return true;
1251     }
1252 };
1253 
1254 class TruncCase : public CommonFunctionCase
1255 {
1256 public:
TruncCase(Context & context,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1257     TruncCase(Context &context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1258         : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "trunc",
1259                              shaderType)
1260     {
1261         m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1262         m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1263         m_spec.source = "out0 = trunc(in0);";
1264     }
1265 
getInputValues(int numValues,void * const * values) const1266     void getInputValues(int numValues, void *const *values) const
1267     {
1268         const Vec2 ranges[] = {
1269             Vec2(-2.0f, 2.0f), // lowp
1270             Vec2(-1e3f, 1e3f), // mediump
1271             Vec2(-1e7f, 1e7f)  // highp
1272         };
1273 
1274         de::Random rnd(deStringHash(getName()) ^ 0xac23fu);
1275         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
1276         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1277         const int scalarSize           = glu::getDataTypeScalarSize(type);
1278         const float specialCases[]     = {0.0f, -0.0f, -0.9f, 0.9f, 1.0f, -1.0f};
1279         const int numSpecialCases      = DE_LENGTH_OF_ARRAY(specialCases);
1280 
1281         // Special cases
1282         for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
1283         {
1284             for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1285                 ((float *)values[0])[caseNdx * scalarSize + scalarNdx] = specialCases[caseNdx];
1286         }
1287 
1288         // Random cases.
1289         fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(),
1290                           (float *)values[0] + scalarSize * numSpecialCases,
1291                           (numValues - numSpecialCases) * scalarSize);
1292 
1293         // If precision is mediump, make sure values can be represented in fp16 exactly
1294         if (precision == glu::PRECISION_MEDIUMP)
1295         {
1296             for (int ndx = 0; ndx < numValues * scalarSize; ndx++)
1297                 ((float *)values[0])[ndx] = tcu::Float16(((float *)values[0])[ndx]).asFloat();
1298         }
1299     }
1300 
compare(const void * const * inputs,const void * const * outputs)1301     bool compare(const void *const *inputs, const void *const *outputs)
1302     {
1303         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
1304         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1305         const int scalarSize           = glu::getDataTypeScalarSize(type);
1306 
1307         if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1308         {
1309             // Require exact result.
1310             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1311             {
1312                 const float in0  = ((const float *)inputs[0])[compNdx];
1313                 const float out0 = ((const float *)outputs[0])[compNdx];
1314                 const bool isNeg = tcu::Float32(in0).sign() < 0;
1315                 const float ref  = isNeg ? (-float(int(-in0))) : float(int(in0));
1316 
1317                 // \note: trunc() function definition is a bit broad on negative zeros. Ignore result sign if zero.
1318                 const uint32_t ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1319 
1320                 if (ulpDiff > 0)
1321                 {
1322                     m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff "
1323                               << tcu::toHex(ulpDiff);
1324                     return false;
1325                 }
1326             }
1327         }
1328         else
1329         {
1330             const int mantissaBits    = getMinMantissaBits(precision);
1331             const uint32_t maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1332             const float eps           = getEpsFromBits(1.0f, mantissaBits);  // epsilon for rounding bounds
1333 
1334             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1335             {
1336                 const float in0  = ((const float *)inputs[0])[compNdx];
1337                 const float out0 = ((const float *)outputs[0])[compNdx];
1338                 const int minRes = int(in0 - eps);
1339                 const int maxRes = int(in0 + eps);
1340                 bool anyOk       = false;
1341 
1342                 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1343                 {
1344                     const uint32_t ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1345 
1346                     if (ulpDiff <= maxUlpDiff)
1347                     {
1348                         anyOk = true;
1349                         break;
1350                     }
1351                 }
1352 
1353                 if (!anyOk)
1354                 {
1355                     m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes
1356                               << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1357                     return false;
1358                 }
1359             }
1360         }
1361 
1362         return true;
1363     }
1364 };
1365 
1366 class RoundCase : public CommonFunctionCase
1367 {
1368 public:
RoundCase(Context & context,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1369     RoundCase(Context &context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1370         : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "round",
1371                              shaderType)
1372     {
1373         m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1374         m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1375         m_spec.source = "out0 = round(in0);";
1376     }
1377 
getInputValues(int numValues,void * const * values) const1378     void getInputValues(int numValues, void *const *values) const
1379     {
1380         const Vec2 ranges[] = {
1381             Vec2(-2.0f, 2.0f), // lowp
1382             Vec2(-1e3f, 1e3f), // mediump
1383             Vec2(-1e7f, 1e7f)  // highp
1384         };
1385 
1386         de::Random rnd(deStringHash(getName()) ^ 0xac23fu);
1387         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
1388         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1389         const int scalarSize           = glu::getDataTypeScalarSize(type);
1390         int numSpecialCases            = 0;
1391 
1392         // Special cases.
1393         if (precision != glu::PRECISION_LOWP)
1394         {
1395             DE_ASSERT(numValues >= 10);
1396             for (int ndx = 0; ndx < 10; ndx++)
1397             {
1398                 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1399                 std::fill((float *)values[0], (float *)values[0] + scalarSize, v);
1400                 numSpecialCases += 1;
1401             }
1402         }
1403 
1404         // Random cases.
1405         fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(),
1406                           (float *)values[0] + numSpecialCases * scalarSize,
1407                           (numValues - numSpecialCases) * scalarSize);
1408 
1409         // If precision is mediump, make sure values can be represented in fp16 exactly
1410         if (precision == glu::PRECISION_MEDIUMP)
1411         {
1412             for (int ndx = 0; ndx < numValues * scalarSize; ndx++)
1413                 ((float *)values[0])[ndx] = tcu::Float16(((float *)values[0])[ndx]).asFloat();
1414         }
1415     }
1416 
compare(const void * const * inputs,const void * const * outputs)1417     bool compare(const void *const *inputs, const void *const *outputs)
1418     {
1419         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
1420         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1421         const bool hasZeroSign         = supportsSignedZero(precision);
1422         const int scalarSize           = glu::getDataTypeScalarSize(type);
1423 
1424         if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1425         {
1426             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1427             {
1428                 const float in0  = ((const float *)inputs[0])[compNdx];
1429                 const float out0 = ((const float *)outputs[0])[compNdx];
1430 
1431                 if (deFloatFrac(in0) == 0.5f)
1432                 {
1433                     // Allow both ceil(in) and floor(in)
1434                     const float ref0 = deFloatFloor(in0);
1435                     const float ref1 = deFloatCeil(in0);
1436                     const uint32_t ulpDiff0 =
1437                         hasZeroSign ? getUlpDiff(out0, ref0) : getUlpDiffIgnoreZeroSign(out0, ref0);
1438                     const uint32_t ulpDiff1 =
1439                         hasZeroSign ? getUlpDiff(out0, ref1) : getUlpDiffIgnoreZeroSign(out0, ref1);
1440 
1441                     if (ulpDiff0 > 0 && ulpDiff1 > 0)
1442                     {
1443                         m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " or " << HexFloat(ref1)
1444                                   << ", got ULP diff " << tcu::toHex(de::min(ulpDiff0, ulpDiff1));
1445                         return false;
1446                     }
1447                 }
1448                 else
1449                 {
1450                     // Require exact result
1451                     const float ref        = roundEven(in0);
1452                     const uint32_t ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1453 
1454                     if (ulpDiff > 0)
1455                     {
1456                         m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff "
1457                                   << tcu::toHex(ulpDiff);
1458                         return false;
1459                     }
1460                 }
1461             }
1462         }
1463         else
1464         {
1465             const int mantissaBits    = getMinMantissaBits(precision);
1466             const uint32_t maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1467             const float eps           = getEpsFromBits(1.0f, mantissaBits);  // epsilon for rounding bounds
1468 
1469             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1470             {
1471                 const float in0  = ((const float *)inputs[0])[compNdx];
1472                 const float out0 = ((const float *)outputs[0])[compNdx];
1473                 const int minRes = int(roundEven(in0 - eps));
1474                 const int maxRes = int(roundEven(in0 + eps));
1475                 bool anyOk       = false;
1476 
1477                 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1478                 {
1479                     const uint32_t ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1480 
1481                     if (ulpDiff <= maxUlpDiff)
1482                     {
1483                         anyOk = true;
1484                         break;
1485                     }
1486                 }
1487 
1488                 if (!anyOk)
1489                 {
1490                     m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes
1491                               << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1492                     return false;
1493                 }
1494             }
1495         }
1496 
1497         return true;
1498     }
1499 };
1500 
1501 class CeilCase : public CommonFunctionCase
1502 {
1503 public:
CeilCase(Context & context,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1504     CeilCase(Context &context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1505         : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ceil",
1506                              shaderType)
1507     {
1508         m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1509         m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1510         m_spec.source = "out0 = ceil(in0);";
1511     }
1512 
getInputValues(int numValues,void * const * values) const1513     void getInputValues(int numValues, void *const *values) const
1514     {
1515         const Vec2 ranges[] = {
1516             Vec2(-2.0f, 2.0f), // lowp
1517             Vec2(-1e3f, 1e3f), // mediump
1518             Vec2(-1e7f, 1e7f)  // highp
1519         };
1520 
1521         de::Random rnd(deStringHash(getName()) ^ 0xac23fu);
1522         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
1523         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1524         const int scalarSize           = glu::getDataTypeScalarSize(type);
1525 
1526         // Random cases.
1527         fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float *)values[0],
1528                           numValues * scalarSize);
1529 
1530         // If precision is mediump, make sure values can be represented in fp16 exactly
1531         if (precision == glu::PRECISION_MEDIUMP)
1532         {
1533             for (int ndx = 0; ndx < numValues * scalarSize; ndx++)
1534                 ((float *)values[0])[ndx] = tcu::Float16(((float *)values[0])[ndx]).asFloat();
1535         }
1536     }
1537 
compare(const void * const * inputs,const void * const * outputs)1538     bool compare(const void *const *inputs, const void *const *outputs)
1539     {
1540         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
1541         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1542         const bool hasZeroSign         = supportsSignedZero(precision);
1543         const int scalarSize           = glu::getDataTypeScalarSize(type);
1544 
1545         if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1546         {
1547             // Require exact result.
1548             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1549             {
1550                 const float in0  = ((const float *)inputs[0])[compNdx];
1551                 const float out0 = ((const float *)outputs[0])[compNdx];
1552                 const float ref  = deFloatCeil(in0);
1553 
1554                 const uint32_t ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1555 
1556                 if (ulpDiff > 0)
1557                 {
1558                     m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff "
1559                               << tcu::toHex(ulpDiff);
1560                     return false;
1561                 }
1562             }
1563         }
1564         else
1565         {
1566             const int mantissaBits    = getMinMantissaBits(precision);
1567             const uint32_t maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value.
1568             const float eps           = getEpsFromBits(1.0f, mantissaBits);  // epsilon for rounding bounds
1569 
1570             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1571             {
1572                 const float in0  = ((const float *)inputs[0])[compNdx];
1573                 const float out0 = ((const float *)outputs[0])[compNdx];
1574                 const int minRes = int(deFloatCeil(in0 - eps));
1575                 const int maxRes = int(deFloatCeil(in0 + eps));
1576                 bool anyOk       = false;
1577 
1578                 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1579                 {
1580                     const uint32_t ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1581 
1582                     if (ulpDiff <= maxUlpDiff)
1583                     {
1584                         anyOk = true;
1585                         break;
1586                     }
1587                 }
1588 
1589                 if (!anyOk && de::inRange(0, minRes, maxRes))
1590                 {
1591                     // Allow -0 as well.
1592                     const int ulpDiff = de::abs((int)tcu::Float32(out0).bits() - (int)0x80000000u);
1593                     anyOk             = ((uint32_t)ulpDiff <= maxUlpDiff);
1594                 }
1595 
1596                 if (!anyOk)
1597                 {
1598                     m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes
1599                               << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1600                     return false;
1601                 }
1602             }
1603         }
1604 
1605         return true;
1606     }
1607 };
1608 
1609 class FractCase : public CommonFunctionCase
1610 {
1611 public:
FractCase(Context & context,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1612     FractCase(Context &context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1613         : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fract",
1614                              shaderType)
1615     {
1616         m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1617         m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1618         m_spec.source = "out0 = fract(in0);";
1619     }
1620 
getInputValues(int numValues,void * const * values) const1621     void getInputValues(int numValues, void *const *values) const
1622     {
1623         const Vec2 ranges[] = {
1624             Vec2(-2.0f, 2.0f), // lowp
1625             Vec2(-1e3f, 1e3f), // mediump
1626             Vec2(-1e7f, 1e7f)  // highp
1627         };
1628 
1629         de::Random rnd(deStringHash(getName()) ^ 0xac23fu);
1630         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
1631         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1632         const int scalarSize           = glu::getDataTypeScalarSize(type);
1633         int numSpecialCases            = 0;
1634 
1635         // Special cases.
1636         if (precision != glu::PRECISION_LOWP)
1637         {
1638             DE_ASSERT(numValues >= 10);
1639             for (int ndx = 0; ndx < 10; ndx++)
1640             {
1641                 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1642                 std::fill((float *)values[0], (float *)values[0] + scalarSize, v);
1643                 numSpecialCases += 1;
1644             }
1645         }
1646 
1647         // Random cases.
1648         fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(),
1649                           (float *)values[0] + numSpecialCases * scalarSize,
1650                           (numValues - numSpecialCases) * scalarSize);
1651 
1652         // If precision is mediump, make sure values can be represented in fp16 exactly
1653         if (precision == glu::PRECISION_MEDIUMP)
1654         {
1655             for (int ndx = 0; ndx < numValues * scalarSize; ndx++)
1656                 ((float *)values[0])[ndx] = tcu::Float16(((float *)values[0])[ndx]).asFloat();
1657         }
1658     }
1659 
compare(const void * const * inputs,const void * const * outputs)1660     bool compare(const void *const *inputs, const void *const *outputs)
1661     {
1662         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
1663         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1664         const bool hasZeroSign         = supportsSignedZero(precision);
1665         const int scalarSize           = glu::getDataTypeScalarSize(type);
1666 
1667         if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1668         {
1669             // Require exact result.
1670             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1671             {
1672                 const float in0  = ((const float *)inputs[0])[compNdx];
1673                 const float out0 = ((const float *)outputs[0])[compNdx];
1674                 const float ref  = deFloatFrac(in0);
1675 
1676                 const uint32_t ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1677 
1678                 if (ulpDiff > 0)
1679                 {
1680                     m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff "
1681                               << tcu::toHex(ulpDiff);
1682                     return false;
1683                 }
1684             }
1685         }
1686         else
1687         {
1688             const int mantissaBits = getMinMantissaBits(precision);
1689             const float eps        = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds
1690 
1691             for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1692             {
1693                 const float in0  = ((const float *)inputs[0])[compNdx];
1694                 const float out0 = ((const float *)outputs[0])[compNdx];
1695 
1696                 if (int(deFloatFloor(in0 - eps)) == int(deFloatFloor(in0 + eps)))
1697                 {
1698                     const float ref           = deFloatFrac(in0);
1699                     const int bitsLost        = numBitsLostInOp(in0, ref);
1700                     const uint32_t maxUlpDiff = getMaxUlpDiffFromBits(
1701                         de::max(0, mantissaBits - bitsLost)); // ULP diff for rounded integer value.
1702                     const uint32_t ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref);
1703 
1704                     if (ulpDiff > maxUlpDiff)
1705                     {
1706                         m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << " with ULP threshold "
1707                                   << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1708                         return false;
1709                     }
1710                 }
1711                 else
1712                 {
1713                     if (out0 >= 1.0f)
1714                     {
1715                         m_failMsg << "Expected [" << compNdx << "] < 1.0";
1716                         return false;
1717                     }
1718                 }
1719             }
1720         }
1721 
1722         return true;
1723     }
1724 };
1725 
frexp(float in,float * significand,int * exponent)1726 static inline void frexp(float in, float *significand, int *exponent)
1727 {
1728     const tcu::Float32 fpValue(in);
1729 
1730     if (!fpValue.isZero())
1731     {
1732         // Construct float that has exactly the mantissa, and exponent of -1.
1733         *significand = tcu::Float32::construct(fpValue.sign(), -1, fpValue.mantissa()).asFloat();
1734         *exponent    = fpValue.exponent() + 1;
1735     }
1736     else
1737     {
1738         *significand = fpValue.sign() < 0 ? -0.0f : 0.0f;
1739         *exponent    = 0;
1740     }
1741 }
1742 
ldexp(float significand,int exponent)1743 static inline float ldexp(float significand, int exponent)
1744 {
1745     const tcu::Float32 mant(significand);
1746 
1747     if (exponent == 0 && mant.isZero())
1748     {
1749         return mant.sign() < 0 ? -0.0f : 0.0f;
1750     }
1751     else
1752     {
1753         return tcu::Float32::construct(mant.sign(), exponent + mant.exponent(), mant.mantissa()).asFloat();
1754     }
1755 }
1756 
1757 class FrexpCase : public CommonFunctionCase
1758 {
1759 public:
FrexpCase(Context & context,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1760     FrexpCase(Context &context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1761         : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "frexp",
1762                              shaderType)
1763     {
1764         const int vecSize           = glu::getDataTypeScalarSize(baseType);
1765         const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
1766 
1767         m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1768         m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1769         m_spec.outputs.push_back(Symbol("out1", glu::VarType(intType, glu::PRECISION_HIGHP)));
1770         m_spec.source = "out0 = frexp(in0, out1);";
1771     }
1772 
getInputValues(int numValues,void * const * values) const1773     void getInputValues(int numValues, void *const *values) const
1774     {
1775         const Vec2 ranges[] = {
1776             Vec2(-2.0f, 2.0f), // lowp
1777             Vec2(-1e3f, 1e3f), // mediump
1778             Vec2(-1e7f, 1e7f)  // highp
1779         };
1780 
1781         de::Random rnd(deStringHash(getName()) ^ 0x2790au);
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 
1786         // Special cases
1787         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1788         {
1789             ((float *)values[0])[scalarSize * 0 + compNdx] = 0.0f;
1790             ((float *)values[0])[scalarSize * 1 + compNdx] = -0.0f;
1791             ((float *)values[0])[scalarSize * 2 + compNdx] = 0.5f;
1792             ((float *)values[0])[scalarSize * 3 + compNdx] = -0.5f;
1793             ((float *)values[0])[scalarSize * 4 + compNdx] = 1.0f;
1794             ((float *)values[0])[scalarSize * 5 + compNdx] = -1.0f;
1795             ((float *)values[0])[scalarSize * 6 + compNdx] = 2.0f;
1796             ((float *)values[0])[scalarSize * 7 + compNdx] = -2.0f;
1797         }
1798 
1799         fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float *)values[0] + 8 * scalarSize,
1800                           (numValues - 8) * scalarSize);
1801 
1802         // Make sure the values are representable in the target format
1803         for (int caseNdx = 0; caseNdx < numValues; ++caseNdx)
1804         {
1805             for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1806             {
1807                 float *const valuePtr = &((float *)values[0])[caseNdx * scalarSize + scalarNdx];
1808 
1809                 *valuePtr = makeFloatRepresentable(*valuePtr, precision);
1810             }
1811         }
1812     }
1813 
compare(const void * const * inputs,const void * const * outputs)1814     bool compare(const void *const *inputs, const void *const *outputs)
1815     {
1816         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
1817         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1818         const int scalarSize           = glu::getDataTypeScalarSize(type);
1819         const bool signedZero          = false;
1820 
1821         const int mantissaBits    = getMinMantissaBits(precision);
1822         const uint32_t maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
1823 
1824         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1825         {
1826             const float in0  = ((const float *)inputs[0])[compNdx];
1827             const float out0 = ((const float *)outputs[0])[compNdx];
1828             const int out1   = ((const int *)outputs[1])[compNdx];
1829 
1830             float refOut0;
1831             int refOut1;
1832 
1833             frexp(in0, &refOut0, &refOut1);
1834 
1835             const uint32_t ulpDiff0 = signedZero ? getUlpDiff(out0, refOut0) : getUlpDiffIgnoreZeroSign(out0, refOut0);
1836 
1837             if (ulpDiff0 > maxUlpDiff || out1 != refOut1)
1838             {
1839                 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", " << refOut1
1840                           << " with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got ULP diff "
1841                           << tcu::toHex(ulpDiff0);
1842                 return false;
1843             }
1844         }
1845 
1846         return true;
1847     }
1848 };
1849 
1850 class LdexpCase : public CommonFunctionCase
1851 {
1852 public:
LdexpCase(Context & context,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1853     LdexpCase(Context &context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1854         : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ldexp",
1855                              shaderType)
1856     {
1857         const int vecSize           = glu::getDataTypeScalarSize(baseType);
1858         const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
1859 
1860         m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1861         m_spec.inputs.push_back(Symbol("in1", glu::VarType(intType, glu::PRECISION_HIGHP)));
1862         m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1863         m_spec.source = "out0 = ldexp(in0, in1);";
1864     }
1865 
getInputValues(int numValues,void * const * values) const1866     void getInputValues(int numValues, void *const *values) const
1867     {
1868         const Vec2 ranges[] = {
1869             Vec2(-2.0f, 2.0f), // lowp
1870             Vec2(-1e3f, 1e3f), // mediump
1871             Vec2(-1e7f, 1e7f)  // highp
1872         };
1873 
1874         de::Random rnd(deStringHash(getName()) ^ 0x2790au);
1875         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
1876         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1877         const int scalarSize           = glu::getDataTypeScalarSize(type);
1878         int valueNdx                   = 0;
1879 
1880         {
1881             const float easySpecialCases[] = {0.0f, -0.0f, 0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f};
1882 
1883             DE_ASSERT(valueNdx + DE_LENGTH_OF_ARRAY(easySpecialCases) <= numValues);
1884             for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(easySpecialCases); caseNdx++)
1885             {
1886                 float in0;
1887                 int in1;
1888 
1889                 frexp(easySpecialCases[caseNdx], &in0, &in1);
1890 
1891                 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1892                 {
1893                     ((float *)values[0])[valueNdx * scalarSize + compNdx] = in0;
1894                     ((int *)values[1])[valueNdx * scalarSize + compNdx]   = in1;
1895                 }
1896 
1897                 valueNdx += 1;
1898             }
1899         }
1900 
1901         {
1902             // \note lowp and mediump can not necessarily fit the values in hard cases, so we'll use only easy ones.
1903             const int numEasyRandomCases = precision == glu::PRECISION_HIGHP ? 50 : (numValues - valueNdx);
1904 
1905             DE_ASSERT(valueNdx + numEasyRandomCases <= numValues);
1906             for (int caseNdx = 0; caseNdx < numEasyRandomCases; caseNdx++)
1907             {
1908                 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1909                 {
1910                     const float in = rnd.getFloat(ranges[precision].x(), ranges[precision].y());
1911                     float in0;
1912                     int in1;
1913 
1914                     frexp(in, &in0, &in1);
1915 
1916                     ((float *)values[0])[valueNdx * scalarSize + compNdx] = in0;
1917                     ((int *)values[1])[valueNdx * scalarSize + compNdx]   = in1;
1918                 }
1919 
1920                 valueNdx += 1;
1921             }
1922         }
1923 
1924         {
1925             const int numHardRandomCases = numValues - valueNdx;
1926             DE_ASSERT(numHardRandomCases >= 0 && valueNdx + numHardRandomCases <= numValues);
1927 
1928             for (int caseNdx = 0; caseNdx < numHardRandomCases; caseNdx++)
1929             {
1930                 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1931                 {
1932                     const int fpExp         = rnd.getInt(-126, 127);
1933                     const int sign          = rnd.getBool() ? -1 : +1;
1934                     const uint32_t mantissa = (1u << 23) | (rnd.getUint32() & ((1u << 23) - 1));
1935                     const int in1           = rnd.getInt(de::max(-126, -126 - fpExp), de::min(127, 127 - fpExp));
1936                     const float in0         = tcu::Float32::construct(sign, fpExp, mantissa).asFloat();
1937 
1938                     DE_ASSERT(de::inRange(in1, -126, 127)); // See Khronos bug 11180
1939                     DE_ASSERT(de::inRange(in1 + fpExp, -126, 127));
1940 
1941                     const float out = ldexp(in0, in1);
1942 
1943                     DE_ASSERT(!tcu::Float32(out).isInf() && !tcu::Float32(out).isDenorm());
1944                     DE_UNREF(out);
1945 
1946                     ((float *)values[0])[valueNdx * scalarSize + compNdx] = in0;
1947                     ((int *)values[1])[valueNdx * scalarSize + compNdx]   = in1;
1948                 }
1949 
1950                 valueNdx += 1;
1951             }
1952         }
1953     }
1954 
compare(const void * const * inputs,const void * const * outputs)1955     bool compare(const void *const *inputs, const void *const *outputs)
1956     {
1957         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
1958         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
1959         const int scalarSize           = glu::getDataTypeScalarSize(type);
1960 
1961         const int mantissaBits    = getMinMantissaBits(precision);
1962         const uint32_t maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits);
1963 
1964         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1965         {
1966             const float in0        = ((const float *)inputs[0])[compNdx];
1967             const int in1          = ((const int *)inputs[1])[compNdx];
1968             const float out0       = ((const float *)outputs[0])[compNdx];
1969             const float refOut0    = ldexp(in0, in1);
1970             const uint32_t ulpDiff = getUlpDiffIgnoreZeroSign(out0, refOut0);
1971 
1972             const int inExp = tcu::Float32(in0).exponent();
1973 
1974             if (ulpDiff > maxUlpDiff)
1975             {
1976                 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", (exp = " << inExp
1977                           << ") with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got ULP diff "
1978                           << tcu::toHex(ulpDiff);
1979                 return false;
1980             }
1981         }
1982 
1983         return true;
1984     }
1985 };
1986 
1987 class FmaCase : public CommonFunctionCase
1988 {
1989 public:
FmaCase(Context & context,glu::DataType baseType,glu::Precision precision,glu::ShaderType shaderType)1990     FmaCase(Context &context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1991         : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fma", shaderType)
1992     {
1993         m_spec.inputs.push_back(Symbol("a", glu::VarType(baseType, precision)));
1994         m_spec.inputs.push_back(Symbol("b", glu::VarType(baseType, precision)));
1995         m_spec.inputs.push_back(Symbol("c", glu::VarType(baseType, precision)));
1996         m_spec.outputs.push_back(Symbol("res", glu::VarType(baseType, precision)));
1997         m_spec.source = "res = fma(a, b, c);";
1998 
1999         if (!glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::es(3, 2)) &&
2000             !glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::core(4, 5)))
2001             m_spec.globalDeclarations = "#extension GL_EXT_gpu_shader5 : require\n";
2002     }
2003 
init(void)2004     void init(void)
2005     {
2006         if (!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) &&
2007             !m_context.getContextInfo().isExtensionSupported("GL_EXT_gpu_shader5") &&
2008             !glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5)))
2009             throw tcu::NotSupportedError("OpenGL ES 3.2, GL_EXT_gpu_shader5 not supported and OpenGL 4.5");
2010 
2011         CommonFunctionCase::init();
2012     }
2013 
getInputValues(int numValues,void * const * values) const2014     void getInputValues(int numValues, void *const *values) const
2015     {
2016         const Vec2 ranges[] = {
2017             Vec2(-2.0f, 2.0f),   // lowp
2018             Vec2(-127.f, 127.f), // mediump
2019             Vec2(-1e7f, 1e7f)    // highp
2020         };
2021 
2022         de::Random rnd(deStringHash(getName()) ^ 0xac23fu);
2023         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
2024         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2025         const int scalarSize           = glu::getDataTypeScalarSize(type);
2026         const float specialCases[][3]  = {
2027             // a        b        c
2028             {0.0f, 0.0f, 0.0f},  {0.0f, 1.0f, 0.0f},  {0.0f, 0.0f, -1.0f},  {1.0f, 1.0f, 0.0f},  {1.0f, 1.0f, 1.0f},
2029             {-1.0f, 1.0f, 0.0f}, {1.0f, -1.0f, 0.0f}, {-1.0f, -1.0f, 0.0f}, {-0.0f, 1.0f, 0.0f}, {1.0f, -0.0f, 0.0f}};
2030         const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
2031 
2032         // Special cases
2033         for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
2034         {
2035             for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2036             {
2037                 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
2038                     ((float *)values[inputNdx])[caseNdx * scalarSize + scalarNdx] = specialCases[caseNdx][inputNdx];
2039             }
2040         }
2041 
2042         // Random cases.
2043         {
2044             const int numScalars = (numValues - numSpecialCases) * scalarSize;
2045             const int offs       = scalarSize * numSpecialCases;
2046 
2047             for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2048                 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float *)values[inputNdx] + offs,
2049                                   numScalars);
2050         }
2051 
2052         // Make sure the values are representable in the target format
2053         for (int inputNdx = 0; inputNdx < 3; inputNdx++)
2054         {
2055             for (int caseNdx = 0; caseNdx < numValues; ++caseNdx)
2056             {
2057                 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
2058                 {
2059                     float *const valuePtr = &((float *)values[inputNdx])[caseNdx * scalarSize + scalarNdx];
2060 
2061                     *valuePtr = makeFloatRepresentable(*valuePtr, precision);
2062                 }
2063             }
2064         }
2065     }
2066 
fma(glu::Precision precision,float a,float b,float c)2067     static tcu::Interval fma(glu::Precision precision, float a, float b, float c)
2068     {
2069         const tcu::FloatFormat formats[] = {
2070             //                 minExp        maxExp        mantissa    exact, subnormals    infinities    NaN
2071             tcu::FloatFormat(0, 0, 7, false, tcu::YES, tcu::MAYBE, tcu::MAYBE),
2072             tcu::FloatFormat(-13, 13, 9, false, tcu::MAYBE, tcu::MAYBE, tcu::MAYBE),
2073             tcu::FloatFormat(-126, 127, 23, true, tcu::MAYBE, tcu::YES, tcu::MAYBE)};
2074         const tcu::FloatFormat &format = de::getSizedArrayElement<glu::PRECISION_LAST>(formats, precision);
2075         const tcu::Interval ia         = format.convert(a);
2076         const tcu::Interval ib         = format.convert(b);
2077         const tcu::Interval ic         = format.convert(c);
2078         tcu::Interval prod0;
2079         tcu::Interval prod1;
2080         tcu::Interval prod2;
2081         tcu::Interval prod3;
2082         tcu::Interval prod;
2083         tcu::Interval res;
2084 
2085         TCU_SET_INTERVAL(prod0, tmp, tmp = ia.lo() * ib.lo());
2086         TCU_SET_INTERVAL(prod1, tmp, tmp = ia.lo() * ib.hi());
2087         TCU_SET_INTERVAL(prod2, tmp, tmp = ia.hi() * ib.lo());
2088         TCU_SET_INTERVAL(prod3, tmp, tmp = ia.hi() * ib.hi());
2089 
2090         prod = format.convert(format.roundOut(prod0 | prod1 | prod2 | prod3,
2091                                               ia.isFinite(format.getMaxValue()) && ib.isFinite(format.getMaxValue())));
2092 
2093         TCU_SET_INTERVAL_BOUNDS(res, tmp, tmp = prod.lo() + ic.lo(), tmp = prod.hi() + ic.hi());
2094 
2095         return format.convert(
2096             format.roundOut(res, prod.isFinite(format.getMaxValue()) && ic.isFinite(format.getMaxValue())));
2097     }
2098 
compare(const void * const * inputs,const void * const * outputs)2099     bool compare(const void *const *inputs, const void *const *outputs)
2100     {
2101         const glu::DataType type       = m_spec.inputs[0].varType.getBasicType();
2102         const glu::Precision precision = m_spec.inputs[0].varType.getPrecision();
2103         const int scalarSize           = glu::getDataTypeScalarSize(type);
2104 
2105         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
2106         {
2107             const float a           = ((const float *)inputs[0])[compNdx];
2108             const float b           = ((const float *)inputs[1])[compNdx];
2109             const float c           = ((const float *)inputs[2])[compNdx];
2110             const float res         = ((const float *)outputs[0])[compNdx];
2111             const tcu::Interval ref = fma(precision, a, b, c);
2112 
2113             if (!ref.contains(res))
2114             {
2115                 m_failMsg << "Expected [" << compNdx << "] = " << ref;
2116                 return false;
2117             }
2118         }
2119 
2120         return true;
2121     }
2122 };
2123 
ShaderCommonFunctionTests(Context & context)2124 ShaderCommonFunctionTests::ShaderCommonFunctionTests(Context &context)
2125     : TestCaseGroup(context, "common", "Common function tests")
2126 {
2127 }
2128 
~ShaderCommonFunctionTests(void)2129 ShaderCommonFunctionTests::~ShaderCommonFunctionTests(void)
2130 {
2131 }
2132 
2133 template <class TestClass>
addFunctionCases(TestCaseGroup * parent,const char * functionName,bool floatTypes,bool intTypes,bool uintTypes,uint32_t shaderBits)2134 static void addFunctionCases(TestCaseGroup *parent, const char *functionName, bool floatTypes, bool intTypes,
2135                              bool uintTypes, uint32_t shaderBits)
2136 {
2137     tcu::TestCaseGroup *group = new tcu::TestCaseGroup(parent->getTestContext(), functionName, functionName);
2138     parent->addChild(group);
2139 
2140     const glu::DataType scalarTypes[] = {glu::TYPE_FLOAT, glu::TYPE_INT, glu::TYPE_UINT};
2141 
2142     for (int scalarTypeNdx = 0; scalarTypeNdx < DE_LENGTH_OF_ARRAY(scalarTypes); scalarTypeNdx++)
2143     {
2144         const glu::DataType scalarType = scalarTypes[scalarTypeNdx];
2145 
2146         if ((!floatTypes && scalarType == glu::TYPE_FLOAT) || (!intTypes && scalarType == glu::TYPE_INT) ||
2147             (!uintTypes && scalarType == glu::TYPE_UINT))
2148             continue;
2149 
2150         for (int vecSize = 1; vecSize <= 4; vecSize++)
2151         {
2152             for (int prec = glu::PRECISION_LOWP; prec <= glu::PRECISION_HIGHP; prec++)
2153             {
2154                 for (int shaderTypeNdx = 0; shaderTypeNdx < glu::SHADERTYPE_LAST; shaderTypeNdx++)
2155                 {
2156                     if (shaderBits & (1 << shaderTypeNdx))
2157                         group->addChild(new TestClass(parent->getContext(), glu::DataType(scalarType + vecSize - 1),
2158                                                       glu::Precision(prec), glu::ShaderType(shaderTypeNdx)));
2159                 }
2160             }
2161         }
2162     }
2163 }
2164 
init(void)2165 void ShaderCommonFunctionTests::init(void)
2166 {
2167     enum
2168     {
2169         VS = (1 << glu::SHADERTYPE_VERTEX),
2170         TC = (1 << glu::SHADERTYPE_TESSELLATION_CONTROL),
2171         TE = (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION),
2172         GS = (1 << glu::SHADERTYPE_GEOMETRY),
2173         FS = (1 << glu::SHADERTYPE_FRAGMENT),
2174         CS = (1 << glu::SHADERTYPE_COMPUTE),
2175 
2176         ALL_SHADERS = VS | TC | TE | GS | FS | CS,
2177         NEW_SHADERS = TC | TE | GS | CS,
2178     };
2179 
2180     //                                                                    Float?    Int?    Uint?    Shaders
2181     addFunctionCases<AbsCase>(this, "abs", true, true, false, NEW_SHADERS);
2182     addFunctionCases<SignCase>(this, "sign", true, true, false, NEW_SHADERS);
2183     addFunctionCases<FloorCase>(this, "floor", true, false, false, NEW_SHADERS);
2184     addFunctionCases<TruncCase>(this, "trunc", true, false, false, NEW_SHADERS);
2185     addFunctionCases<RoundCase>(this, "round", true, false, false, NEW_SHADERS);
2186     addFunctionCases<RoundEvenCase>(this, "roundeven", true, false, false, NEW_SHADERS);
2187     addFunctionCases<CeilCase>(this, "ceil", true, false, false, NEW_SHADERS);
2188     addFunctionCases<FractCase>(this, "fract", true, false, false, NEW_SHADERS);
2189     // mod
2190     addFunctionCases<ModfCase>(this, "modf", true, false, false, NEW_SHADERS);
2191     // min
2192     // max
2193     // clamp
2194     // mix
2195     // step
2196     // smoothstep
2197     addFunctionCases<IsnanCase>(this, "isnan", true, false, false, NEW_SHADERS);
2198     addFunctionCases<IsinfCase>(this, "isinf", true, false, false, NEW_SHADERS);
2199     addFunctionCases<FloatBitsToIntCase>(this, "floatbitstoint", true, false, false, NEW_SHADERS);
2200     addFunctionCases<FloatBitsToUintCase>(this, "floatbitstouint", true, false, false, NEW_SHADERS);
2201 
2202     addFunctionCases<FrexpCase>(this, "frexp", true, false, false, ALL_SHADERS);
2203     addFunctionCases<LdexpCase>(this, "ldexp", true, false, false, ALL_SHADERS);
2204     addFunctionCases<FmaCase>(this, "fma", true, false, false, ALL_SHADERS);
2205 
2206     // (u)intBitsToFloat()
2207     {
2208         const uint32_t shaderBits     = NEW_SHADERS;
2209         tcu::TestCaseGroup *intGroup  = new tcu::TestCaseGroup(m_testCtx, "intbitstofloat", "intBitsToFloat() Tests");
2210         tcu::TestCaseGroup *uintGroup = new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat", "uintBitsToFloat() Tests");
2211 
2212         addChild(intGroup);
2213         addChild(uintGroup);
2214 
2215         for (int vecSize = 1; vecSize < 4; vecSize++)
2216         {
2217             const glu::DataType intType  = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
2218             const glu::DataType uintType = vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT;
2219 
2220             for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
2221             {
2222                 if (shaderBits & (1 << shaderType))
2223                 {
2224                     intGroup->addChild(new BitsToFloatCase(m_context, intType, glu::ShaderType(shaderType)));
2225                     uintGroup->addChild(new BitsToFloatCase(m_context, uintType, glu::ShaderType(shaderType)));
2226                 }
2227             }
2228         }
2229     }
2230 }
2231 
2232 } // namespace Functional
2233 } // namespace gles31
2234 } // namespace deqp
2235