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