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