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