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