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