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