am ea702448: am d38c14ba: am 75e63782: am 523bdab6: am 4667bcc1: am f55476d8: Remove...
[platform/upstream/VK-GL-CTS.git] / modules / gles3 / functional / es3fShaderCommonFunctionTests.cpp
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.0 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 "es3fShaderCommonFunctionTests.hpp"
25 #include "glsShaderExecUtil.hpp"
26 #include "tcuTestLog.hpp"
27 #include "tcuFormatUtil.hpp"
28 #include "tcuFloat.hpp"
29 #include "deRandom.hpp"
30 #include "deMath.h"
31 #include "deString.h"
32
33 namespace deqp
34 {
35 namespace gles3
36 {
37 namespace Functional
38 {
39
40 using std::vector;
41 using std::string;
42 using tcu::TestLog;
43 using namespace gls::ShaderExecUtil;
44
45 using tcu::Vec2;
46 using tcu::Vec3;
47 using tcu::Vec4;
48 using tcu::IVec2;
49 using tcu::IVec3;
50 using tcu::IVec4;
51
52 // Utilities
53
54 template<typename T, int Size>
55 struct VecArrayAccess
56 {
57 public:
58                                                                         VecArrayAccess  (const void* ptr) : m_array((tcu::Vector<T, Size>*)ptr) {}
59                                                                         ~VecArrayAccess (void) {}
60
61         const tcu::Vector<T, Size>&             operator[]              (size_t offset) const   { return m_array[offset];       }
62         tcu::Vector<T, Size>&                   operator[]              (size_t offset)                 { return m_array[offset];       }
63
64 private:
65         tcu::Vector<T, Size>*                   m_array;
66 };
67
68 template<typename T>    T                       randomScalar    (de::Random& rnd, T minValue, T maxValue);
69 template<> inline               float           randomScalar    (de::Random& rnd, float minValue, float maxValue)               { return rnd.getFloat(minValue, maxValue);      }
70 template<> inline               deInt32         randomScalar    (de::Random& rnd, deInt32 minValue, deInt32 maxValue)   { return rnd.getInt(minValue, maxValue);        }
71 template<> inline               deUint32        randomScalar    (de::Random& rnd, deUint32 minValue, deUint32 maxValue) { return minValue + rnd.getUint32() % (maxValue - minValue + 1); }
72
73 template<typename T, int Size>
74 inline tcu::Vector<T, Size> randomVector (de::Random& rnd, const tcu::Vector<T, Size>& minValue, const tcu::Vector<T, Size>& maxValue)
75 {
76         tcu::Vector<T, Size> res;
77         for (int ndx = 0; ndx < Size; ndx++)
78                 res[ndx] = randomScalar<T>(rnd, minValue[ndx], maxValue[ndx]);
79         return res;
80 }
81
82 template<typename T, int Size>
83 static void fillRandomVectors (de::Random& rnd, const tcu::Vector<T, Size>& minValue, const tcu::Vector<T, Size>& maxValue, void* dst, int numValues, int offset = 0)
84 {
85         VecArrayAccess<T, Size> access(dst);
86         for (int ndx = 0; ndx < numValues; ndx++)
87                 access[offset + ndx] = randomVector<T, Size>(rnd, minValue, maxValue);
88 }
89
90 template<typename T>
91 static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0)
92 {
93         T* typedPtr = (T*)dst;
94         for (int ndx = 0; ndx < numValues; ndx++)
95                 typedPtr[offset + ndx] = randomScalar<T>(rnd, minValue, maxValue);
96 }
97
98 inline int numBitsLostInOp (float input, float output)
99 {
100         const int       inExp           = tcu::Float32(input).exponent();
101         const int       outExp          = tcu::Float32(output).exponent();
102
103         return de::max(0, inExp-outExp); // Lost due to mantissa shift.
104 }
105
106 inline deUint32 getUlpDiff (float a, float b)
107 {
108         const deUint32  aBits   = tcu::Float32(a).bits();
109         const deUint32  bBits   = tcu::Float32(b).bits();
110         return aBits > bBits ? aBits - bBits : bBits - aBits;
111 }
112
113 inline deUint32 getUlpDiffIgnoreZeroSign (float a, float b)
114 {
115         if (tcu::Float32(a).isZero())
116                 return getUlpDiff(tcu::Float32::construct(tcu::Float32(b).sign(), 0, 0).asFloat(), b);
117         else if (tcu::Float32(b).isZero())
118                 return getUlpDiff(a, tcu::Float32::construct(tcu::Float32(a).sign(), 0, 0).asFloat());
119         else
120                 return getUlpDiff(a, b);
121 }
122
123 inline bool supportsSignedZero (glu::Precision precision)
124 {
125         // \note GLSL ES 3.0 doesn't really require support for -0, but we require it for highp
126         //               as it is very widely supported.
127         return precision == glu::PRECISION_HIGHP;
128 }
129
130 inline float getEpsFromMaxUlpDiff (float value, deUint32 ulpDiff)
131 {
132         const int exp = tcu::Float32(value).exponent();
133         return tcu::Float32::construct(+1, exp, (1u<<23) | ulpDiff).asFloat() - tcu::Float32::construct(+1, exp, 1u<<23).asFloat();
134 }
135
136 inline deUint32 getMaxUlpDiffFromBits (int numAccurateBits)
137 {
138         const int               numGarbageBits  = 23-numAccurateBits;
139         const deUint32  mask                    = (1u<<numGarbageBits)-1u;
140
141         return mask;
142 }
143
144 inline float getEpsFromBits (float value, int numAccurateBits)
145 {
146         return getEpsFromMaxUlpDiff(value, getMaxUlpDiffFromBits(numAccurateBits));
147 }
148
149 static int getMinMantissaBits (glu::Precision precision)
150 {
151         const int bits[] =
152         {
153                 7,              // lowp
154                 10,             // mediump
155                 23              // highp
156         };
157         DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(bits) == glu::PRECISION_LAST);
158         DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(bits)));
159         return bits[precision];
160 }
161
162 // CommonFunctionCase
163
164 class CommonFunctionCase : public TestCase
165 {
166 public:
167                                                         CommonFunctionCase              (Context& context, const char* name, const char* description, glu::ShaderType shaderType);
168                                                         ~CommonFunctionCase             (void);
169
170         void                                    init                                    (void);
171         void                                    deinit                                  (void);
172         IterateResult                   iterate                                 (void);
173
174 protected:
175                                                         CommonFunctionCase              (const CommonFunctionCase& other);
176         CommonFunctionCase&             operator=                               (const CommonFunctionCase& other);
177
178         virtual void                    getInputValues                  (int numValues, void* const* values) const = 0;
179         virtual bool                    compare                                 (const void* const* inputs, const void* const* outputs) = 0;
180
181         glu::ShaderType                 m_shaderType;
182         ShaderSpec                              m_spec;
183         int                                             m_numValues;
184
185         std::ostringstream              m_failMsg;                              //!< Comparison failure help message.
186
187 private:
188         ShaderExecutor*                 m_executor;
189 };
190
191 CommonFunctionCase::CommonFunctionCase (Context& context, const char* name, const char* description, glu::ShaderType shaderType)
192         : TestCase              (context, name, description)
193         , m_shaderType  (shaderType)
194         , m_numValues   (100)
195         , m_executor    (DE_NULL)
196 {
197         m_spec.version = glu::GLSL_VERSION_300_ES;
198 }
199
200 CommonFunctionCase::~CommonFunctionCase (void)
201 {
202         CommonFunctionCase::deinit();
203 }
204
205 void CommonFunctionCase::init (void)
206 {
207         DE_ASSERT(!m_executor);
208
209         m_executor = createExecutor(m_context.getRenderContext(), m_shaderType, m_spec);
210         m_testCtx.getLog() << m_executor;
211
212         if (!m_executor->isOk())
213                 throw tcu::TestError("Compile failed");
214 }
215
216 void CommonFunctionCase::deinit (void)
217 {
218         delete m_executor;
219         m_executor = DE_NULL;
220 }
221
222 static vector<int> getScalarSizes (const vector<Symbol>& symbols)
223 {
224         vector<int> sizes(symbols.size());
225         for (int ndx = 0; ndx < (int)symbols.size(); ++ndx)
226                 sizes[ndx] = symbols[ndx].varType.getScalarSize();
227         return sizes;
228 }
229
230 static int computeTotalScalarSize (const vector<Symbol>& symbols)
231 {
232         int totalSize = 0;
233         for (vector<Symbol>::const_iterator sym = symbols.begin(); sym != symbols.end(); ++sym)
234                 totalSize += sym->varType.getScalarSize();
235         return totalSize;
236 }
237
238 static vector<void*> getInputOutputPointers (const vector<Symbol>& symbols, vector<deUint32>& data, const int numValues)
239 {
240         vector<void*>   pointers                (symbols.size());
241         int                             curScalarOffset = 0;
242
243         for (int varNdx = 0; varNdx < (int)symbols.size(); ++varNdx)
244         {
245                 const Symbol&   var                             = symbols[varNdx];
246                 const int               scalarSize              = var.varType.getScalarSize();
247
248                 // Uses planar layout as input/output specs do not support strides.
249                 pointers[varNdx] = &data[curScalarOffset];
250                 curScalarOffset += scalarSize*numValues;
251         }
252
253         DE_ASSERT(curScalarOffset == (int)data.size());
254
255         return pointers;
256 }
257
258 // \todo [2013-08-08 pyry] Make generic utility and move to glu?
259
260 struct HexFloat
261 {
262         const float value;
263         HexFloat (const float value_) : value(value_) {}
264 };
265
266 std::ostream& operator<< (std::ostream& str, const HexFloat& v)
267 {
268         return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
269 }
270
271 struct HexBool
272 {
273         const deUint32 value;
274         HexBool (const deUint32 value_) : value(value_) {}
275 };
276
277 std::ostream& operator<< (std::ostream& str, const HexBool& v)
278 {
279         return str << (v.value ? "true" : "false") << " / " << tcu::toHex(v.value);
280 }
281
282 struct VarValue
283 {
284         const glu::VarType&     type;
285         const void*                     value;
286
287         VarValue (const glu::VarType& type_, const void* value_) : type(type_), value(value_) {}
288 };
289
290 std::ostream& operator<< (std::ostream& str, const VarValue& varValue)
291 {
292         DE_ASSERT(varValue.type.isBasicType());
293
294         const glu::DataType             basicType               = varValue.type.getBasicType();
295         const glu::DataType             scalarType              = glu::getDataTypeScalarType(basicType);
296         const int                               numComponents   = glu::getDataTypeScalarSize(basicType);
297
298         if (numComponents > 1)
299                 str << glu::getDataTypeName(basicType) << "(";
300
301         for (int compNdx = 0; compNdx < numComponents; compNdx++)
302         {
303                 if (compNdx != 0)
304                         str << ", ";
305
306                 switch (scalarType)
307                 {
308                         case glu::TYPE_FLOAT:   str << HexFloat(((const float*)varValue.value)[compNdx]);                       break;
309                         case glu::TYPE_INT:             str << ((const deInt32*)varValue.value)[compNdx];                                       break;
310                         case glu::TYPE_UINT:    str << tcu::toHex(((const deUint32*)varValue.value)[compNdx]);          break;
311                         case glu::TYPE_BOOL:    str << HexBool(((const deUint32*)varValue.value)[compNdx]);                     break;
312
313                         default:
314                                 DE_ASSERT(false);
315                 }
316         }
317
318         if (numComponents > 1)
319                 str << ")";
320
321         return str;
322 }
323
324 CommonFunctionCase::IterateResult CommonFunctionCase::iterate (void)
325 {
326         const int                               numInputScalars                 = computeTotalScalarSize(m_spec.inputs);
327         const int                               numOutputScalars                = computeTotalScalarSize(m_spec.outputs);
328         vector<deUint32>                inputData                               (numInputScalars * m_numValues);
329         vector<deUint32>                outputData                              (numOutputScalars * m_numValues);
330         const vector<void*>             inputPointers                   = getInputOutputPointers(m_spec.inputs, inputData, m_numValues);
331         const vector<void*>             outputPointers                  = getInputOutputPointers(m_spec.outputs, outputData, m_numValues);
332
333         // Initialize input data.
334         getInputValues(m_numValues, &inputPointers[0]);
335
336         // Execute shader.
337         m_executor->useProgram();
338         m_executor->execute(m_numValues, &inputPointers[0], &outputPointers[0]);
339
340         // Compare results.
341         {
342                 const vector<int>               inScalarSizes           = getScalarSizes(m_spec.inputs);
343                 const vector<int>               outScalarSizes          = getScalarSizes(m_spec.outputs);
344                 vector<void*>                   curInputPtr                     (inputPointers.size());
345                 vector<void*>                   curOutputPtr            (outputPointers.size());
346                 int                                             numFailed                       = 0;
347
348                 for (int valNdx = 0; valNdx < m_numValues; valNdx++)
349                 {
350                         // Set up pointers for comparison.
351                         for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx)
352                                 curInputPtr[inNdx] = (deUint32*)inputPointers[inNdx] + inScalarSizes[inNdx]*valNdx;
353
354                         for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx)
355                                 curOutputPtr[outNdx] = (deUint32*)outputPointers[outNdx] + outScalarSizes[outNdx]*valNdx;
356
357                         if (!compare(&curInputPtr[0], &curOutputPtr[0]))
358                         {
359                                 // \todo [2013-08-08 pyry] We probably want to log reference value as well?
360
361                                 m_testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n  " << m_failMsg.str() << TestLog::EndMessage;
362
363                                 m_testCtx.getLog() << TestLog::Message << "  inputs:" << TestLog::EndMessage;
364                                 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++)
365                                         m_testCtx.getLog() << TestLog::Message << "    " << m_spec.inputs[inNdx].name << " = "
366                                                                                                                    << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx])
367                                                                            << TestLog::EndMessage;
368
369                                 m_testCtx.getLog() << TestLog::Message << "  outputs:" << TestLog::EndMessage;
370                                 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++)
371                                         m_testCtx.getLog() << TestLog::Message << "    " << m_spec.outputs[outNdx].name << " = "
372                                                                                                                    << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx])
373                                                                            << TestLog::EndMessage;
374
375                                 m_failMsg.str("");
376                                 m_failMsg.clear();
377                                 numFailed += 1;
378                         }
379                 }
380
381                 m_testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage;
382
383                 m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS    : QP_TEST_RESULT_FAIL,
384                                                                 numFailed == 0 ? "Pass"                                 : "Result comparison failed");
385         }
386
387         return STOP;
388 }
389
390 static const char* getPrecisionPostfix (glu::Precision precision)
391 {
392         static const char* s_postfix[] =
393         {
394                 "_lowp",
395                 "_mediump",
396                 "_highp"
397         };
398         DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_postfix) == glu::PRECISION_LAST);
399         DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
400         return s_postfix[precision];
401 }
402
403 static const char* getShaderTypePostfix (glu::ShaderType shaderType)
404 {
405         static const char* s_postfix[] =
406         {
407                 "_vertex",
408                 "_fragment"
409         };
410         DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_postfix)));
411         return s_postfix[shaderType];
412 }
413
414 static std::string getCommonFuncCaseName (glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
415 {
416         return string(glu::getDataTypeName(baseType)) + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType);
417 }
418
419 class AbsCase : public CommonFunctionCase
420 {
421 public:
422         AbsCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
423                 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "abs", shaderType)
424         {
425                 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
426                 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
427                 m_spec.source = "out0 = abs(in0);";
428         }
429
430         void getInputValues (int numValues, void* const* values) const
431         {
432                 const Vec2 floatRanges[] =
433                 {
434                         Vec2(-2.0f,             2.0f),  // lowp
435                         Vec2(-1e3f,             1e3f),  // mediump
436                         Vec2(-1e7f,             1e7f)   // highp
437                 };
438                 const IVec2 intRanges[] =
439                 {
440                         IVec2(-(1<<7)+1,        (1<<7)-1),
441                         IVec2(-(1<<15)+1,       (1<<15)-1),
442                         IVec2(0x80000001,       0x7fffffff)
443                 };
444
445                 de::Random                              rnd                     (deStringHash(getName()) ^ 0x235facu);
446                 const glu::DataType             type            = m_spec.inputs[0].varType.getBasicType();
447                 const glu::Precision    precision       = m_spec.inputs[0].varType.getPrecision();
448                 const int                               scalarSize      = glu::getDataTypeScalarSize(type);
449
450                 if (glu::isDataTypeFloatOrVec(type))
451                         fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), values[0], numValues*scalarSize);
452                 else
453                         fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize);
454         }
455
456         bool compare (const void* const* inputs, const void* const* outputs)
457         {
458                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
459                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
460                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
461
462                 if (glu::isDataTypeFloatOrVec(type))
463                 {
464                         const int               mantissaBits    = getMinMantissaBits(precision);
465                         const deUint32  maxUlpDiff              = (1u<<(23-mantissaBits))-1u;
466
467                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
468                         {
469                                 const float             in0                     = ((const float*)inputs[0])[compNdx];
470                                 const float             out0            = ((const float*)outputs[0])[compNdx];
471                                 const float             ref0            = de::abs(in0);
472                                 const deUint32  ulpDiff0        = getUlpDiff(out0, ref0);
473
474                                 if (ulpDiff0 > maxUlpDiff)
475                                 {
476                                         m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
477                                         return false;
478                                 }
479                         }
480                 }
481                 else
482                 {
483                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
484                         {
485                                 const int       in0             = ((const int*)inputs[0])[compNdx];
486                                 const int       out0    = ((const int*)outputs[0])[compNdx];
487                                 const int       ref0    = de::abs(in0);
488
489                                 if (out0 != ref0)
490                                 {
491                                         m_failMsg << "Expected [" << compNdx << "] = " << ref0;
492                                         return false;
493                                 }
494                         }
495                 }
496
497                 return true;
498         }
499 };
500
501 class SignCase : public CommonFunctionCase
502 {
503 public:
504         SignCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
505                 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "sign", shaderType)
506         {
507                 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
508                 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
509                 m_spec.source = "out0 = sign(in0);";
510         }
511
512         void getInputValues (int numValues, void* const* values) const
513         {
514                 const Vec2 floatRanges[] =
515                 {
516                         Vec2(-2.0f,             2.0f),  // lowp
517                         Vec2(-1e4f,             1e4f),  // mediump      - note: may end up as inf
518                         Vec2(-1e8f,             1e8f)   // highp        - note: may end up as inf
519                 };
520                 const IVec2 intRanges[] =
521                 {
522                         IVec2(-(1<<7),          (1<<7)-1),
523                         IVec2(-(1<<15),         (1<<15)-1),
524                         IVec2(0x80000000,       0x7fffffff)
525                 };
526
527                 de::Random                              rnd                     (deStringHash(getName()) ^ 0x324u);
528                 const glu::DataType             type            = m_spec.inputs[0].varType.getBasicType();
529                 const glu::Precision    precision       = m_spec.inputs[0].varType.getPrecision();
530                 const int                               scalarSize      = glu::getDataTypeScalarSize(type);
531
532                 if (glu::isDataTypeFloatOrVec(type))
533                 {
534                         // Special cases.
535                         std::fill((float*)values[0],                            (float*)values[0] + scalarSize,         +1.0f);
536                         std::fill((float*)values[0] + scalarSize*1,     (float*)values[0] + scalarSize*2,       -1.0f);
537                         std::fill((float*)values[0] + scalarSize*2,     (float*)values[0] + scalarSize*3,       0.0f);
538                         fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), (float*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
539                 }
540                 else
541                 {
542                         std::fill((int*)values[0],                                      (int*)values[0] + scalarSize,           +1);
543                         std::fill((int*)values[0] + scalarSize*1,       (int*)values[0] + scalarSize*2,         -1);
544                         std::fill((int*)values[0] + scalarSize*2,       (int*)values[0] + scalarSize*3,         0);
545                         fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize);
546                 }
547         }
548
549         bool compare (const void* const* inputs, const void* const* outputs)
550         {
551                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
552                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
553                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
554
555                 if (glu::isDataTypeFloatOrVec(type))
556                 {
557                         // Both highp and mediump should be able to represent -1, 0, and +1 exactly
558                         const deUint32 maxUlpDiff = precision == glu::PRECISION_LOWP ? getMaxUlpDiffFromBits(getMinMantissaBits(precision)) : 0;
559
560                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
561                         {
562                                 const float             in0                     = ((const float*)inputs[0])[compNdx];
563                                 const float             out0            = ((const float*)outputs[0])[compNdx];
564                                 const float             ref0            = in0 < 0.0f ? -1.0f :
565                                                                                           in0 > 0.0f ? +1.0f : 0.0f;
566                                 const deUint32  ulpDiff0        = getUlpDiff(out0, ref0);
567
568                                 if (ulpDiff0 > maxUlpDiff)
569                                 {
570                                         m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0;
571                                         return false;
572                                 }
573                         }
574                 }
575                 else
576                 {
577                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
578                         {
579                                 const int       in0             = ((const int*)inputs[0])[compNdx];
580                                 const int       out0    = ((const int*)outputs[0])[compNdx];
581                                 const int       ref0    = in0 < 0 ? -1 :
582                                                                           in0 > 0 ? +1 : 0;
583
584                                 if (out0 != ref0)
585                                 {
586                                         m_failMsg << "Expected [" << compNdx << "] = " << ref0;
587                                         return false;
588                                 }
589                         }
590                 }
591
592                 return true;
593         }
594 };
595
596 static float roundEven (float v)
597 {
598         const float             q                       = deFloatFrac(v);
599         const int               truncated       = int(v-q);
600         const int               rounded         = (q > 0.5f)                                                    ? (truncated + 1) :     // Rounded up
601                                                                         (q == 0.5f && (truncated % 2 != 0))     ? (truncated + 1) :     // Round to nearest even at 0.5
602                                                                         truncated;                                                                                              // Rounded down
603
604         return float(rounded);
605 }
606
607 class RoundEvenCase : public CommonFunctionCase
608 {
609 public:
610         RoundEvenCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
611                 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "roundEven", shaderType)
612         {
613                 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
614                 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
615                 m_spec.source = "out0 = roundEven(in0);";
616         }
617
618         void getInputValues (int numValues, void* const* values) const
619         {
620                 const Vec2 ranges[] =
621                 {
622                         Vec2(-2.0f,             2.0f),  // lowp
623                         Vec2(-1e3f,             1e3f),  // mediump
624                         Vec2(-1e7f,             1e7f)   // highp
625                 };
626
627                 de::Random                              rnd                             (deStringHash(getName()) ^ 0xac23fu);
628                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
629                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
630                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
631                 int                                             numSpecialCases = 0;
632
633                 // Special cases.
634                 if (precision != glu::PRECISION_LOWP)
635                 {
636                         DE_ASSERT(numValues >= 20);
637                         for (int ndx = 0; ndx < 20; ndx++)
638                         {
639                                 const float v = de::clamp(float(ndx) - 10.5f, ranges[precision].x(), ranges[precision].y());
640                                 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
641                                 numSpecialCases += 1;
642                         }
643                 }
644
645                 // Random cases.
646                 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
647
648                 // If precision is mediump, make sure values can be represented in fp16 exactly
649                 if (precision == glu::PRECISION_MEDIUMP)
650                 {
651                         for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
652                                 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
653                 }
654         }
655
656         bool compare (const void* const* inputs, const void* const* outputs)
657         {
658                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
659                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
660                 const bool                              hasSignedZero   = supportsSignedZero(precision);
661                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
662
663                 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
664                 {
665                         // Require exact rounding result.
666                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
667                         {
668                                 const float             in0                     = ((const float*)inputs[0])[compNdx];
669                                 const float             out0            = ((const float*)outputs[0])[compNdx];
670                                 const float             ref                     = roundEven(in0);
671
672                                 const deUint32  ulpDiff         = hasSignedZero ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
673
674                                 if (ulpDiff > 0)
675                                 {
676                                         m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
677                                         return false;
678                                 }
679                         }
680                 }
681                 else
682                 {
683                         const int               mantissaBits    = getMinMantissaBits(precision);
684                         const deUint32  maxUlpDiff              = getMaxUlpDiffFromBits(mantissaBits);  // ULP diff for rounded integer value.
685                         const float             eps                             = getEpsFromBits(1.0f, mantissaBits);   // epsilon for rounding bounds
686
687                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
688                         {
689                                 const float             in0                     = ((const float*)inputs[0])[compNdx];
690                                 const float             out0            = ((const float*)outputs[0])[compNdx];
691                                 const int               minRes          = int(roundEven(in0-eps));
692                                 const int               maxRes          = int(roundEven(in0+eps));
693                                 bool                    anyOk           = false;
694
695                                 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
696                                 {
697                                         const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
698
699                                         if (ulpDiff <= maxUlpDiff)
700                                         {
701                                                 anyOk = true;
702                                                 break;
703                                         }
704                                 }
705
706                                 if (!anyOk)
707                                 {
708                                         m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
709                                         return false;
710                                 }
711                         }
712                 }
713
714                 return true;
715         }
716 };
717
718 class ModfCase : public CommonFunctionCase
719 {
720 public:
721         ModfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
722                 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "modf", shaderType)
723         {
724                 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
725                 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
726                 m_spec.outputs.push_back(Symbol("out1", glu::VarType(baseType, precision)));
727                 m_spec.source = "out0 = modf(in0, out1);";
728         }
729
730         void getInputValues (int numValues, void* const* values) const
731         {
732                 const Vec2 ranges[] =
733                 {
734                         Vec2(-2.0f,             2.0f),  // lowp
735                         Vec2(-1e3f,             1e3f),  // mediump
736                         Vec2(-1e7f,             1e7f)   // highp
737                 };
738
739                 de::Random                              rnd                     (deStringHash(getName()) ^ 0xac23fu);
740                 const glu::DataType             type            = m_spec.inputs[0].varType.getBasicType();
741                 const glu::Precision    precision       = m_spec.inputs[0].varType.getPrecision();
742                 const int                               scalarSize      = glu::getDataTypeScalarSize(type);
743
744                 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
745         }
746
747         bool compare (const void* const* inputs, const void* const* outputs)
748         {
749                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
750                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
751                 const bool                              hasZeroSign             = supportsSignedZero(precision);
752                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
753
754                 const int                               mantissaBits    = getMinMantissaBits(precision);
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 float             out1            = ((const float*)outputs[1])[compNdx];
761
762                         const float             refOut1         = float(int(in0));
763                         const float             refOut0         = in0 - refOut1;
764
765                         const int               bitsLost        = precision != glu::PRECISION_HIGHP ? numBitsLostInOp(in0, refOut0) : 0;
766                         const deUint32  maxUlpDiff      = getMaxUlpDiffFromBits(de::max(mantissaBits - bitsLost, 0));
767
768                         const float             resSum          = out0 + out1;
769
770                         const deUint32  ulpDiff         = hasZeroSign ? getUlpDiff(resSum, in0) : getUlpDiffIgnoreZeroSign(resSum, in0);
771
772                         if (ulpDiff > maxUlpDiff)
773                         {
774                                 m_failMsg << "Expected [" << compNdx << "] = (" << HexFloat(refOut0) << ") + (" << HexFloat(refOut1) << ") = " << HexFloat(in0) << " with ULP threshold "
775                                                         << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
776                                 return false;
777                         }
778                 }
779
780                 return true;
781         }
782 };
783
784 class IsnanCase : public CommonFunctionCase
785 {
786 public:
787         IsnanCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
788                 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isnan", shaderType)
789         {
790                 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
791
792                 const int                       vecSize         = glu::getDataTypeScalarSize(baseType);
793                 const glu::DataType     boolType        = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
794
795                 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
796                 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
797                 m_spec.source = "out0 = isnan(in0);";
798         }
799
800         void getInputValues (int numValues, void* const* values) const
801         {
802                 de::Random                              rnd                             (deStringHash(getName()) ^ 0xc2a39fu);
803                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
804                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
805                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
806                 const int                               mantissaBits    = getMinMantissaBits(precision);
807                 const deUint32                  mantissaMask    = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
808
809                 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
810                 {
811                         const bool              isNan           = rnd.getFloat() > 0.3f;
812                         const bool              isInf           = !isNan && rnd.getFloat() > 0.4f;
813                         const deUint32  mantissa        = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
814                         const deUint32  exp                     = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
815                         const deUint32  sign            = rnd.getUint32() & 0x1u;
816                         const deUint32  value           = (sign << 31) | (exp << 23) | mantissa;
817
818                         DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
819
820                         ((deUint32*)values[0])[valNdx] = value;
821                 }
822         }
823
824         bool compare (const void* const* inputs, const void* const* outputs)
825         {
826                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
827                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
828                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
829
830                 if (precision == glu::PRECISION_HIGHP)
831                 {
832                         // Only highp is required to support inf/nan
833                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
834                         {
835                                 const float             in0             = ((const float*)inputs[0])[compNdx];
836                                 const deUint32  out0    = ((const deUint32*)outputs[0])[compNdx];
837                                 const deUint32  ref             = tcu::Float32(in0).isNaN() ? 1u : 0u;
838
839                                 if (out0 != ref)
840                                 {
841                                         m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
842                                         return false;
843                                 }
844                         }
845                 }
846                 else
847                 {
848                         // Value can be either 0 or 1
849                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
850                         {
851                                 const int out0 = ((const int*)outputs[0])[compNdx];
852
853                                 if (out0 != 0 && out0 != 1)
854                                 {
855                                         m_failMsg << "Expected [" << compNdx << "] = 0 / 1";
856                                         return false;
857                                 }
858                         }
859                 }
860
861                 return true;
862         }
863 };
864
865 class IsinfCase : public CommonFunctionCase
866 {
867 public:
868         IsinfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
869                 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isinf", shaderType)
870         {
871                 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType));
872
873                 const int                       vecSize         = glu::getDataTypeScalarSize(baseType);
874                 const glu::DataType     boolType        = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL;
875
876                 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
877                 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST)));
878                 m_spec.source = "out0 = isinf(in0);";
879         }
880
881         void getInputValues (int numValues, void* const* values) const
882         {
883                 de::Random                              rnd                             (deStringHash(getName()) ^ 0xc2a39fu);
884                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
885                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
886                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
887                 const int                               mantissaBits    = getMinMantissaBits(precision);
888                 const deUint32                  mantissaMask    = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u);
889
890                 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++)
891                 {
892                         const bool              isInf           = rnd.getFloat() > 0.3f;
893                         const bool              isNan           = !isInf && rnd.getFloat() > 0.4f;
894                         const deUint32  mantissa        = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0;
895                         const deUint32  exp                     = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu;
896                         const deUint32  sign            = rnd.getUint32() & 0x1u;
897                         const deUint32  value           = (sign << 31) | (exp << 23) | mantissa;
898
899                         DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan);
900
901                         ((deUint32*)values[0])[valNdx] = value;
902                 }
903         }
904
905         bool compare (const void* const* inputs, const void* const* outputs)
906         {
907                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
908                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
909                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
910
911                 if (precision == glu::PRECISION_HIGHP)
912                 {
913                         // Only highp is required to support inf/nan
914                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
915                         {
916                                 const float             in0             = ((const float*)inputs[0])[compNdx];
917                                 const deUint32  out0    = ((const deUint32*)outputs[0])[compNdx];
918                                 const deUint32  ref             = tcu::Float32(in0).isInf() ? 1u : 0u;
919
920                                 if (out0 != ref)
921                                 {
922                                         m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref);
923                                         return false;
924                                 }
925                         }
926                 }
927                 else
928                 {
929                         // Value can be either 0 or 1
930                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
931                         {
932                                 const int out0 = ((const int*)outputs[0])[compNdx];
933
934                                 if (out0 != 0 && out0 != 1)
935                                 {
936                                         m_failMsg << "Expected [" << compNdx << "] = 0 / 1";
937                                         return false;
938                                 }
939                         }
940                 }
941
942                 return true;
943         }
944 };
945
946 class FloatBitsToUintIntCase : public CommonFunctionCase
947 {
948 public:
949         FloatBitsToUintIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType, bool outIsSigned)
950                 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), outIsSigned ? "floatBitsToInt" : "floatBitsToUint", shaderType)
951         {
952                 const int                       vecSize         = glu::getDataTypeScalarSize(baseType);
953                 const glu::DataType     intType         = outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT)
954                                                                                                           : (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT);
955
956                 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
957                 m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP)));
958                 m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);";
959         }
960
961         void getInputValues (int numValues, void* const* values) const
962         {
963                 const Vec2 ranges[] =
964                 {
965                         Vec2(-2.0f,             2.0f),  // lowp
966                         Vec2(-1e3f,             1e3f),  // mediump
967                         Vec2(-1e7f,             1e7f)   // highp
968                 };
969
970                 de::Random                              rnd                     (deStringHash(getName()) ^ 0x2790au);
971                 const glu::DataType             type            = m_spec.inputs[0].varType.getBasicType();
972                 const glu::Precision    precision       = m_spec.inputs[0].varType.getPrecision();
973                 const int                               scalarSize      = glu::getDataTypeScalarSize(type);
974
975                 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize);
976         }
977
978         bool compare (const void* const* inputs, const void* const* outputs)
979         {
980                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
981                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
982                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
983
984                 const int                               mantissaBits    = getMinMantissaBits(precision);
985                 const int                               maxUlpDiff              = getMaxUlpDiffFromBits(mantissaBits);
986
987                 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
988                 {
989                         const float             in0                     = ((const float*)inputs[0])[compNdx];
990                         const deUint32  out0            = ((const deUint32*)outputs[0])[compNdx];
991                         const deUint32  refOut0         = tcu::Float32(in0).bits();
992                         const int               ulpDiff         = de::abs((int)out0 - (int)refOut0);
993
994                         if (ulpDiff > maxUlpDiff)
995                         {
996                                 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold "
997                                                         << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
998                                 return false;
999                         }
1000                 }
1001
1002                 return true;
1003         }
1004 };
1005
1006 class FloatBitsToIntCase : public FloatBitsToUintIntCase
1007 {
1008 public:
1009         FloatBitsToIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1010                 : FloatBitsToUintIntCase(context, baseType, precision, shaderType, true)
1011         {
1012         }
1013 };
1014
1015 class FloatBitsToUintCase : public FloatBitsToUintIntCase
1016 {
1017 public:
1018         FloatBitsToUintCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1019                 : FloatBitsToUintIntCase(context, baseType, precision, shaderType, false)
1020         {
1021         }
1022 };
1023
1024 class BitsToFloatCase : public CommonFunctionCase
1025 {
1026 public:
1027         BitsToFloatCase (Context& context, glu::DataType baseType, glu::ShaderType shaderType)
1028                 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP, shaderType).c_str(), glu::isDataTypeIntOrIVec(baseType) ? "intBitsToFloat" : "uintBitsToFloat", shaderType)
1029         {
1030                 const bool                      inIsSigned      = glu::isDataTypeIntOrIVec(baseType);
1031                 const int                       vecSize         = glu::getDataTypeScalarSize(baseType);
1032                 const glu::DataType     floatType       = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1033
1034                 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP)));
1035                 m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP)));
1036                 m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);";
1037         }
1038
1039         void getInputValues (int numValues, void* const* values) const
1040         {
1041                 de::Random                              rnd                     (deStringHash(getName()) ^ 0xbbb225u);
1042                 const glu::DataType             type            = m_spec.inputs[0].varType.getBasicType();
1043                 const int                               scalarSize      = glu::getDataTypeScalarSize(type);
1044                 const Vec2                              range           (-1e8f, +1e8f);
1045
1046                 // \note Filled as floats.
1047                 fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize);
1048         }
1049
1050         bool compare (const void* const* inputs, const void* const* outputs)
1051         {
1052                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
1053                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
1054                 const deUint32                  maxUlpDiff              = 0;
1055
1056                 for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1057                 {
1058                         const float             in0                     = ((const float*)inputs[0])[compNdx];
1059                         const float             out0            = ((const float*)outputs[0])[compNdx];
1060                         const deUint32  ulpDiff         = getUlpDiff(in0, out0);
1061
1062                         if (ulpDiff > maxUlpDiff)
1063                         {
1064                                 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits()) << " with ULP threshold "
1065                                                         << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff);
1066                                 return false;
1067                         }
1068                 }
1069
1070                 return true;
1071         }
1072 };
1073
1074 class FloorCase : public CommonFunctionCase
1075 {
1076 public:
1077         FloorCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1078                 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "floor", shaderType)
1079         {
1080                 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1081                 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1082                 m_spec.source = "out0 = floor(in0);";
1083         }
1084
1085         void getInputValues (int numValues, void* const* values) const
1086         {
1087                 const Vec2 ranges[] =
1088                 {
1089                         Vec2(-2.0f,             2.0f),  // lowp
1090                         Vec2(-1e3f,             1e3f),  // mediump
1091                         Vec2(-1e7f,             1e7f)   // highp
1092                 };
1093
1094                 de::Random                              rnd                     (deStringHash(getName()) ^ 0xac23fu);
1095                 const glu::DataType             type            = m_spec.inputs[0].varType.getBasicType();
1096                 const glu::Precision    precision       = m_spec.inputs[0].varType.getPrecision();
1097                 const int                               scalarSize      = glu::getDataTypeScalarSize(type);
1098                 // Random cases.
1099                 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1100
1101                 // If precision is mediump, make sure values can be represented in fp16 exactly
1102                 if (precision == glu::PRECISION_MEDIUMP)
1103                 {
1104                         for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1105                                 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1106                 }
1107         }
1108
1109         bool compare (const void* const* inputs, const void* const* outputs)
1110         {
1111                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
1112                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
1113                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
1114
1115                 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1116                 {
1117                         // Require exact result.
1118                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1119                         {
1120                                 const float             in0                     = ((const float*)inputs[0])[compNdx];
1121                                 const float             out0            = ((const float*)outputs[0])[compNdx];
1122                                 const float             ref                     = deFloatFloor(in0);
1123
1124                                 const deUint32  ulpDiff         = getUlpDiff(out0, ref);
1125
1126                                 if (ulpDiff > 0)
1127                                 {
1128                                         m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1129                                         return false;
1130                                 }
1131                         }
1132                 }
1133                 else
1134                 {
1135                         const int               mantissaBits    = getMinMantissaBits(precision);
1136                         const deUint32  maxUlpDiff              = getMaxUlpDiffFromBits(mantissaBits);  // ULP diff for rounded integer value.
1137                         const float             eps                             = getEpsFromBits(1.0f, mantissaBits);   // epsilon for rounding bounds
1138
1139                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1140                         {
1141                                 const float             in0                     = ((const float*)inputs[0])[compNdx];
1142                                 const float             out0            = ((const float*)outputs[0])[compNdx];
1143                                 const int               minRes          = int(deFloatFloor(in0-eps));
1144                                 const int               maxRes          = int(deFloatFloor(in0+eps));
1145                                 bool                    anyOk           = false;
1146
1147                                 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1148                                 {
1149                                         const deUint32 ulpDiff = getUlpDiff(out0, float(roundedVal));
1150
1151                                         if (ulpDiff <= maxUlpDiff)
1152                                         {
1153                                                 anyOk = true;
1154                                                 break;
1155                                         }
1156                                 }
1157
1158                                 if (!anyOk)
1159                                 {
1160                                         m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1161                                         return false;
1162                                 }
1163                         }
1164                 }
1165
1166                 return true;
1167         }
1168 };
1169
1170 class TruncCase : public CommonFunctionCase
1171 {
1172 public:
1173         TruncCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1174                 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "trunc", shaderType)
1175         {
1176                 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1177                 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1178                 m_spec.source = "out0 = trunc(in0);";
1179         }
1180
1181         void getInputValues (int numValues, void* const* values) const
1182         {
1183                 const Vec2 ranges[] =
1184                 {
1185                         Vec2(-2.0f,             2.0f),  // lowp
1186                         Vec2(-1e3f,             1e3f),  // mediump
1187                         Vec2(-1e7f,             1e7f)   // highp
1188                 };
1189
1190                 de::Random                              rnd                             (deStringHash(getName()) ^ 0xac23fu);
1191                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
1192                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
1193                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
1194                 const float                             specialCases[]  = { 0.0f, -0.0f, -0.9f, 0.9f, 1.0f, -1.0f };
1195                 const int                               numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases);
1196
1197                 // Special cases
1198                 for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++)
1199                 {
1200                         for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1201                                 ((float*)values[0])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx];
1202                 }
1203
1204                 // Random cases.
1205                 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + scalarSize*numSpecialCases, (numValues-numSpecialCases)*scalarSize);
1206
1207                 // If precision is mediump, make sure values can be represented in fp16 exactly
1208                 if (precision == glu::PRECISION_MEDIUMP)
1209                 {
1210                         for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1211                                 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1212                 }
1213         }
1214
1215         bool compare (const void* const* inputs, const void* const* outputs)
1216         {
1217                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
1218                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
1219                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
1220
1221                 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1222                 {
1223                         // Require exact result.
1224                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1225                         {
1226                                 const float             in0                     = ((const float*)inputs[0])[compNdx];
1227                                 const float             out0            = ((const float*)outputs[0])[compNdx];
1228                                 const bool              isNeg           = tcu::Float32(in0).sign() < 0;
1229                                 const float             ref                     = isNeg ? (-float(int(-in0))) : float(int(in0));
1230
1231                                 // \note: trunc() function definition is a bit broad on negative zeros. Ignore result sign if zero.
1232                                 const deUint32  ulpDiff         = getUlpDiffIgnoreZeroSign(out0, ref);
1233
1234                                 if (ulpDiff > 0)
1235                                 {
1236                                         m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1237                                         return false;
1238                                 }
1239                         }
1240                 }
1241                 else
1242                 {
1243                         const int               mantissaBits    = getMinMantissaBits(precision);
1244                         const deUint32  maxUlpDiff              = getMaxUlpDiffFromBits(mantissaBits);  // ULP diff for rounded integer value.
1245                         const float             eps                             = getEpsFromBits(1.0f, mantissaBits);   // epsilon for rounding bounds
1246
1247                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1248                         {
1249                                 const float             in0                     = ((const float*)inputs[0])[compNdx];
1250                                 const float             out0            = ((const float*)outputs[0])[compNdx];
1251                                 const int               minRes          = int(in0-eps);
1252                                 const int               maxRes          = int(in0+eps);
1253                                 bool                    anyOk           = false;
1254
1255                                 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1256                                 {
1257                                         const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1258
1259                                         if (ulpDiff <= maxUlpDiff)
1260                                         {
1261                                                 anyOk = true;
1262                                                 break;
1263                                         }
1264                                 }
1265
1266                                 if (!anyOk)
1267                                 {
1268                                         m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1269                                         return false;
1270                                 }
1271                         }
1272                 }
1273
1274                 return true;
1275         }
1276 };
1277
1278 class RoundCase : public CommonFunctionCase
1279 {
1280 public:
1281         RoundCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1282                 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "round", shaderType)
1283         {
1284                 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1285                 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1286                 m_spec.source = "out0 = round(in0);";
1287         }
1288
1289         void getInputValues (int numValues, void* const* values) const
1290         {
1291                 const Vec2 ranges[] =
1292                 {
1293                         Vec2(-2.0f,             2.0f),  // lowp
1294                         Vec2(-1e3f,             1e3f),  // mediump
1295                         Vec2(-1e7f,             1e7f)   // highp
1296                 };
1297
1298                 de::Random                              rnd                             (deStringHash(getName()) ^ 0xac23fu);
1299                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
1300                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
1301                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
1302                 int                                             numSpecialCases = 0;
1303
1304                 // Special cases.
1305                 if (precision != glu::PRECISION_LOWP)
1306                 {
1307                         DE_ASSERT(numValues >= 10);
1308                         for (int ndx = 0; ndx < 10; ndx++)
1309                         {
1310                                 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1311                                 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1312                                 numSpecialCases += 1;
1313                         }
1314                 }
1315
1316                 // Random cases.
1317                 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1318
1319                 // If precision is mediump, make sure values can be represented in fp16 exactly
1320                 if (precision == glu::PRECISION_MEDIUMP)
1321                 {
1322                         for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1323                                 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1324                 }
1325         }
1326
1327         bool compare (const void* const* inputs, const void* const* outputs)
1328         {
1329                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
1330                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
1331                 const bool                              hasZeroSign             = supportsSignedZero(precision);
1332                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
1333
1334                 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1335                 {
1336                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1337                         {
1338                                 const float             in0                     = ((const float*)inputs[0])[compNdx];
1339                                 const float             out0            = ((const float*)outputs[0])[compNdx];
1340
1341                                 if (deFloatFrac(in0) == 0.5f)
1342                                 {
1343                                         // Allow both ceil(in) and floor(in)
1344                                         const float             ref0            = deFloatFloor(in0);
1345                                         const float             ref1            = deFloatCeil(in0);
1346                                         const deUint32  ulpDiff0        = hasZeroSign ? getUlpDiff(out0, ref0) : getUlpDiffIgnoreZeroSign(out0, ref0);
1347                                         const deUint32  ulpDiff1        = hasZeroSign ? getUlpDiff(out0, ref1) : getUlpDiffIgnoreZeroSign(out0, ref1);
1348
1349                                         if (ulpDiff0 > 0 && ulpDiff1 > 0)
1350                                         {
1351                                                 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " or " << HexFloat(ref1) << ", got ULP diff " << tcu::toHex(de::min(ulpDiff0, ulpDiff1));
1352                                                 return false;
1353                                         }
1354                                 }
1355                                 else
1356                                 {
1357                                         // Require exact result
1358                                         const float             ref             = roundEven(in0);
1359                                         const deUint32  ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1360
1361                                         if (ulpDiff > 0)
1362                                         {
1363                                                 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1364                                                 return false;
1365                                         }
1366                                 }
1367                         }
1368                 }
1369                 else
1370                 {
1371                         const int               mantissaBits    = getMinMantissaBits(precision);
1372                         const deUint32  maxUlpDiff              = getMaxUlpDiffFromBits(mantissaBits);  // ULP diff for rounded integer value.
1373                         const float             eps                             = getEpsFromBits(1.0f, mantissaBits);   // epsilon for rounding bounds
1374
1375                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1376                         {
1377                                 const float             in0                     = ((const float*)inputs[0])[compNdx];
1378                                 const float             out0            = ((const float*)outputs[0])[compNdx];
1379                                 const int               minRes          = int(roundEven(in0-eps));
1380                                 const int               maxRes          = int(roundEven(in0+eps));
1381                                 bool                    anyOk           = false;
1382
1383                                 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1384                                 {
1385                                         const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1386
1387                                         if (ulpDiff <= maxUlpDiff)
1388                                         {
1389                                                 anyOk = true;
1390                                                 break;
1391                                         }
1392                                 }
1393
1394                                 if (!anyOk)
1395                                 {
1396                                         m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1397                                         return false;
1398                                 }
1399                         }
1400                 }
1401
1402                 return true;
1403         }
1404 };
1405
1406 class CeilCase : public CommonFunctionCase
1407 {
1408 public:
1409         CeilCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1410                 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ceil", shaderType)
1411         {
1412                 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1413                 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1414                 m_spec.source = "out0 = ceil(in0);";
1415         }
1416
1417         void getInputValues (int numValues, void* const* values) const
1418         {
1419                 const Vec2 ranges[] =
1420                 {
1421                         Vec2(-2.0f,             2.0f),  // lowp
1422                         Vec2(-1e3f,             1e3f),  // mediump
1423                         Vec2(-1e7f,             1e7f)   // highp
1424                 };
1425
1426                 de::Random                              rnd                     (deStringHash(getName()) ^ 0xac23fu);
1427                 const glu::DataType             type            = m_spec.inputs[0].varType.getBasicType();
1428                 const glu::Precision    precision       = m_spec.inputs[0].varType.getPrecision();
1429                 const int                               scalarSize      = glu::getDataTypeScalarSize(type);
1430
1431                 // Random cases.
1432                 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize);
1433
1434                 // If precision is mediump, make sure values can be represented in fp16 exactly
1435                 if (precision == glu::PRECISION_MEDIUMP)
1436                 {
1437                         for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1438                                 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1439                 }
1440         }
1441
1442         bool compare (const void* const* inputs, const void* const* outputs)
1443         {
1444                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
1445                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
1446                 const bool                              hasZeroSign             = supportsSignedZero(precision);
1447                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
1448
1449                 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1450                 {
1451                         // Require exact result.
1452                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1453                         {
1454                                 const float             in0                     = ((const float*)inputs[0])[compNdx];
1455                                 const float             out0            = ((const float*)outputs[0])[compNdx];
1456                                 const float             ref                     = deFloatCeil(in0);
1457
1458                                 const deUint32  ulpDiff         = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1459
1460                                 if (ulpDiff > 0)
1461                                 {
1462                                         m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1463                                         return false;
1464                                 }
1465                         }
1466                 }
1467                 else
1468                 {
1469                         const int               mantissaBits    = getMinMantissaBits(precision);
1470                         const deUint32  maxUlpDiff              = getMaxUlpDiffFromBits(mantissaBits);  // ULP diff for rounded integer value.
1471                         const float             eps                             = getEpsFromBits(1.0f, mantissaBits);   // epsilon for rounding bounds
1472
1473                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1474                         {
1475                                 const float             in0                     = ((const float*)inputs[0])[compNdx];
1476                                 const float             out0            = ((const float*)outputs[0])[compNdx];
1477                                 const int               minRes          = int(deFloatCeil(in0-eps));
1478                                 const int               maxRes          = int(deFloatCeil(in0+eps));
1479                                 bool                    anyOk           = false;
1480
1481                                 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++)
1482                                 {
1483                                         const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal));
1484
1485                                         if (ulpDiff <= maxUlpDiff)
1486                                         {
1487                                                 anyOk = true;
1488                                                 break;
1489                                         }
1490                                 }
1491
1492                                 if (!anyOk && de::inRange(0, minRes, maxRes))
1493                                 {
1494                                         // Allow -0 as well.
1495                                         const int ulpDiff = de::abs((int)tcu::Float32(out0).bits() - (int)0x80000000u);
1496                                         anyOk = ((deUint32)ulpDiff <= maxUlpDiff);
1497                                 }
1498
1499                                 if (!anyOk)
1500                                 {
1501                                         m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff);
1502                                         return false;
1503                                 }
1504                         }
1505                 }
1506
1507                 return true;
1508         }
1509 };
1510
1511 class FractCase : public CommonFunctionCase
1512 {
1513 public:
1514         FractCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType)
1515                 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fract", shaderType)
1516         {
1517                 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision)));
1518                 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision)));
1519                 m_spec.source = "out0 = fract(in0);";
1520         }
1521
1522         void getInputValues (int numValues, void* const* values) const
1523         {
1524                 const Vec2 ranges[] =
1525                 {
1526                         Vec2(-2.0f,             2.0f),  // lowp
1527                         Vec2(-1e3f,             1e3f),  // mediump
1528                         Vec2(-1e7f,             1e7f)   // highp
1529                 };
1530
1531                 de::Random                              rnd                             (deStringHash(getName()) ^ 0xac23fu);
1532                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
1533                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
1534                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
1535                 int                                             numSpecialCases = 0;
1536
1537                 // Special cases.
1538                 if (precision != glu::PRECISION_LOWP)
1539                 {
1540                         DE_ASSERT(numValues >= 10);
1541                         for (int ndx = 0; ndx < 10; ndx++)
1542                         {
1543                                 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y());
1544                                 std::fill((float*)values[0], (float*)values[0] + scalarSize, v);
1545                                 numSpecialCases += 1;
1546                         }
1547                 }
1548
1549                 // Random cases.
1550                 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize);
1551
1552                 // If precision is mediump, make sure values can be represented in fp16 exactly
1553                 if (precision == glu::PRECISION_MEDIUMP)
1554                 {
1555                         for (int ndx = 0; ndx < numValues*scalarSize; ndx++)
1556                                 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat();
1557                 }
1558         }
1559
1560         bool compare (const void* const* inputs, const void* const* outputs)
1561         {
1562                 const glu::DataType             type                    = m_spec.inputs[0].varType.getBasicType();
1563                 const glu::Precision    precision               = m_spec.inputs[0].varType.getPrecision();
1564                 const bool                              hasZeroSign             = supportsSignedZero(precision);
1565                 const int                               scalarSize              = glu::getDataTypeScalarSize(type);
1566
1567                 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP)
1568                 {
1569                         // Require exact result.
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 float             ref                     = deFloatFrac(in0);
1575
1576                                 const deUint32  ulpDiff         = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref);
1577
1578                                 if (ulpDiff > 0)
1579                                 {
1580                                         m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff);
1581                                         return false;
1582                                 }
1583                         }
1584                 }
1585                 else
1586                 {
1587                         const int               mantissaBits    = getMinMantissaBits(precision);
1588                         const float             eps                             = getEpsFromBits(1.0f, mantissaBits);   // epsilon for rounding bounds
1589
1590                         for (int compNdx = 0; compNdx < scalarSize; compNdx++)
1591                         {
1592                                 const float             in0                     = ((const float*)inputs[0])[compNdx];
1593                                 const float             out0            = ((const float*)outputs[0])[compNdx];
1594
1595                                 if (int(deFloatFloor(in0-eps)) == int(deFloatFloor(in0+eps)))
1596                                 {
1597                                         const float             ref                     = deFloatFrac(in0);
1598                                         const int               bitsLost        = numBitsLostInOp(in0, ref);
1599                                         const deUint32  maxUlpDiff      = getMaxUlpDiffFromBits(de::max(0, mantissaBits-bitsLost));     // ULP diff for rounded integer value.
1600                                         const deUint32  ulpDiff         = getUlpDiffIgnoreZeroSign(out0, ref);
1601
1602                                         if (ulpDiff > maxUlpDiff)
1603                                         {
1604                                                 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << " with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff);
1605                                                 return false;
1606                                         }
1607                                 }
1608                                 else
1609                                 {
1610                                         if (out0 >= 1.0f)
1611                                         {
1612                                                 m_failMsg << "Expected [" << compNdx << "] < 1.0";
1613                                                 return false;
1614                                         }
1615                                 }
1616                         }
1617                 }
1618
1619                 return true;
1620         }
1621 };
1622
1623 ShaderCommonFunctionTests::ShaderCommonFunctionTests (Context& context)
1624         : TestCaseGroup(context, "common", "Common function tests")
1625 {
1626 }
1627
1628 ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void)
1629 {
1630 }
1631
1632 template<class TestClass>
1633 static void addFunctionCases (TestCaseGroup* parent, const char* functionName, bool floatTypes, bool intTypes, bool uintTypes)
1634 {
1635         tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), functionName, functionName);
1636         parent->addChild(group);
1637
1638         const glu::DataType scalarTypes[] =
1639         {
1640                 glu::TYPE_FLOAT,
1641                 glu::TYPE_INT,
1642                 glu::TYPE_UINT
1643         };
1644
1645         for (int scalarTypeNdx = 0; scalarTypeNdx < DE_LENGTH_OF_ARRAY(scalarTypes); scalarTypeNdx++)
1646         {
1647                 const glu::DataType scalarType = scalarTypes[scalarTypeNdx];
1648
1649                 if ((!floatTypes && scalarType == glu::TYPE_FLOAT)      ||
1650                         (!intTypes && scalarType == glu::TYPE_INT)              ||
1651                         (!uintTypes && scalarType == glu::TYPE_UINT))
1652                         continue;
1653
1654                 for (int vecSize = 1; vecSize <= 4; vecSize++)
1655                 {
1656                         for (int prec = glu::PRECISION_LOWP; prec <= glu::PRECISION_HIGHP; prec++)
1657                         {
1658                                 for (int shaderType = glu::SHADERTYPE_VERTEX; shaderType <= glu::SHADERTYPE_FRAGMENT; shaderType++)
1659                                         group->addChild(new TestClass(parent->getContext(), glu::DataType(scalarType + vecSize - 1), glu::Precision(prec), glu::ShaderType(shaderType)));
1660                         }
1661                 }
1662         }
1663 }
1664
1665 void ShaderCommonFunctionTests::init (void)
1666 {
1667         //                                                                                                                                      Float?  Int?    Uint?
1668         addFunctionCases<AbsCase>                               (this,  "abs",                          true,   true,   false);
1669         addFunctionCases<SignCase>                              (this,  "sign",                         true,   true,   false);
1670         addFunctionCases<FloorCase>                             (this,  "floor",                        true,   false,  false);
1671         addFunctionCases<TruncCase>                             (this,  "trunc",                        true,   false,  false);
1672         addFunctionCases<RoundCase>                             (this,  "round",                        true,   false,  false);
1673         addFunctionCases<RoundEvenCase>                 (this,  "roundeven",            true,   false,  false);
1674         addFunctionCases<CeilCase>                              (this,  "ceil",                         true,   false,  false);
1675         addFunctionCases<FractCase>                             (this,  "fract",                        true,   false,  false);
1676         // mod
1677         addFunctionCases<ModfCase>                              (this,  "modf",                         true,   false,  false);
1678         // min
1679         // max
1680         // clamp
1681         // mix
1682         // step
1683         // smoothstep
1684         addFunctionCases<IsnanCase>                             (this,  "isnan",                        true,   false,  false);
1685         addFunctionCases<IsinfCase>                             (this,  "isinf",                        true,   false,  false);
1686         addFunctionCases<FloatBitsToIntCase>    (this,  "floatbitstoint",       true,   false,  false);
1687         addFunctionCases<FloatBitsToUintCase>   (this,  "floatbitstouint",      true,   false,  false);
1688
1689         // (u)intBitsToFloat()
1690         {
1691                 tcu::TestCaseGroup* intGroup    = new tcu::TestCaseGroup(m_testCtx, "intbitstofloat",   "intBitsToFloat() Tests");
1692                 tcu::TestCaseGroup* uintGroup   = new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat",  "uintBitsToFloat() Tests");
1693
1694                 addChild(intGroup);
1695                 addChild(uintGroup);
1696
1697                 for (int vecSize = 1; vecSize < 4; vecSize++)
1698                 {
1699                         const glu::DataType             intType         = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT;
1700                         const glu::DataType             uintType        = vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT;
1701
1702                         for (int shaderType = glu::SHADERTYPE_VERTEX; shaderType <= glu::SHADERTYPE_FRAGMENT; shaderType++)
1703                         {
1704                                 intGroup->addChild(new BitsToFloatCase(m_context, intType, glu::ShaderType(shaderType)));
1705                                 uintGroup->addChild(new BitsToFloatCase(m_context, uintType, glu::ShaderType(shaderType)));
1706                         }
1707                 }
1708         }
1709 }
1710
1711 } // Functional
1712 } // gles3
1713 } // deqp