1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2015 The Khronos Group Inc.
6 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
7 * Copyright (c) 2016 The Android Open Source Project
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 *
21 *//*!
22 * \file
23 * \brief Floating-point packing and unpacking function tests.
24 *//*--------------------------------------------------------------------*/
25
26 #include "vktShaderPackingFunctionTests.hpp"
27 #include "vktShaderExecutor.hpp"
28 #include "tcuTestLog.hpp"
29 #include "tcuFormatUtil.hpp"
30 #include "tcuFloat.hpp"
31 #include "tcuVectorUtil.hpp"
32 #include "deRandom.hpp"
33 #include "deMath.h"
34 #include "deString.h"
35 #include "deSharedPtr.hpp"
36
37 namespace vkt
38 {
39 namespace shaderexecutor
40 {
41
42 using namespace shaderexecutor;
43
44 using std::string;
45 using tcu::TestLog;
46
47 namespace
48 {
49
getUlpDiff(float a,float b)50 inline deUint32 getUlpDiff (float a, float b)
51 {
52 const deUint32 aBits = tcu::Float32(a).bits();
53 const deUint32 bBits = tcu::Float32(b).bits();
54 return aBits > bBits ? aBits - bBits : bBits - aBits;
55 }
56
57 struct HexFloat
58 {
59 const float value;
HexFloatvkt::shaderexecutor::__anon2396bd750111::HexFloat60 HexFloat (const float value_) : value(value_) {}
61 };
62
operator <<(std::ostream & str,const HexFloat & v)63 std::ostream& operator<< (std::ostream& str, const HexFloat& v)
64 {
65 return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
66 }
67
68 } // anonymous
69
70 // ShaderPackingFunctionCase
71
72 class ShaderPackingFunctionCase : public TestCase
73 {
74 public:
75 ShaderPackingFunctionCase (tcu::TestContext& testCtx, const char* name, const char* description, glu::ShaderType shaderType);
76 ~ShaderPackingFunctionCase (void);
77
78 void checkSupport (Context& context) const;
initPrograms(vk::SourceCollections & programCollection) const79 virtual void initPrograms (vk::SourceCollections& programCollection) const
80 {
81 generateSources(m_shaderType, m_spec, programCollection);
82 }
83
84 protected:
85 const glu::ShaderType m_shaderType;
86 ShaderSpec m_spec;
87
88 private:
89 ShaderPackingFunctionCase (const ShaderPackingFunctionCase& other);
90 ShaderPackingFunctionCase& operator= (const ShaderPackingFunctionCase& other);
91 };
92
ShaderPackingFunctionCase(tcu::TestContext & testCtx,const char * name,const char * description,glu::ShaderType shaderType)93 ShaderPackingFunctionCase::ShaderPackingFunctionCase (tcu::TestContext& testCtx, const char* name, const char* description, glu::ShaderType shaderType)
94 : TestCase (testCtx, name, description)
95 , m_shaderType (shaderType)
96 {
97 }
98
~ShaderPackingFunctionCase(void)99 ShaderPackingFunctionCase::~ShaderPackingFunctionCase (void)
100 {
101 }
102
checkSupport(Context & context) const103 void ShaderPackingFunctionCase::checkSupport (Context& context) const
104 {
105 checkSupportShader(context, m_shaderType);
106 }
107
108 // ShaderPackingFunctionTestInstance
109
110 class ShaderPackingFunctionTestInstance : public TestInstance
111 {
112 public:
ShaderPackingFunctionTestInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,const char * name)113 ShaderPackingFunctionTestInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, const char* name)
114 : TestInstance (context)
115 , m_testCtx (context.getTestContext())
116 , m_shaderType (shaderType)
117 , m_spec (spec)
118 , m_name (name)
119 , m_executor (createExecutor(context, m_shaderType, m_spec))
120 {
121 }
122 virtual tcu::TestStatus iterate (void) = 0;
123 protected:
124 tcu::TestContext& m_testCtx;
125 const glu::ShaderType m_shaderType;
126 ShaderSpec m_spec;
127 const char* m_name;
128 de::UniquePtr<ShaderExecutor> m_executor;
129 };
130
131 // Test cases
132
133 class PackSnorm2x16CaseInstance: public ShaderPackingFunctionTestInstance
134 {
135 public:
PackSnorm2x16CaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,glu::Precision precision,const char * name)136 PackSnorm2x16CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, glu::Precision precision, const char* name)
137 : ShaderPackingFunctionTestInstance (context, shaderType, spec, name)
138 , m_precision (precision)
139 {
140 }
141
iterate(void)142 tcu::TestStatus iterate (void)
143 {
144 de::Random rnd (deStringHash(m_name) ^ 0x776002);
145 std::vector<tcu::Vec2> inputs;
146 std::vector<deUint32> outputs;
147 const int maxDiff = m_precision == glu::PRECISION_HIGHP ? 1 : // Rounding only.
148 m_precision == glu::PRECISION_MEDIUMP ? 33 : // (2^-10) * (2^15) + 1
149 m_precision == glu::PRECISION_LOWP ? 129 : 0; // (2^-8) * (2^15) + 1
150
151 // Special values to check.
152 inputs.push_back(tcu::Vec2(0.0f, 0.0f));
153 inputs.push_back(tcu::Vec2(-1.0f, 1.0f));
154 inputs.push_back(tcu::Vec2(0.5f, -0.5f));
155 inputs.push_back(tcu::Vec2(-1.5f, 1.5f));
156 inputs.push_back(tcu::Vec2(0.25f, -0.75f));
157
158 // Random values, mostly in range.
159 for (int ndx = 0; ndx < 15; ndx++)
160 {
161 inputs.push_back(tcu::randomVector<float, 2>(rnd, tcu::Vec2(-1.25f), tcu::Vec2(1.25f)));
162 }
163
164 // Large random values.
165 for (int ndx = 0; ndx < 80; ndx++)
166 {
167 inputs.push_back(tcu::randomVector<float, 2>(rnd, tcu::Vec2(-0.5e6f), tcu::Vec2(0.5e6f)));
168 }
169
170 outputs.resize(inputs.size());
171
172 m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
173
174 {
175 const void* in = &inputs[0];
176 void* out = &outputs[0];
177
178 m_executor->execute((int)inputs.size(), &in, &out);
179 }
180
181 // Verify
182 {
183 const int numValues = (int)inputs.size();
184 const int maxPrints = 10;
185 int numFailed = 0;
186
187 for (int valNdx = 0; valNdx < numValues; valNdx++)
188 {
189 const deUint16 ref0 = (deUint16)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), -1.0f, 1.0f) * 32767.0f), -(1<<15), (1<<15)-1);
190 const deUint16 ref1 = (deUint16)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), -1.0f, 1.0f) * 32767.0f), -(1<<15), (1<<15)-1);
191 const deUint32 ref = (ref1 << 16) | ref0;
192 const deUint32 res = outputs[valNdx];
193 const deUint16 res0 = (deUint16)(res & 0xffff);
194 const deUint16 res1 = (deUint16)(res >> 16);
195 const int diff0 = de::abs((int)ref0 - (int)res0);
196 const int diff1 = de::abs((int)ref1 - (int)res1);
197
198 if (diff0 > maxDiff || diff1 > maxDiff)
199 {
200 if (numFailed < maxPrints)
201 {
202 m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
203 << ", expected packSnorm2x16(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
204 << ", got " << tcu::toHex(res)
205 << "\n diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
206 << TestLog::EndMessage;
207 }
208 else if (numFailed == maxPrints)
209 m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
210
211 numFailed += 1;
212 }
213 }
214
215 m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
216
217 if (numFailed == 0)
218 return tcu::TestStatus::pass("Pass");
219 else
220 return tcu::TestStatus::fail("Result comparison failed");
221
222 }
223 }
224
225 private:
226 const glu::Precision m_precision;
227 };
228
229 class PackSnorm2x16Case : public ShaderPackingFunctionCase
230 {
231 public:
PackSnorm2x16Case(tcu::TestContext & testCtx,glu::ShaderType shaderType,glu::Precision precision)232 PackSnorm2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType, glu::Precision precision)
233 : ShaderPackingFunctionCase (testCtx, (string("packsnorm2x16") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(), "packSnorm2x16", shaderType)
234 , m_precision (precision)
235 {
236 m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC2, precision)));
237 m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
238
239 m_spec.source = "out0 = packSnorm2x16(in0);";
240 }
241
createInstance(Context & ctx) const242 TestInstance* createInstance (Context& ctx) const
243 {
244 return new PackSnorm2x16CaseInstance(ctx, m_shaderType, m_spec, m_precision, getName());
245 }
246
247 private:
248 const glu::Precision m_precision;
249 };
250
251 class UnpackSnorm2x16CaseInstance : public ShaderPackingFunctionTestInstance
252 {
253 public:
UnpackSnorm2x16CaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,const char * name)254 UnpackSnorm2x16CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, const char* name)
255 : ShaderPackingFunctionTestInstance (context, shaderType, spec, name)
256 {
257 }
258
iterate(void)259 tcu::TestStatus iterate (void)
260 {
261 const deUint32 maxDiff = 1; // Rounding error.
262 de::Random rnd (deStringHash(m_name) ^ 0x776002);
263 std::vector<deUint32> inputs;
264 std::vector<tcu::Vec2> outputs;
265
266 inputs.push_back(0x00000000u);
267 inputs.push_back(0x7fff8000u);
268 inputs.push_back(0x80007fffu);
269 inputs.push_back(0xffffffffu);
270 inputs.push_back(0x0001fffeu);
271
272 // Random values.
273 for (int ndx = 0; ndx < 95; ndx++)
274 inputs.push_back(rnd.getUint32());
275
276 outputs.resize(inputs.size());
277
278 m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
279
280 {
281 const void* in = &inputs[0];
282 void* out = &outputs[0];
283
284 m_executor->execute((int)inputs.size(), &in, &out);
285 }
286
287 // Verify
288 {
289 const int numValues = (int)inputs.size();
290 const int maxPrints = 10;
291 int numFailed = 0;
292
293 for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
294 {
295 const deInt16 in0 = (deInt16)(deUint16)(inputs[valNdx] & 0xffff);
296 const deInt16 in1 = (deInt16)(deUint16)(inputs[valNdx] >> 16);
297 const float ref0 = de::clamp(float(in0) / 32767.f, -1.0f, 1.0f);
298 const float ref1 = de::clamp(float(in1) / 32767.f, -1.0f, 1.0f);
299 const float res0 = outputs[valNdx].x();
300 const float res1 = outputs[valNdx].y();
301
302 const deUint32 diff0 = getUlpDiff(ref0, res0);
303 const deUint32 diff1 = getUlpDiff(ref1, res1);
304
305 if (diff0 > maxDiff || diff1 > maxDiff)
306 {
307 if (numFailed < maxPrints)
308 {
309 m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
310 << " expected unpackSnorm2x16(" << tcu::toHex(inputs[valNdx]) << ") = "
311 << "vec2(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ")"
312 << ", got vec2(" << HexFloat(res0) << ", " << HexFloat(res1) << ")"
313 << "\n ULP diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
314 << TestLog::EndMessage;
315 }
316 else if (numFailed == maxPrints)
317 m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
318
319 numFailed += 1;
320 }
321 }
322
323 m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
324
325 if (numFailed == 0)
326 return tcu::TestStatus::pass("Pass");
327 else
328 return tcu::TestStatus::fail("Result comparison failed");
329
330 }
331 }
332 };
333
334 class UnpackSnorm2x16Case : public ShaderPackingFunctionCase
335 {
336 public:
UnpackSnorm2x16Case(tcu::TestContext & testCtx,glu::ShaderType shaderType)337 UnpackSnorm2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
338 : ShaderPackingFunctionCase (testCtx, (string("unpacksnorm2x16") + getShaderTypePostfix(shaderType)).c_str(), "unpackSnorm2x16", shaderType)
339 {
340 m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
341 m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_HIGHP)));
342
343 m_spec.source = "out0 = unpackSnorm2x16(in0);";
344 }
345
createInstance(Context & ctx) const346 TestInstance* createInstance (Context& ctx) const
347 {
348 return new UnpackSnorm2x16CaseInstance(ctx, m_shaderType, m_spec, getName());
349 }
350 };
351
352 class PackUnorm2x16CaseInstance : public ShaderPackingFunctionTestInstance
353 {
354 public:
PackUnorm2x16CaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,glu::Precision precision,const char * name)355 PackUnorm2x16CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, glu::Precision precision, const char* name)
356 : ShaderPackingFunctionTestInstance (context, shaderType, spec, name)
357 , m_precision (precision)
358 {
359 }
360
iterate(void)361 tcu::TestStatus iterate (void)
362 {
363 de::Random rnd (deStringHash(m_name) ^ 0x776002);
364 std::vector<tcu::Vec2> inputs;
365 std::vector<deUint32> outputs;
366 const int maxDiff = m_precision == glu::PRECISION_HIGHP ? 1 : // Rounding only.
367 m_precision == glu::PRECISION_MEDIUMP ? 65 : // (2^-10) * (2^16) + 1
368 m_precision == glu::PRECISION_LOWP ? 257 : 0; // (2^-8) * (2^16) + 1
369
370 // Special values to check.
371 inputs.push_back(tcu::Vec2(0.0f, 0.0f));
372 inputs.push_back(tcu::Vec2(0.5f, 1.0f));
373 inputs.push_back(tcu::Vec2(1.0f, 0.5f));
374 inputs.push_back(tcu::Vec2(-0.5f, 1.5f));
375 inputs.push_back(tcu::Vec2(0.25f, 0.75f));
376
377 // Random values, mostly in range.
378 for (int ndx = 0; ndx < 15; ndx++)
379 {
380 inputs.push_back(tcu::randomVector<float, 2>(rnd, tcu::Vec2(0.0f), tcu::Vec2(1.25f)));
381 }
382
383 // Large random values.
384 for (int ndx = 0; ndx < 80; ndx++)
385 {
386 inputs.push_back(tcu::randomVector<float, 2>(rnd, tcu::Vec2(-1e5f), tcu::Vec2(0.9e6f)));
387 }
388
389 outputs.resize(inputs.size());
390
391 m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
392
393 {
394 const void* in = &inputs[0];
395 void* out = &outputs[0];
396
397 m_executor->execute((int)inputs.size(), &in, &out);
398 }
399
400 // Verify
401 {
402 const int numValues = (int)inputs.size();
403 const int maxPrints = 10;
404 int numFailed = 0;
405
406 for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
407 {
408 const deUint16 ref0 = (deUint16)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), 0.0f, 1.0f) * 65535.0f), 0, (1<<16)-1);
409 const deUint16 ref1 = (deUint16)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), 0.0f, 1.0f) * 65535.0f), 0, (1<<16)-1);
410 const deUint32 ref = (ref1 << 16) | ref0;
411 const deUint32 res = outputs[valNdx];
412 const deUint16 res0 = (deUint16)(res & 0xffff);
413 const deUint16 res1 = (deUint16)(res >> 16);
414 const int diff0 = de::abs((int)ref0 - (int)res0);
415 const int diff1 = de::abs((int)ref1 - (int)res1);
416
417 if (diff0 > maxDiff || diff1 > maxDiff)
418 {
419 if (numFailed < maxPrints)
420 {
421 m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
422 << ", expected packUnorm2x16(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
423 << ", got " << tcu::toHex(res)
424 << "\n diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
425 << TestLog::EndMessage;
426 }
427 else if (numFailed == maxPrints)
428 m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
429
430 numFailed += 1;
431 }
432 }
433
434 m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
435
436 if (numFailed == 0)
437 return tcu::TestStatus::pass("Pass");
438 else
439 return tcu::TestStatus::fail("Result comparison failed");
440
441 }
442 }
443
444 private:
445 const glu::Precision m_precision;
446 };
447
448 class PackUnorm2x16Case : public ShaderPackingFunctionCase
449 {
450 public:
PackUnorm2x16Case(tcu::TestContext & testCtx,glu::ShaderType shaderType,glu::Precision precision)451 PackUnorm2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType, glu::Precision precision)
452 : ShaderPackingFunctionCase (testCtx, (string("packunorm2x16") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(), "packUnorm2x16", shaderType)
453 , m_precision (precision)
454 {
455 m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC2, precision)));
456 m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
457
458 m_spec.source = "out0 = packUnorm2x16(in0);";
459 }
460
createInstance(Context & ctx) const461 TestInstance* createInstance (Context& ctx) const
462 {
463 return new PackUnorm2x16CaseInstance(ctx, m_shaderType, m_spec, m_precision, getName());
464 }
465
466 private:
467 const glu::Precision m_precision;
468 };
469
470 class UnpackUnorm2x16CaseInstance : public ShaderPackingFunctionTestInstance
471 {
472 public:
UnpackUnorm2x16CaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,const char * name)473 UnpackUnorm2x16CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, const char* name)
474 : ShaderPackingFunctionTestInstance (context, shaderType, spec, name)
475 {
476 }
477
iterate(void)478 tcu::TestStatus iterate (void)
479 {
480 const deUint32 maxDiff = 1; // Rounding error.
481 de::Random rnd (deStringHash(m_name) ^ 0x776002);
482 std::vector<deUint32> inputs;
483 std::vector<tcu::Vec2> outputs;
484
485 inputs.push_back(0x00000000u);
486 inputs.push_back(0x7fff8000u);
487 inputs.push_back(0x80007fffu);
488 inputs.push_back(0xffffffffu);
489 inputs.push_back(0x0001fffeu);
490
491 // Random values.
492 for (int ndx = 0; ndx < 95; ndx++)
493 inputs.push_back(rnd.getUint32());
494
495 outputs.resize(inputs.size());
496
497 m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
498
499 {
500 const void* in = &inputs[0];
501 void* out = &outputs[0];
502
503 m_executor->execute((int)inputs.size(), &in, &out);
504 }
505
506 // Verify
507 {
508 const int numValues = (int)inputs.size();
509 const int maxPrints = 10;
510 int numFailed = 0;
511
512 for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
513 {
514 const deUint16 in0 = (deUint16)(inputs[valNdx] & 0xffff);
515 const deUint16 in1 = (deUint16)(inputs[valNdx] >> 16);
516 const float ref0 = float(in0) / 65535.0f;
517 const float ref1 = float(in1) / 65535.0f;
518 const float res0 = outputs[valNdx].x();
519 const float res1 = outputs[valNdx].y();
520
521 const deUint32 diff0 = getUlpDiff(ref0, res0);
522 const deUint32 diff1 = getUlpDiff(ref1, res1);
523
524 if (diff0 > maxDiff || diff1 > maxDiff)
525 {
526 if (numFailed < maxPrints)
527 {
528 m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
529 << " expected unpackUnorm2x16(" << tcu::toHex(inputs[valNdx]) << ") = "
530 << "vec2(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ")"
531 << ", got vec2(" << HexFloat(res0) << ", " << HexFloat(res1) << ")"
532 << "\n ULP diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
533 << TestLog::EndMessage;
534 }
535 else if (numFailed == maxPrints)
536 m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
537
538 numFailed += 1;
539 }
540 }
541
542 m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
543
544 if (numFailed == 0)
545 return tcu::TestStatus::pass("Pass");
546 else
547 return tcu::TestStatus::fail("Result comparison failed");
548
549 }
550 }
551 };
552
553
554 class UnpackUnorm2x16Case : public ShaderPackingFunctionCase
555 {
556 public:
UnpackUnorm2x16Case(tcu::TestContext & testCtx,glu::ShaderType shaderType)557 UnpackUnorm2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
558 : ShaderPackingFunctionCase(testCtx, (string("unpackunorm2x16") + getShaderTypePostfix(shaderType)).c_str(), "unpackUnorm2x16", shaderType)
559 {
560 m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
561 m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_HIGHP)));
562
563 m_spec.source = "out0 = unpackUnorm2x16(in0);";
564 }
565
createInstance(Context & ctx) const566 TestInstance* createInstance (Context& ctx) const
567 {
568 return new UnpackUnorm2x16CaseInstance(ctx, m_shaderType, m_spec, getName());
569 }
570
571 };
572
573 class PackHalf2x16CaseInstance : public ShaderPackingFunctionTestInstance
574 {
575 public:
PackHalf2x16CaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,const char * name)576 PackHalf2x16CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, const char* name)
577 : ShaderPackingFunctionTestInstance (context, shaderType, spec, name)
578 {
579 }
580
iterate(void)581 tcu::TestStatus iterate (void)
582 {
583 const int maxDiff = 0; // Values can be represented exactly in mediump.
584 de::Random rnd (deStringHash(m_name) ^ 0x776002);
585 std::vector<tcu::Vec2> inputs;
586 std::vector<deUint32> outputs;
587
588 // Special values to check.
589 inputs.push_back(tcu::Vec2(0.0f, 0.0f));
590 inputs.push_back(tcu::Vec2(0.5f, 1.0f));
591 inputs.push_back(tcu::Vec2(1.0f, 0.5f));
592 inputs.push_back(tcu::Vec2(-0.5f, 1.5f));
593 inputs.push_back(tcu::Vec2(0.25f, 0.75f));
594
595 // Random values.
596 {
597 const int minExp = -14;
598 const int maxExp = 15;
599
600 for (int ndx = 0; ndx < 95; ndx++)
601 {
602 tcu::Vec2 v;
603 for (int c = 0; c < 2; c++)
604 {
605 const int s = rnd.getBool() ? 1 : -1;
606 const int exp = rnd.getInt(minExp, maxExp);
607 const deUint32 mantissa = rnd.getUint32() & ((1<<23)-1);
608
609 v[c] = tcu::Float32::construct(s, exp ? exp : 1 /* avoid denormals */, (1u<<23) | mantissa).asFloat();
610 }
611 inputs.push_back(v);
612 }
613 }
614
615 // Convert input values to fp16 and back to make sure they can be represented exactly in mediump.
616 for (std::vector<tcu::Vec2>::iterator inVal = inputs.begin(); inVal != inputs.end(); ++inVal)
617 *inVal = tcu::Vec2(tcu::Float16(inVal->x()).asFloat(), tcu::Float16(inVal->y()).asFloat());
618
619 outputs.resize(inputs.size());
620
621 m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
622
623 {
624 const void* in = &inputs[0];
625 void* out = &outputs[0];
626
627 m_executor->execute((int)inputs.size(), &in, &out);
628 }
629
630 // Verify
631 {
632 const int numValues = (int)inputs.size();
633 const int maxPrints = 10;
634 int numFailed = 0;
635
636 for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
637 {
638 const deUint16 ref0 = (deUint16)tcu::Float16(inputs[valNdx].x()).bits();
639 const deUint16 ref1 = (deUint16)tcu::Float16(inputs[valNdx].y()).bits();
640 const deUint32 ref = (ref1 << 16) | ref0;
641 const deUint32 res = outputs[valNdx];
642 const deUint16 res0 = (deUint16)(res & 0xffff);
643 const deUint16 res1 = (deUint16)(res >> 16);
644 const int diff0 = de::abs((int)ref0 - (int)res0);
645 const int diff1 = de::abs((int)ref1 - (int)res1);
646
647 if (diff0 > maxDiff || diff1 > maxDiff)
648 {
649 if (numFailed < maxPrints)
650 {
651 m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
652 << ", expected packHalf2x16(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
653 << ", got " << tcu::toHex(res)
654 << "\n diffs = (" << diff0 << ", " << diff1 << "), max diff = " << maxDiff
655 << TestLog::EndMessage;
656 }
657 else if (numFailed == maxPrints)
658 m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
659
660 numFailed += 1;
661 }
662 }
663
664 m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
665
666 if (numFailed == 0)
667 return tcu::TestStatus::pass("Pass");
668 else
669 return tcu::TestStatus::fail("Result comparison failed");
670
671 }
672 }
673 };
674
675 class PackHalf2x16Case : public ShaderPackingFunctionCase
676 {
677 public:
PackHalf2x16Case(tcu::TestContext & testCtx,glu::ShaderType shaderType)678 PackHalf2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
679 : ShaderPackingFunctionCase (testCtx, (string("packhalf2x16") + getShaderTypePostfix(shaderType)).c_str(), "packHalf2x16", shaderType)
680 {
681 m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_HIGHP)));
682 m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
683
684 m_spec.source = "out0 = packHalf2x16(in0);";
685 }
686
createInstance(Context & ctx) const687 TestInstance* createInstance (Context& ctx) const
688 {
689 return new PackHalf2x16CaseInstance(ctx, m_shaderType, m_spec, getName());
690 }
691
692 };
693
694 class UnpackHalf2x16CaseInstance : public ShaderPackingFunctionTestInstance
695 {
696 enum Sign
697 {
698 POSITIVE = 0,
699 NEGATIVE
700 };
701 enum SubnormalizedConversionType
702 {
703 UNKNOWN = 0,
704 CONVERTED,
705 ZERO_FLUSHED,
706 };
707 public:
UnpackHalf2x16CaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,const char * name)708 UnpackHalf2x16CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, const char* name)
709 : ShaderPackingFunctionTestInstance (context, shaderType, spec, name)
710 {
711 }
712
iterate(void)713 tcu::TestStatus iterate (void)
714 {
715 const int minExp = -14;
716 const int maxExp = 15;
717 const int mantBits = 10;
718 const deUint32 mantBitMask = (1u << mantBits) - 1u;
719 tcu::TestLog& log = m_testCtx.getLog();
720
721 de::Random rnd (deStringHash(m_name) ^ 0x776002);
722 std::vector<deUint32> inputs;
723 std::vector<tcu::Vec2> outputs;
724
725 // Special values.
726 inputs.push_back((tcu::Float16( 0.0f).bits() << 16) | tcu::Float16( 1.0f).bits());
727 inputs.push_back((tcu::Float16( 1.0f).bits() << 16) | tcu::Float16( 0.0f).bits());
728 inputs.push_back((tcu::Float16(-1.0f).bits() << 16) | tcu::Float16( 0.5f).bits());
729 inputs.push_back((tcu::Float16( 0.5f).bits() << 16) | tcu::Float16(-0.5f).bits());
730 // Special subnormal value: single lowest bit set
731 inputs.push_back((tcu::Float16(composeHalfFloat(POSITIVE, 0u, 1u)).bits() << 16)
732 | tcu::Float16(composeHalfFloat(NEGATIVE, 0u, 1u)).bits());
733 // Special subnormal value: single highest fraction bit set
734 inputs.push_back((tcu::Float16(composeHalfFloat(NEGATIVE, 0u, 1u << (mantBits - 1u))).bits() << 16)
735 | tcu::Float16(composeHalfFloat(POSITIVE, 0u, 1u << (mantBits - 1u))).bits());
736 // Special subnormal value: all fraction bits set
737 inputs.push_back((tcu::Float16(composeHalfFloat(POSITIVE, 0u, mantBitMask)).bits() << 16)
738 | tcu::Float16(composeHalfFloat(NEGATIVE, 0u, mantBitMask)).bits());
739
740 // Construct random values.
741 for (int ndx = 0; ndx < 90; ndx++)
742 {
743 deUint32 inVal = 0;
744 for (int c = 0; c < 2; c++)
745 {
746 const int s = rnd.getBool() ? 1 : -1;
747 const int exp = rnd.getInt(minExp, maxExp);
748 const deUint32 mantissa = rnd.getUint32() & mantBitMask;
749 const deUint16 value = tcu::Float16::construct(s, exp != 0 ? exp : 1 /* avoid denorm */, static_cast<deUint16>((1u<<10) | mantissa)).bits();
750
751 inVal |= value << (16u * c);
752 }
753 inputs.push_back(inVal);
754 }
755 for (int ndx = 0; ndx < 15; ndx++)
756 {
757 deUint32 inVal = 0;
758 for (int c = 0; c < 2; c++)
759 {
760 const Sign sign = rnd.getBool()? POSITIVE : NEGATIVE;
761 const deUint32 mantissa = rnd.getUint32() & mantBitMask;
762 const deUint16 value = tcu::Float16(composeHalfFloat(sign, 0u /* force denorm */, mantissa)).bits();
763
764 inVal |= value << (16u * c);
765 }
766 inputs.push_back(inVal);
767 }
768
769 outputs.resize(inputs.size());
770
771 log << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
772
773 {
774 const void* in = &inputs[0];
775 void* out = &outputs[0];
776
777 m_executor->execute((int)inputs.size(), &in, &out);
778 }
779
780 // Verify
781 {
782 const int numValues = (int)inputs.size();
783 const int maxPrints = 10;
784 int numFailed = 0;
785 SubnormalizedConversionType conversion = UNKNOWN;
786
787 for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
788 {
789 const deUint16 in0 = (deUint16)(inputs[valNdx] & 0xffff);
790 const deUint16 in1 = (deUint16)(inputs[valNdx] >> 16);
791 const float res0 = outputs[valNdx].x();
792 const float res1 = outputs[valNdx].y();
793
794 const deBool value0 = checkValue(in0, res0, conversion);
795 // note: do not avoid calling checkValue for in1 if it failed for in0 by using && laziness
796 // checkValue may potentially change 'conversion' parameter if it was set to UNKNOWN so far
797 const deBool value1 = checkValue(in1, res1, conversion);
798 const deBool valuesOK = value0 && value1;
799
800 if (!valuesOK)
801 {
802 if (numFailed < maxPrints)
803 printErrorMessage(log, valNdx, in0, in1, res0, res1);
804 else if (numFailed == maxPrints)
805 log << TestLog::Message << "..." << TestLog::EndMessage;
806 ++numFailed;
807 }
808 }
809
810 log << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
811
812 if (numFailed == 0)
813 return tcu::TestStatus::pass("Pass");
814 else
815 return tcu::TestStatus::fail("Result comparison failed");
816 }
817 }
818 private:
checkValue(deUint16 inValue,float outValue,SubnormalizedConversionType & conversion)819 deBool checkValue (deUint16 inValue, float outValue, SubnormalizedConversionType& conversion)
820 {
821 const tcu::Float16 temp = tcu::Float16(inValue);
822 const float ref = temp.asFloat();
823 const deUint32 refBits = tcu::Float32(ref).bits();
824 const deUint32 resBits = tcu::Float32(outValue).bits();
825 const deBool bitMatch = (refBits ^ resBits) == 0u;
826 const deBool denorm = temp.isDenorm();
827
828 if (conversion != CONVERTED && denorm)
829 {
830 if (resBits == 0 || (ref < 0 && resBits == 0x80000000UL))
831 {
832 conversion = ZERO_FLUSHED;
833 return DE_TRUE;
834 }
835 if (conversion != ZERO_FLUSHED && bitMatch)
836 {
837 conversion = CONVERTED;
838 return DE_TRUE;
839 }
840 return DE_FALSE;
841 }
842 else if (bitMatch)
843 return DE_TRUE;
844 return DE_FALSE;
845 }
printErrorMessage(tcu::TestLog & log,deUint32 valNdx,deUint16 in0,deUint16 in1,float out0,float out1)846 void printErrorMessage (tcu::TestLog& log, deUint32 valNdx, deUint16 in0, deUint16 in1, float out0, float out1)
847 {
848 const float ref0 = tcu::Float16(in0).asFloat();
849 const deUint32 refBits0 = tcu::Float32(ref0).bits();
850 const deUint32 resBits0 = tcu::Float32(out0).bits();
851 const float ref1 = tcu::Float16(in1).asFloat();
852 const deUint32 refBits1 = tcu::Float32(ref1).bits();
853 const deUint32 resBits1 = tcu::Float32(out1).bits();
854 log << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
855 << " expected unpackHalf2x16(" << tcu::toHex((in1 << 16u) | in0) << ") = "
856 << "vec2(" << ref0 << " / " << tcu::toHex(refBits0) << ", " << ref1 << " / " << tcu::toHex(refBits1) << ")"
857 << ", got vec2(" << out0 << " / " << tcu::toHex(resBits0) << ", " << out1 << " / " << tcu::toHex(resBits1) << ")"
858 << TestLog::EndMessage;
859 }
composeHalfFloat(Sign sign,deUint32 exponent,deUint32 significand)860 deUint16 composeHalfFloat (Sign sign, deUint32 exponent, deUint32 significand)
861 {
862 const deUint32 BitMask_05 = (1u << 5u) - 1u;
863 const deUint32 BitMask_10 = (1u << 10u) - 1u;
864 const deUint32 BitMask_16 = (1u << 16u) - 1u;
865 DE_UNREF(BitMask_05);
866 DE_UNREF(BitMask_10);
867 DE_UNREF(BitMask_16);
868 DE_ASSERT((exponent & ~BitMask_05) == 0u);
869 DE_ASSERT((significand & ~BitMask_10) == 0u);
870 const deUint32 value = (((sign == NEGATIVE ? 1u : 0u) << 5u | exponent) << 10u) | significand;
871 DE_ASSERT((value & ~BitMask_16) == 0u);
872 return static_cast<deUint16>(value);
873 }
874 };
875
876 class UnpackHalf2x16Case : public ShaderPackingFunctionCase
877 {
878 public:
UnpackHalf2x16Case(tcu::TestContext & testCtx,glu::ShaderType shaderType)879 UnpackHalf2x16Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
880 : ShaderPackingFunctionCase (testCtx, (string("unpackhalf2x16") + getShaderTypePostfix(shaderType)).c_str(), "unpackHalf2x16", shaderType)
881 {
882 m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
883 m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_MEDIUMP)));
884
885 m_spec.source = "out0 = unpackHalf2x16(in0);";
886 }
887
createInstance(Context & ctx) const888 TestInstance* createInstance (Context& ctx) const
889 {
890 return new UnpackHalf2x16CaseInstance(ctx, m_shaderType, m_spec, getName());
891 }
892
893 };
894
895 class PackSnorm4x8CaseInstance : public ShaderPackingFunctionTestInstance
896 {
897 public:
PackSnorm4x8CaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,glu::Precision precision,const char * name)898 PackSnorm4x8CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, glu::Precision precision, const char* name)
899 : ShaderPackingFunctionTestInstance (context, shaderType, spec, name)
900 , m_precision (precision)
901 {
902 }
903
iterate(void)904 tcu::TestStatus iterate (void)
905 {
906 de::Random rnd (deStringHash(m_name) ^ 0x42f2c0);
907 std::vector<tcu::Vec4> inputs;
908 std::vector<deUint32> outputs;
909 const int maxDiff = m_precision == glu::PRECISION_HIGHP ? 1 : // Rounding only.
910 m_precision == glu::PRECISION_MEDIUMP ? 1 : // (2^-10) * (2^7) + 1
911 m_precision == glu::PRECISION_LOWP ? 2 : 0; // (2^-8) * (2^7) + 1
912
913 // Special values to check.
914 inputs.push_back(tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f));
915 inputs.push_back(tcu::Vec4(-1.0f, 1.0f, -1.0f, 1.0f));
916 inputs.push_back(tcu::Vec4(0.5f, -0.5f, -0.5f, 0.5f));
917 inputs.push_back(tcu::Vec4(-1.5f, 1.5f, -1.5f, 1.5f));
918 inputs.push_back(tcu::Vec4(0.25f, -0.75f, -0.25f, 0.75f));
919
920 // Random values, mostly in range.
921 for (int ndx = 0; ndx < 15; ndx++)
922 {
923 inputs.push_back(tcu::randomVector<float, 4>(rnd, tcu::Vec4(-1.25f), tcu::Vec4(1.25f)));
924 }
925
926 // Large random values.
927 for (int ndx = 0; ndx < 80; ndx++)
928 {
929 inputs.push_back(tcu::randomVector<float, 4>(rnd, tcu::Vec4(-0.5e6f), tcu::Vec4(0.5e6f)));
930 }
931
932 outputs.resize(inputs.size());
933
934 m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
935
936 {
937 const void* in = &inputs[0];
938 void* out = &outputs[0];
939
940 m_executor->execute((int)inputs.size(), &in, &out);
941 }
942
943 // Verify
944 {
945 const int numValues = (int)inputs.size();
946 const int maxPrints = 10;
947 int numFailed = 0;
948
949 for (int valNdx = 0; valNdx < numValues; valNdx++)
950 {
951 const deUint16 ref0 = (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), -1.0f, 1.0f) * 127.0f), -(1<<7), (1<<7)-1);
952 const deUint16 ref1 = (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), -1.0f, 1.0f) * 127.0f), -(1<<7), (1<<7)-1);
953 const deUint16 ref2 = (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].z(), -1.0f, 1.0f) * 127.0f), -(1<<7), (1<<7)-1);
954 const deUint16 ref3 = (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].w(), -1.0f, 1.0f) * 127.0f), -(1<<7), (1<<7)-1);
955 const deUint32 ref = (deUint32(ref3) << 24) | (deUint32(ref2) << 16) | (deUint32(ref1) << 8) | deUint32(ref0);
956 const deUint32 res = outputs[valNdx];
957 const deUint16 res0 = (deUint8)(res & 0xff);
958 const deUint16 res1 = (deUint8)((res >> 8) & 0xff);
959 const deUint16 res2 = (deUint8)((res >> 16) & 0xff);
960 const deUint16 res3 = (deUint8)((res >> 24) & 0xff);
961 const int diff0 = de::abs((int)ref0 - (int)res0);
962 const int diff1 = de::abs((int)ref1 - (int)res1);
963 const int diff2 = de::abs((int)ref2 - (int)res2);
964 const int diff3 = de::abs((int)ref3 - (int)res3);
965
966 if (diff0 > maxDiff || diff1 > maxDiff || diff2 > maxDiff || diff3 > maxDiff)
967 {
968 if (numFailed < maxPrints)
969 {
970 m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
971 << ", expected packSnorm4x8(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
972 << ", got " << tcu::toHex(res)
973 << "\n diffs = " << tcu::IVec4(diff0, diff1, diff2, diff3) << ", max diff = " << maxDiff
974 << TestLog::EndMessage;
975 }
976 else if (numFailed == maxPrints)
977 m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
978
979 numFailed += 1;
980 }
981 }
982
983 m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
984
985 if (numFailed == 0)
986 return tcu::TestStatus::pass("Pass");
987 else
988 return tcu::TestStatus::fail("Result comparison failed");
989
990 }
991 }
992
993 private:
994 const glu::Precision m_precision;
995 };
996
997 class PackSnorm4x8Case : public ShaderPackingFunctionCase
998 {
999 public:
PackSnorm4x8Case(tcu::TestContext & testCtx,glu::ShaderType shaderType,glu::Precision precision)1000 PackSnorm4x8Case (tcu::TestContext& testCtx, glu::ShaderType shaderType, glu::Precision precision)
1001 : ShaderPackingFunctionCase (testCtx, (string("packsnorm4x8") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(), "packSnorm4x8", shaderType)
1002 , m_precision (precision)
1003 {
1004 m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC4, precision)));
1005 m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
1006
1007 m_spec.source = "out0 = packSnorm4x8(in0);";
1008 }
1009
createInstance(Context & ctx) const1010 TestInstance* createInstance (Context& ctx) const
1011 {
1012 return new PackSnorm4x8CaseInstance(ctx, m_shaderType, m_spec, m_precision, getName());
1013 }
1014
1015 private:
1016 const glu::Precision m_precision;
1017 };
1018
1019 class UnpackSnorm4x8CaseInstance : public ShaderPackingFunctionTestInstance
1020 {
1021 public:
UnpackSnorm4x8CaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,const char * name)1022 UnpackSnorm4x8CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, const char* name)
1023 : ShaderPackingFunctionTestInstance (context, shaderType, spec, name)
1024 {
1025 }
1026
iterate(void)1027 tcu::TestStatus iterate (void)
1028 {
1029 const deUint32 maxDiff = 1; // Rounding error.
1030 de::Random rnd (deStringHash(m_name) ^ 0x776002);
1031 std::vector<deUint32> inputs;
1032 std::vector<tcu::Vec4> outputs;
1033
1034 inputs.push_back(0x00000000u);
1035 inputs.push_back(0x7fff8000u);
1036 inputs.push_back(0x80007fffu);
1037 inputs.push_back(0xffffffffu);
1038 inputs.push_back(0x0001fffeu);
1039
1040 // Random values.
1041 for (int ndx = 0; ndx < 95; ndx++)
1042 inputs.push_back(rnd.getUint32());
1043
1044 outputs.resize(inputs.size());
1045
1046 m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
1047
1048 {
1049 const void* in = &inputs[0];
1050 void* out = &outputs[0];
1051
1052 m_executor->execute((int)inputs.size(), &in, &out);
1053 }
1054
1055 // Verify
1056 {
1057 const int numValues = (int)inputs.size();
1058 const int maxPrints = 10;
1059 int numFailed = 0;
1060
1061 for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
1062 {
1063 const deInt8 in0 = (deInt8)(deUint8)(inputs[valNdx] & 0xff);
1064 const deInt8 in1 = (deInt8)(deUint8)((inputs[valNdx] >> 8) & 0xff);
1065 const deInt8 in2 = (deInt8)(deUint8)((inputs[valNdx] >> 16) & 0xff);
1066 const deInt8 in3 = (deInt8)(deUint8)(inputs[valNdx] >> 24);
1067 const float ref0 = de::clamp(float(in0) / 127.f, -1.0f, 1.0f);
1068 const float ref1 = de::clamp(float(in1) / 127.f, -1.0f, 1.0f);
1069 const float ref2 = de::clamp(float(in2) / 127.f, -1.0f, 1.0f);
1070 const float ref3 = de::clamp(float(in3) / 127.f, -1.0f, 1.0f);
1071 const float res0 = outputs[valNdx].x();
1072 const float res1 = outputs[valNdx].y();
1073 const float res2 = outputs[valNdx].z();
1074 const float res3 = outputs[valNdx].w();
1075
1076 const deUint32 diff0 = getUlpDiff(ref0, res0);
1077 const deUint32 diff1 = getUlpDiff(ref1, res1);
1078 const deUint32 diff2 = getUlpDiff(ref2, res2);
1079 const deUint32 diff3 = getUlpDiff(ref3, res3);
1080
1081 if (diff0 > maxDiff || diff1 > maxDiff || diff2 > maxDiff || diff3 > maxDiff)
1082 {
1083 if (numFailed < maxPrints)
1084 {
1085 m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
1086 << " expected unpackSnorm4x8(" << tcu::toHex(inputs[valNdx]) << ") = "
1087 << "vec4(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ", " << HexFloat(ref2) << ", " << HexFloat(ref3) << ")"
1088 << ", got vec4(" << HexFloat(res0) << ", " << HexFloat(res1) << ", " << HexFloat(res2) << ", " << HexFloat(res3) << ")"
1089 << "\n ULP diffs = (" << diff0 << ", " << diff1 << ", " << diff2 << ", " << diff3 << "), max diff = " << maxDiff
1090 << TestLog::EndMessage;
1091 }
1092 else if (numFailed == maxPrints)
1093 m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
1094
1095 numFailed += 1;
1096 }
1097 }
1098
1099 m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
1100
1101 if (numFailed == 0)
1102 return tcu::TestStatus::pass("Pass");
1103 else
1104 return tcu::TestStatus::fail("Result comparison failed");
1105
1106 }
1107 }
1108 };
1109
1110
1111 class UnpackSnorm4x8Case : public ShaderPackingFunctionCase
1112 {
1113 public:
UnpackSnorm4x8Case(tcu::TestContext & testCtx,glu::ShaderType shaderType)1114 UnpackSnorm4x8Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
1115 : ShaderPackingFunctionCase (testCtx, (string("unpacksnorm4x8") + getShaderTypePostfix(shaderType)).c_str(), "unpackSnorm4x8", shaderType)
1116 {
1117 m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
1118 m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP)));
1119
1120 m_spec.source = "out0 = unpackSnorm4x8(in0);";
1121 }
1122
createInstance(Context & ctx) const1123 TestInstance* createInstance (Context& ctx) const
1124 {
1125 return new UnpackSnorm4x8CaseInstance(ctx, m_shaderType, m_spec, getName());
1126 }
1127
1128 };
1129
1130 class PackUnorm4x8CaseInstance : public ShaderPackingFunctionTestInstance
1131 {
1132 public:
PackUnorm4x8CaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,glu::Precision precision,const char * name)1133 PackUnorm4x8CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, glu::Precision precision, const char* name)
1134 : ShaderPackingFunctionTestInstance (context, shaderType, spec, name)
1135 , m_precision (precision)
1136 {
1137 }
1138
iterate(void)1139 tcu::TestStatus iterate (void)
1140 {
1141 de::Random rnd (deStringHash(m_name) ^ 0x776002);
1142 std::vector<tcu::Vec4> inputs;
1143 std::vector<deUint32> outputs;
1144 const int maxDiff = m_precision == glu::PRECISION_HIGHP ? 1 : // Rounding only.
1145 m_precision == glu::PRECISION_MEDIUMP ? 1 : // (2^-10) * (2^8) + 1
1146 m_precision == glu::PRECISION_LOWP ? 2 : 0; // (2^-8) * (2^8) + 1
1147
1148 // Special values to check.
1149 inputs.push_back(tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f));
1150 inputs.push_back(tcu::Vec4(-1.0f, 1.0f, -1.0f, 1.0f));
1151 inputs.push_back(tcu::Vec4(0.5f, -0.5f, -0.5f, 0.5f));
1152 inputs.push_back(tcu::Vec4(-1.5f, 1.5f, -1.5f, 1.5f));
1153 inputs.push_back(tcu::Vec4(0.25f, -0.75f, -0.25f, 0.75f));
1154
1155 // Random values, mostly in range.
1156 for (int ndx = 0; ndx < 15; ndx++)
1157 {
1158 inputs.push_back(tcu::randomVector<float, 4>(rnd, tcu::Vec4(-0.125f), tcu::Vec4(1.125f)));
1159 }
1160
1161 // Large random values.
1162 for (int ndx = 0; ndx < 80; ndx++)
1163 {
1164 inputs.push_back(tcu::randomVector<float, 4>(rnd, tcu::Vec4(-1e5f), tcu::Vec4(0.9e6f)));
1165 }
1166
1167 outputs.resize(inputs.size());
1168
1169 m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
1170
1171 {
1172 const void* in = &inputs[0];
1173 void* out = &outputs[0];
1174
1175 m_executor->execute((int)inputs.size(), &in, &out);
1176 }
1177
1178 // Verify
1179 {
1180 const int numValues = (int)inputs.size();
1181 const int maxPrints = 10;
1182 int numFailed = 0;
1183
1184 for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
1185 {
1186 const deUint16 ref0 = (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), 0.0f, 1.0f) * 255.0f), 0, (1<<8)-1);
1187 const deUint16 ref1 = (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), 0.0f, 1.0f) * 255.0f), 0, (1<<8)-1);
1188 const deUint16 ref2 = (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].z(), 0.0f, 1.0f) * 255.0f), 0, (1<<8)-1);
1189 const deUint16 ref3 = (deUint8)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].w(), 0.0f, 1.0f) * 255.0f), 0, (1<<8)-1);
1190 const deUint32 ref = (deUint32(ref3) << 24) | (deUint32(ref2) << 16) | (deUint32(ref1) << 8) | deUint32(ref0);
1191 const deUint32 res = outputs[valNdx];
1192 const deUint16 res0 = (deUint8)(res & 0xff);
1193 const deUint16 res1 = (deUint8)((res >> 8) & 0xff);
1194 const deUint16 res2 = (deUint8)((res >> 16) & 0xff);
1195 const deUint16 res3 = (deUint8)((res >> 24) & 0xff);
1196 const int diff0 = de::abs((int)ref0 - (int)res0);
1197 const int diff1 = de::abs((int)ref1 - (int)res1);
1198 const int diff2 = de::abs((int)ref2 - (int)res2);
1199 const int diff3 = de::abs((int)ref3 - (int)res3);
1200
1201 if (diff0 > maxDiff || diff1 > maxDiff || diff2 > maxDiff || diff3 > maxDiff)
1202 {
1203 if (numFailed < maxPrints)
1204 {
1205 m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
1206 << ", expected packUnorm4x8(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
1207 << ", got " << tcu::toHex(res)
1208 << "\n diffs = " << tcu::IVec4(diff0, diff1, diff2, diff3) << ", max diff = " << maxDiff
1209 << TestLog::EndMessage;
1210 }
1211 else if (numFailed == maxPrints)
1212 m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
1213
1214 numFailed += 1;
1215 }
1216 }
1217
1218 m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
1219
1220 if (numFailed == 0)
1221 return tcu::TestStatus::pass("Pass");
1222 else
1223 return tcu::TestStatus::fail("Result comparison failed");
1224
1225 }
1226 }
1227
1228 private:
1229 const glu::Precision m_precision;
1230 };
1231
1232 class PackUnorm4x8Case : public ShaderPackingFunctionCase
1233 {
1234 public:
PackUnorm4x8Case(tcu::TestContext & testCtx,glu::ShaderType shaderType,glu::Precision precision)1235 PackUnorm4x8Case (tcu::TestContext& testCtx, glu::ShaderType shaderType, glu::Precision precision)
1236 : ShaderPackingFunctionCase (testCtx, (string("packunorm4x8") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(), "packUnorm4x8", shaderType)
1237 , m_precision (precision)
1238 {
1239 m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC4, precision)));
1240 m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
1241
1242 m_spec.source = "out0 = packUnorm4x8(in0);";
1243 }
1244
createInstance(Context & ctx) const1245 TestInstance* createInstance (Context& ctx) const
1246 {
1247 return new PackUnorm4x8CaseInstance(ctx, m_shaderType, m_spec, m_precision, getName());
1248 }
1249
1250 private:
1251 const glu::Precision m_precision;
1252 };
1253
1254 class UnpackUnorm4x8CaseInstance : public ShaderPackingFunctionTestInstance
1255 {
1256 public:
UnpackUnorm4x8CaseInstance(Context & context,glu::ShaderType shaderType,const ShaderSpec & spec,const char * name)1257 UnpackUnorm4x8CaseInstance (Context& context, glu::ShaderType shaderType, const ShaderSpec& spec, const char* name)
1258 : ShaderPackingFunctionTestInstance (context, shaderType, spec, name)
1259 {
1260 }
1261
iterate(void)1262 tcu::TestStatus iterate (void)
1263 {
1264 const deUint32 maxDiff = 1; // Rounding error.
1265 de::Random rnd (deStringHash(m_name) ^ 0x776002);
1266 std::vector<deUint32> inputs;
1267 std::vector<tcu::Vec4> outputs;
1268
1269 inputs.push_back(0x00000000u);
1270 inputs.push_back(0x7fff8000u);
1271 inputs.push_back(0x80007fffu);
1272 inputs.push_back(0xffffffffu);
1273 inputs.push_back(0x0001fffeu);
1274
1275 // Random values.
1276 for (int ndx = 0; ndx < 95; ndx++)
1277 inputs.push_back(rnd.getUint32());
1278
1279 outputs.resize(inputs.size());
1280
1281 m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values" << tcu::TestLog::EndMessage;
1282
1283 {
1284 const void* in = &inputs[0];
1285 void* out = &outputs[0];
1286
1287 m_executor->execute((int)inputs.size(), &in, &out);
1288 }
1289
1290 // Verify
1291 {
1292 const int numValues = (int)inputs.size();
1293 const int maxPrints = 10;
1294 int numFailed = 0;
1295
1296 for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
1297 {
1298 const deUint8 in0 = (deUint8)(inputs[valNdx] & 0xff);
1299 const deUint8 in1 = (deUint8)((inputs[valNdx] >> 8) & 0xff);
1300 const deUint8 in2 = (deUint8)((inputs[valNdx] >> 16) & 0xff);
1301 const deUint8 in3 = (deUint8)(inputs[valNdx] >> 24);
1302 const float ref0 = de::clamp(float(in0) / 255.f, 0.0f, 1.0f);
1303 const float ref1 = de::clamp(float(in1) / 255.f, 0.0f, 1.0f);
1304 const float ref2 = de::clamp(float(in2) / 255.f, 0.0f, 1.0f);
1305 const float ref3 = de::clamp(float(in3) / 255.f, 0.0f, 1.0f);
1306 const float res0 = outputs[valNdx].x();
1307 const float res1 = outputs[valNdx].y();
1308 const float res2 = outputs[valNdx].z();
1309 const float res3 = outputs[valNdx].w();
1310
1311 const deUint32 diff0 = getUlpDiff(ref0, res0);
1312 const deUint32 diff1 = getUlpDiff(ref1, res1);
1313 const deUint32 diff2 = getUlpDiff(ref2, res2);
1314 const deUint32 diff3 = getUlpDiff(ref3, res3);
1315
1316 if (diff0 > maxDiff || diff1 > maxDiff || diff2 > maxDiff || diff3 > maxDiff)
1317 {
1318 if (numFailed < maxPrints)
1319 {
1320 m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
1321 << " expected unpackUnorm4x8(" << tcu::toHex(inputs[valNdx]) << ") = "
1322 << "vec4(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ", " << HexFloat(ref2) << ", " << HexFloat(ref3) << ")"
1323 << ", got vec4(" << HexFloat(res0) << ", " << HexFloat(res1) << ", " << HexFloat(res2) << ", " << HexFloat(res3) << ")"
1324 << "\n ULP diffs = (" << diff0 << ", " << diff1 << ", " << diff2 << ", " << diff3 << "), max diff = " << maxDiff
1325 << TestLog::EndMessage;
1326 }
1327 else if (numFailed == maxPrints)
1328 m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
1329
1330 numFailed += 1;
1331 }
1332 }
1333
1334 m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed" << TestLog::EndMessage;
1335
1336 if (numFailed == 0)
1337 return tcu::TestStatus::pass("Pass");
1338 else
1339 return tcu::TestStatus::fail("Result comparison failed");
1340
1341 }
1342 }
1343 };
1344
1345 class UnpackUnorm4x8Case : public ShaderPackingFunctionCase
1346 {
1347 public:
UnpackUnorm4x8Case(tcu::TestContext & testCtx,glu::ShaderType shaderType)1348 UnpackUnorm4x8Case (tcu::TestContext& testCtx, glu::ShaderType shaderType)
1349 : ShaderPackingFunctionCase (testCtx, (string("unpackunorm4x8") + getShaderTypePostfix(shaderType)).c_str(), "unpackUnorm4x8", shaderType)
1350 {
1351 m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
1352 m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP)));
1353
1354 m_spec.source = "out0 = unpackUnorm4x8(in0);";
1355 }
1356
createInstance(Context & ctx) const1357 TestInstance* createInstance (Context& ctx) const
1358 {
1359 return new UnpackUnorm4x8CaseInstance(ctx, m_shaderType, m_spec, getName());
1360 }
1361
1362 };
1363
ShaderPackingFunctionTests(tcu::TestContext & testCtx)1364 ShaderPackingFunctionTests::ShaderPackingFunctionTests (tcu::TestContext& testCtx)
1365 : tcu::TestCaseGroup (testCtx, "pack_unpack", "Floating-point pack and unpack function tests")
1366 {
1367 }
1368
~ShaderPackingFunctionTests(void)1369 ShaderPackingFunctionTests::~ShaderPackingFunctionTests (void)
1370 {
1371 }
1372
init(void)1373 void ShaderPackingFunctionTests::init (void)
1374 {
1375 // New built-in functions in GLES 3.1
1376 {
1377 const glu::ShaderType allShaderTypes[] =
1378 {
1379 glu::SHADERTYPE_VERTEX,
1380 glu::SHADERTYPE_TESSELLATION_CONTROL,
1381 glu::SHADERTYPE_TESSELLATION_EVALUATION,
1382 glu::SHADERTYPE_GEOMETRY,
1383 glu::SHADERTYPE_FRAGMENT,
1384 glu::SHADERTYPE_COMPUTE
1385 };
1386
1387 // packSnorm4x8
1388 for (int prec = glu::PRECISION_MEDIUMP; prec < glu::PRECISION_LAST; prec++)
1389 {
1390 for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(allShaderTypes); shaderTypeNdx++)
1391 addChild(new PackSnorm4x8Case(m_testCtx, allShaderTypes[shaderTypeNdx], glu::Precision(prec)));
1392 }
1393
1394 // unpackSnorm4x8
1395 for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(allShaderTypes); shaderTypeNdx++)
1396 addChild(new UnpackSnorm4x8Case(m_testCtx, allShaderTypes[shaderTypeNdx]));
1397
1398 // packUnorm4x8
1399 for (int prec = glu::PRECISION_MEDIUMP; prec < glu::PRECISION_LAST; prec++)
1400 {
1401 for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(allShaderTypes); shaderTypeNdx++)
1402 addChild(new PackUnorm4x8Case(m_testCtx, allShaderTypes[shaderTypeNdx], glu::Precision(prec)));
1403 }
1404
1405 // unpackUnorm4x8
1406 for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(allShaderTypes); shaderTypeNdx++)
1407 addChild(new UnpackUnorm4x8Case(m_testCtx, allShaderTypes[shaderTypeNdx]));
1408 }
1409
1410 // GLES 3 functions in new shader types.
1411 {
1412 const glu::ShaderType newShaderTypes[] =
1413 {
1414 glu::SHADERTYPE_GEOMETRY,
1415 glu::SHADERTYPE_COMPUTE
1416 };
1417
1418 // packSnorm2x16
1419 for (int prec = glu::PRECISION_MEDIUMP; prec < glu::PRECISION_LAST; prec++)
1420 {
1421 for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1422 addChild(new PackSnorm2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx], glu::Precision(prec)));
1423 }
1424
1425 // unpackSnorm2x16
1426 for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1427 addChild(new UnpackSnorm2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx]));
1428
1429 // packUnorm2x16
1430 for (int prec = glu::PRECISION_MEDIUMP; prec < glu::PRECISION_LAST; prec++)
1431 {
1432 for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1433 addChild(new PackUnorm2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx], glu::Precision(prec)));
1434 }
1435
1436 // unpackUnorm2x16
1437 for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1438 addChild(new UnpackUnorm2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx]));
1439
1440 // packHalf2x16
1441 for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1442 addChild(new PackHalf2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx]));
1443
1444 // unpackHalf2x16
1445 for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1446 addChild(new UnpackHalf2x16Case(m_testCtx, newShaderTypes[shaderTypeNdx]));
1447 }
1448 }
1449
1450 } // shaderexecutor
1451 } // vkt
1452