1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.1 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 Shader atomic operation tests.
22 *//*--------------------------------------------------------------------*/
23
24 #include "es31fShaderAtomicOpTests.hpp"
25 #include "gluShaderProgram.hpp"
26 #include "gluShaderUtil.hpp"
27 #include "gluRenderContext.hpp"
28 #include "gluObjectWrapper.hpp"
29 #include "gluProgramInterfaceQuery.hpp"
30 #include "tcuVector.hpp"
31 #include "tcuTestLog.hpp"
32 #include "tcuVectorUtil.hpp"
33 #include "tcuFormatUtil.hpp"
34 #include "deStringUtil.hpp"
35 #include "deRandom.hpp"
36 #include "glwFunctions.hpp"
37 #include "glwEnums.hpp"
38
39 #include <algorithm>
40 #include <set>
41
42 namespace deqp
43 {
44 namespace gles31
45 {
46 namespace Functional
47 {
48
49 using std::string;
50 using std::vector;
51 using tcu::TestLog;
52 using tcu::UVec3;
53 using std::set;
54 using namespace glu;
55
56 template<typename T, int Size>
product(const tcu::Vector<T,Size> & v)57 static inline T product (const tcu::Vector<T, Size>& v)
58 {
59 T res = v[0];
60 for (int ndx = 1; ndx < Size; ndx++)
61 res *= v[ndx];
62 return res;
63 }
64
65 class ShaderAtomicOpCase : public TestCase
66 {
67 public:
68 ShaderAtomicOpCase (Context& context, const char* name, const char* funcName, AtomicOperandType operandType, DataType type, Precision precision, const UVec3& workGroupSize);
69 ~ShaderAtomicOpCase (void);
70
71 void init (void);
72 void deinit (void);
73 IterateResult iterate (void);
74
75 protected:
76 virtual void getInputs (int numValues, int stride, void* inputs) const = 0;
77 virtual bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const = 0;
78
79 const string m_funcName;
80 const AtomicOperandType m_operandType;
81 const DataType m_type;
82 const Precision m_precision;
83
84 const UVec3 m_workGroupSize;
85 const UVec3 m_numWorkGroups;
86
87 deUint32 m_initialValue;
88
89 private:
90 ShaderAtomicOpCase (const ShaderAtomicOpCase& other);
91 ShaderAtomicOpCase& operator= (const ShaderAtomicOpCase& other);
92
93 ShaderProgram* m_program;
94 };
95
ShaderAtomicOpCase(Context & context,const char * name,const char * funcName,AtomicOperandType operandType,DataType type,Precision precision,const UVec3 & workGroupSize)96 ShaderAtomicOpCase::ShaderAtomicOpCase (Context& context, const char* name, const char* funcName, AtomicOperandType operandType, DataType type, Precision precision, const UVec3& workGroupSize)
97 : TestCase (context, name, funcName)
98 , m_funcName (funcName)
99 , m_operandType (operandType)
100 , m_type (type)
101 , m_precision (precision)
102 , m_workGroupSize (workGroupSize)
103 , m_numWorkGroups (4,4,4)
104 , m_initialValue (0)
105 , m_program (DE_NULL)
106 {
107 }
108
~ShaderAtomicOpCase(void)109 ShaderAtomicOpCase::~ShaderAtomicOpCase (void)
110 {
111 ShaderAtomicOpCase::deinit();
112 }
113
init(void)114 void ShaderAtomicOpCase::init (void)
115 {
116 const bool isSSBO = m_operandType == ATOMIC_OPERAND_BUFFER_VARIABLE;
117 const char* precName = getPrecisionName(m_precision);
118 const char* typeName = getDataTypeName(m_type);
119
120 const DataType outType = isSSBO ? m_type : glu::TYPE_UINT;
121 const char* outTypeName = getDataTypeName(outType);
122
123 const deUint32 numValues = product(m_workGroupSize)*product(m_numWorkGroups);
124 std::ostringstream src;
125
126 src << "#version 310 es\n"
127 << "layout(local_size_x = " << m_workGroupSize.x()
128 << ", local_size_y = " << m_workGroupSize.y()
129 << ", local_size_z = " << m_workGroupSize.z() << ") in;\n"
130 << "layout(binding = 0) buffer InOut\n"
131 << "{\n"
132 << " " << precName << " " << typeName << " inputValues[" << numValues << "];\n"
133 << " " << precName << " " << outTypeName << " outputValues[" << numValues << "];\n"
134 << " " << (isSSBO ? "coherent " : "") << precName << " " << outTypeName << " groupValues[" << product(m_numWorkGroups) << "];\n"
135 << "} sb_inout;\n";
136
137 if (!isSSBO)
138 src << "shared " << precName << " " << typeName << " s_var;\n";
139
140 src << "\n"
141 << "void main (void)\n"
142 << "{\n"
143 << " uint localSize = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z;\n"
144 << " uint globalNdx = gl_NumWorkGroups.x*gl_NumWorkGroups.y*gl_WorkGroupID.z + gl_NumWorkGroups.x*gl_WorkGroupID.y + gl_WorkGroupID.x;\n"
145 << " uint globalOffs = localSize*globalNdx;\n"
146 << " uint offset = globalOffs + gl_LocalInvocationIndex;\n"
147 << "\n";
148
149 if (isSSBO)
150 {
151 DE_ASSERT(outType == m_type);
152 src << " sb_inout.outputValues[offset] = " << m_funcName << "(sb_inout.groupValues[globalNdx], sb_inout.inputValues[offset]);\n";
153 }
154 else
155 {
156 const string castBeg = outType != m_type ? (string(outTypeName) + "(") : string("");
157 const char* const castEnd = outType != m_type ? ")" : "";
158
159 src << " if (gl_LocalInvocationIndex == 0u)\n"
160 << " s_var = " << typeName << "(" << tcu::toHex(m_initialValue) << "u);\n"
161 << " barrier();\n"
162 << " " << precName << " " << typeName << " res = " << m_funcName << "(s_var, sb_inout.inputValues[offset]);\n"
163 << " sb_inout.outputValues[offset] = " << castBeg << "res" << castEnd << ";\n"
164 << " barrier();\n"
165 << " if (gl_LocalInvocationIndex == 0u)\n"
166 << " sb_inout.groupValues[globalNdx] = " << castBeg << "s_var" << castEnd << ";\n";
167 }
168
169 src << "}\n";
170
171 DE_ASSERT(!m_program);
172 m_program = new ShaderProgram(m_context.getRenderContext(), ProgramSources() << ComputeSource(src.str()));
173
174 m_testCtx.getLog() << *m_program;
175
176 if (!m_program->isOk())
177 {
178 delete m_program;
179 m_program = DE_NULL;
180 throw tcu::TestError("Compile failed");
181 }
182 }
183
deinit(void)184 void ShaderAtomicOpCase::deinit (void)
185 {
186 delete m_program;
187 m_program = DE_NULL;
188 }
189
iterate(void)190 ShaderAtomicOpCase::IterateResult ShaderAtomicOpCase::iterate (void)
191 {
192 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
193 const deUint32 program = m_program->getProgram();
194 const Buffer inoutBuffer (m_context.getRenderContext());
195 const deUint32 blockNdx = gl.getProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, "InOut");
196 const InterfaceBlockInfo blockInfo = getProgramInterfaceBlockInfo(gl, program, GL_SHADER_STORAGE_BLOCK, blockNdx);
197 const deUint32 inVarNdx = gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.inputValues[0]");
198 const InterfaceVariableInfo inVarInfo = getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, inVarNdx);
199 const deUint32 outVarNdx = gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.outputValues[0]");
200 const InterfaceVariableInfo outVarInfo = getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, outVarNdx);
201 const deUint32 groupVarNdx = gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.groupValues[0]");
202 const InterfaceVariableInfo groupVarInfo = getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, groupVarNdx);
203 const deUint32 numValues = product(m_workGroupSize)*product(m_numWorkGroups);
204
205 TCU_CHECK(inVarInfo.arraySize == numValues &&
206 outVarInfo.arraySize == numValues &&
207 groupVarInfo.arraySize == product(m_numWorkGroups));
208
209 gl.useProgram(program);
210
211 // Setup buffer.
212 {
213 vector<deUint8> bufData(blockInfo.dataSize);
214 std::fill(bufData.begin(), bufData.end(), 0);
215
216 getInputs((int)numValues, (int)inVarInfo.arrayStride, &bufData[0] + inVarInfo.offset);
217
218 if (m_operandType == ATOMIC_OPERAND_BUFFER_VARIABLE)
219 {
220 for (deUint32 valNdx = 0; valNdx < product(m_numWorkGroups); valNdx++)
221 *(deUint32*)(&bufData[0] + groupVarInfo.offset + groupVarInfo.arrayStride*valNdx) = m_initialValue;
222 }
223
224 gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *inoutBuffer);
225 gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockInfo.dataSize, &bufData[0], GL_STATIC_READ);
226 gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *inoutBuffer);
227 GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
228 }
229
230 gl.dispatchCompute(m_numWorkGroups.x(), m_numWorkGroups.y(), m_numWorkGroups.z());
231
232 // Read back and compare
233 {
234 const void* resPtr = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, blockInfo.dataSize, GL_MAP_READ_BIT);
235 bool isOk = true;
236
237 GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
238 TCU_CHECK(resPtr);
239
240 isOk = verify((int)numValues,
241 (int)inVarInfo.arrayStride, (const deUint8*)resPtr + inVarInfo.offset,
242 (int)outVarInfo.arrayStride, (const deUint8*)resPtr + outVarInfo.offset,
243 (int)groupVarInfo.arrayStride, (const deUint8*)resPtr + groupVarInfo.offset);
244
245 gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
246 GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer()");
247
248 m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
249 isOk ? "Pass" : "Comparison failed");
250 }
251
252 return STOP;
253 }
254
255 class ShaderAtomicAddCase : public ShaderAtomicOpCase
256 {
257 public:
ShaderAtomicAddCase(Context & context,const char * name,AtomicOperandType operandType,DataType type,Precision precision)258 ShaderAtomicAddCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
259 : ShaderAtomicOpCase(context, name, "atomicAdd", operandType, type, precision, UVec3(3,2,1))
260 {
261 m_initialValue = 1;
262 }
263
264 protected:
getInputs(int numValues,int stride,void * inputs) const265 void getInputs (int numValues, int stride, void* inputs) const
266 {
267 de::Random rnd (deStringHash(getName()));
268 const int maxVal = m_precision == PRECISION_LOWP ? 2 : 32;
269 const int minVal = 1;
270
271 // \todo [2013-09-04 pyry] Negative values!
272
273 for (int valNdx = 0; valNdx < numValues; valNdx++)
274 *(int*)((deUint8*)inputs + stride*valNdx) = rnd.getInt(minVal, maxVal);
275 }
276
verify(int numValues,int inputStride,const void * inputs,int outputStride,const void * outputs,int groupStride,const void * groupOutputs) const277 bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
278 {
279 const int workGroupSize = (int)product(m_workGroupSize);
280 const int numWorkGroups = numValues/workGroupSize;
281
282 for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
283 {
284 const int groupOffset = groupNdx*workGroupSize;
285 const int groupOutput = *(const int*)((const deUint8*)groupOutputs + groupNdx*groupStride);
286 set<int> outValues;
287 bool maxFound = false;
288 int valueSum = (int)m_initialValue;
289
290 for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
291 {
292 const int inputValue = *(const int*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
293 valueSum += inputValue;
294 }
295
296 if (groupOutput != valueSum)
297 {
298 m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected sum " << valueSum << ", got " << groupOutput << TestLog::EndMessage;
299 return false;
300 }
301
302 for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
303 {
304 const int inputValue = *(const int*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
305 const int outputValue = *(const int*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
306
307 if (!de::inRange(outputValue, (int)m_initialValue, valueSum-inputValue))
308 {
309 m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
310 << ": expected value in range [" << m_initialValue << ", " << (valueSum-inputValue)
311 << "], got " << outputValue
312 << TestLog::EndMessage;
313 return false;
314 }
315
316 if (outValues.find(outputValue) != outValues.end())
317 {
318 m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
319 << ": found duplicate value " << outputValue
320 << TestLog::EndMessage;
321 return false;
322 }
323
324 outValues.insert(outputValue);
325 if (outputValue == valueSum-inputValue)
326 maxFound = true;
327 }
328
329 if (!maxFound)
330 {
331 m_testCtx.getLog() << TestLog::Message << "ERROR: could not find maximum expected value from group " << groupNdx << TestLog::EndMessage;
332 return false;
333 }
334
335 if (outValues.find((int)m_initialValue) == outValues.end())
336 {
337 m_testCtx.getLog() << TestLog::Message << "ERROR: could not find initial value from group " << groupNdx << TestLog::EndMessage;
338 return false;
339 }
340 }
341
342 return true;
343 }
344 };
345
346
getPrecisionNumIntegerBits(glu::Precision precision)347 static int getPrecisionNumIntegerBits (glu::Precision precision)
348 {
349 switch (precision)
350 {
351 case glu::PRECISION_HIGHP: return 32;
352 case glu::PRECISION_MEDIUMP: return 16;
353 case glu::PRECISION_LOWP: return 9;
354 default:
355 DE_ASSERT(false);
356 return 0;
357 }
358 }
359
getPrecisionMask(int numPreciseBits)360 static deUint32 getPrecisionMask (int numPreciseBits)
361 {
362 // \note: bit shift with larger or equal than var length is undefined, use 64 bit ints
363 return (deUint32)((((deUint64)1u) << numPreciseBits) - 1) ;
364 }
365
intEqualsAfterUintCast(deInt32 value,deUint32 casted,glu::Precision precision)366 static bool intEqualsAfterUintCast (deInt32 value, deUint32 casted, glu::Precision precision)
367 {
368 // Bit format of 'casted' = [ uint -> highp uint promotion bits (0) ] [ sign extend bits (s) ] [ value bits ]
369 // |--min len---|
370 // |---------------signed length---------|
371 // |-------------------------------- highp uint length ----------------------------|
372
373 const deUint32 reference = (deUint32)value;
374 const int signBitOn = value < 0;
375 const int numPreciseBits = getPrecisionNumIntegerBits(precision);
376 const deUint32 preciseMask = getPrecisionMask(numPreciseBits);
377
378 // Lowest N bits must match, N = minimum precision
379 if ((reference & preciseMask) != (casted & preciseMask))
380 return false;
381
382 // Other lowest bits must match the sign and the remaining (topmost) if any must be 0
383 for (int signedIntegerLength = numPreciseBits; signedIntegerLength <= 32; ++signedIntegerLength)
384 {
385 const deUint32 signBits = (signBitOn) ? (getPrecisionMask(signedIntegerLength)) : (0u);
386
387 if ((signBits & ~preciseMask) == (casted & ~preciseMask))
388 return true;
389 }
390 return false;
391 }
392
containsAfterUintCast(const std::set<deInt32> & haystack,deUint32 needle,glu::Precision precision)393 static bool containsAfterUintCast (const std::set<deInt32>& haystack, deUint32 needle, glu::Precision precision)
394 {
395 for (std::set<deInt32>::const_iterator it = haystack.begin(); it != haystack.end(); ++it)
396 if (intEqualsAfterUintCast(*it, needle, precision))
397 return true;
398 return false;
399 }
400
containsAfterUintCast(const std::set<deUint32> & haystack,deInt32 needle,glu::Precision precision)401 static bool containsAfterUintCast (const std::set<deUint32>& haystack, deInt32 needle, glu::Precision precision)
402 {
403 for (std::set<deUint32>::const_iterator it = haystack.begin(); it != haystack.end(); ++it)
404 if (intEqualsAfterUintCast(needle, *it, precision))
405 return true;
406 return false;
407 }
408
409 class ShaderAtomicMinCase : public ShaderAtomicOpCase
410 {
411 public:
ShaderAtomicMinCase(Context & context,const char * name,AtomicOperandType operandType,DataType type,Precision precision)412 ShaderAtomicMinCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
413 : ShaderAtomicOpCase(context, name, "atomicMin", operandType, type, precision, UVec3(3,2,1))
414 {
415 m_initialValue = m_precision == PRECISION_LOWP ? 100 : 1000;
416 }
417
418 protected:
getInputs(int numValues,int stride,void * inputs) const419 void getInputs (int numValues, int stride, void* inputs) const
420 {
421 de::Random rnd (deStringHash(getName()));
422 const bool isSigned = m_type == TYPE_INT;
423 const int maxVal = m_precision == PRECISION_LOWP ? 100 : 1000;
424 const int minVal = isSigned ? -maxVal : 0;
425
426 for (int valNdx = 0; valNdx < numValues; valNdx++)
427 *(int*)((deUint8*)inputs + stride*valNdx) = rnd.getInt(minVal, maxVal);
428 }
429
verify(int numValues,int inputStride,const void * inputs,int outputStride,const void * outputs,int groupStride,const void * groupOutputs) const430 bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
431 {
432 const int workGroupSize = (int)product(m_workGroupSize);
433 const int numWorkGroups = numValues/workGroupSize;
434 bool anyError = false;
435
436 for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
437 {
438 const int groupOffset = groupNdx*workGroupSize;
439 const deUint32 groupOutput = *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
440 set<deInt32> inValues;
441 set<deUint32> outValues;
442 int minValue = (int)m_initialValue;
443
444 for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
445 {
446 const deInt32 inputValue = *(const deInt32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
447 inValues.insert(inputValue);
448 minValue = de::min(inputValue, minValue);
449 }
450
451 if (!intEqualsAfterUintCast(minValue, groupOutput, m_precision))
452 {
453 m_testCtx.getLog()
454 << TestLog::Message
455 << "ERROR: at group " << groupNdx
456 << ": expected minimum " << minValue << " (" << tcu::Format::Hex<8>((deUint32)minValue) << ")"
457 << ", got " << groupOutput << " (" << tcu::Format::Hex<8>(groupOutput) << ")"
458 << TestLog::EndMessage;
459 anyError = true;
460 }
461
462 for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
463 {
464 const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
465
466 if (!containsAfterUintCast(inValues, outputValue, m_precision) &&
467 !intEqualsAfterUintCast((deInt32)m_initialValue, outputValue, m_precision))
468 {
469 m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
470 << ": found unexpected value " << outputValue
471 << " (" << tcu::Format::Hex<8>(outputValue) << ")"
472 << TestLog::EndMessage;
473 anyError = true;
474 }
475
476 outValues.insert(outputValue);
477 }
478
479 if (!containsAfterUintCast(outValues, (int)m_initialValue, m_precision))
480 {
481 m_testCtx.getLog() << TestLog::Message << "ERROR: could not find initial value from group " << groupNdx << TestLog::EndMessage;
482 anyError = true;
483 }
484 }
485
486 return !anyError;
487 }
488 };
489
490 class ShaderAtomicMaxCase : public ShaderAtomicOpCase
491 {
492 public:
ShaderAtomicMaxCase(Context & context,const char * name,AtomicOperandType operandType,DataType type,Precision precision)493 ShaderAtomicMaxCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
494 : ShaderAtomicOpCase(context, name, "atomicMax", operandType, type, precision, UVec3(3,2,1))
495 {
496 const bool isSigned = m_type == TYPE_INT;
497 m_initialValue = isSigned ? (m_precision == PRECISION_LOWP ? -100 : -1000) : 0;
498 }
499
500 protected:
getInputs(int numValues,int stride,void * inputs) const501 void getInputs (int numValues, int stride, void* inputs) const
502 {
503 de::Random rnd (deStringHash(getName()));
504 const bool isSigned = m_type == TYPE_INT;
505 const int maxVal = m_precision == PRECISION_LOWP ? 100 : 1000;
506 const int minVal = isSigned ? -maxVal : 0;
507
508 for (int valNdx = 0; valNdx < numValues; valNdx++)
509 *(int*)((deUint8*)inputs + stride*valNdx) = rnd.getInt(minVal, maxVal);
510 }
511
verify(int numValues,int inputStride,const void * inputs,int outputStride,const void * outputs,int groupStride,const void * groupOutputs) const512 bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
513 {
514 const int workGroupSize = (int)product(m_workGroupSize);
515 const int numWorkGroups = numValues/workGroupSize;
516 bool anyError = false;
517
518 for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
519 {
520 const int groupOffset = groupNdx*workGroupSize;
521 const deUint32 groupOutput = *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
522 set<int> inValues;
523 set<deUint32> outValues;
524 int maxValue = (int)m_initialValue;
525
526 for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
527 {
528 const deInt32 inputValue = *(const deInt32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
529 inValues.insert(inputValue);
530 maxValue = de::max(maxValue, inputValue);
531 }
532
533 if (!intEqualsAfterUintCast(maxValue, groupOutput, m_precision))
534 {
535 m_testCtx.getLog()
536 << TestLog::Message
537 << "ERROR: at group " << groupNdx
538 << ": expected maximum " << maxValue << " (" << tcu::Format::Hex<8>((deUint32)maxValue) << ")"
539 << ", got " << groupOutput << " (" << tcu::Format::Hex<8>(groupOutput) << ")"
540 << TestLog::EndMessage;
541 anyError = true;
542 }
543
544 for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
545 {
546 const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
547
548 if (!containsAfterUintCast(inValues, outputValue, m_precision) &&
549 !intEqualsAfterUintCast((deInt32)m_initialValue, outputValue, m_precision))
550 {
551 m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
552 << ": found unexpected value " << outputValue
553 << " (" << tcu::Format::Hex<8>(outputValue) << ")"
554 << TestLog::EndMessage;
555 anyError = true;
556 }
557
558 outValues.insert(outputValue);
559 }
560
561 if (!containsAfterUintCast(outValues, (int)m_initialValue, m_precision))
562 {
563 m_testCtx.getLog() << TestLog::Message << "ERROR: could not find initial value from group " << groupNdx << TestLog::EndMessage;
564 anyError = true;
565 }
566 }
567
568 return !anyError;
569 }
570 };
571
572 class ShaderAtomicAndCase : public ShaderAtomicOpCase
573 {
574 public:
ShaderAtomicAndCase(Context & context,const char * name,AtomicOperandType operandType,DataType type,Precision precision)575 ShaderAtomicAndCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
576 : ShaderAtomicOpCase(context, name, "atomicAnd", operandType, type, precision, UVec3(3,2,1))
577 {
578 const int numBits = m_precision == PRECISION_HIGHP ? 32 :
579 m_precision == PRECISION_MEDIUMP ? 16 : 8;
580 const deUint32 valueMask = numBits == 32 ? ~0u : (1u<<numBits)-1u;
581 m_initialValue = ~((1u<<(numBits-1u)) | 1u) & valueMask; // All bits except lowest and highest set.
582 }
583
584 protected:
getInputs(int numValues,int stride,void * inputs) const585 void getInputs (int numValues, int stride, void* inputs) const
586 {
587 de::Random rnd (deStringHash(getName()));
588 const int workGroupSize = (int)product(m_workGroupSize);
589 const int numWorkGroups = numValues/workGroupSize;
590 const int numBits = m_precision == PRECISION_HIGHP ? 32 :
591 m_precision == PRECISION_MEDIUMP ? 16 : 8;
592 const deUint32 valueMask = numBits == 32 ? ~0u : (1u<<numBits)-1u;
593
594 for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
595 {
596 const int groupOffset = groupNdx*workGroupSize;
597 const deUint32 groupMask = 1<<rnd.getInt(0, numBits-2); // One bit is always set.
598
599 for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
600 *(deUint32*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = (rnd.getUint32() & valueMask) | groupMask;
601 }
602 }
603
verify(int numValues,int inputStride,const void * inputs,int outputStride,const void * outputs,int groupStride,const void * groupOutputs) const604 bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
605 {
606 const int workGroupSize = (int)product(m_workGroupSize);
607 const int numWorkGroups = numValues/workGroupSize;
608
609 for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
610 {
611 const int groupOffset = groupNdx*workGroupSize;
612 const deUint32 groupOutput = *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
613 deUint32 expectedValue = m_initialValue;
614
615 for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
616 {
617 const deUint32 inputValue = *(const deUint32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
618 expectedValue &= inputValue;
619 }
620
621 if (expectedValue != groupOutput)
622 {
623 m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected " << tcu::toHex(expectedValue) << ", got " << tcu::toHex(groupOutput) << TestLog::EndMessage;
624 return false;
625 }
626
627 for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
628 {
629 const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
630
631 if ((outputValue & ~m_initialValue) != 0)
632 {
633 m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
634 << ": found unexpected value " << tcu::toHex(outputValue)
635 << TestLog::EndMessage;
636 return false;
637 }
638 }
639 }
640
641 return true;
642 }
643 };
644
645 class ShaderAtomicOrCase : public ShaderAtomicOpCase
646 {
647 public:
ShaderAtomicOrCase(Context & context,const char * name,AtomicOperandType operandType,DataType type,Precision precision)648 ShaderAtomicOrCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
649 : ShaderAtomicOpCase(context, name, "atomicOr", operandType, type, precision, UVec3(3,2,1))
650 {
651 m_initialValue = 1u; // Lowest bit set.
652 }
653
654 protected:
getInputs(int numValues,int stride,void * inputs) const655 void getInputs (int numValues, int stride, void* inputs) const
656 {
657 de::Random rnd (deStringHash(getName()));
658 const int workGroupSize = (int)product(m_workGroupSize);
659 const int numWorkGroups = numValues/workGroupSize;
660 const int numBits = m_precision == PRECISION_HIGHP ? 32 :
661 m_precision == PRECISION_MEDIUMP ? 16 : 8;
662
663 for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
664 {
665 const int groupOffset = groupNdx*workGroupSize;
666
667 for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
668 *(deUint32*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = 1u<<rnd.getInt(0, numBits-1);
669 }
670 }
671
verify(int numValues,int inputStride,const void * inputs,int outputStride,const void * outputs,int groupStride,const void * groupOutputs) const672 bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
673 {
674 const int workGroupSize = (int)product(m_workGroupSize);
675 const int numWorkGroups = numValues/workGroupSize;
676
677 for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
678 {
679 const int groupOffset = groupNdx*workGroupSize;
680 const deUint32 groupOutput = *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
681 deUint32 expectedValue = m_initialValue;
682
683 for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
684 {
685 const deUint32 inputValue = *(const deUint32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
686 expectedValue |= inputValue;
687 }
688
689 if (expectedValue != groupOutput)
690 {
691 m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected " << tcu::toHex(expectedValue) << ", got " << tcu::toHex(groupOutput) << TestLog::EndMessage;
692 return false;
693 }
694
695 for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
696 {
697 const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
698
699 if ((outputValue & m_initialValue) == 0)
700 {
701 m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
702 << ": found unexpected value " << tcu::toHex(outputValue)
703 << TestLog::EndMessage;
704 return false;
705 }
706 }
707 }
708
709 return true;
710 }
711 };
712
713 class ShaderAtomicXorCase : public ShaderAtomicOpCase
714 {
715 public:
ShaderAtomicXorCase(Context & context,const char * name,AtomicOperandType operandType,DataType type,Precision precision)716 ShaderAtomicXorCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
717 : ShaderAtomicOpCase(context, name, "atomicXor", operandType, type, precision, UVec3(3,2,1))
718 {
719 m_initialValue = 0;
720 }
721
722 protected:
getInputs(int numValues,int stride,void * inputs) const723 void getInputs (int numValues, int stride, void* inputs) const
724 {
725 de::Random rnd (deStringHash(getName()));
726 const int workGroupSize = (int)product(m_workGroupSize);
727 const int numWorkGroups = numValues/workGroupSize;
728
729 for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
730 {
731 const int groupOffset = groupNdx*workGroupSize;
732
733 // First uses random bit-pattern.
734 *(deUint32*)((deUint8*)inputs + stride*(groupOffset)) = rnd.getUint32();
735
736 // Rest have either all or no bits set.
737 for (int localNdx = 1; localNdx < workGroupSize; localNdx++)
738 *(deUint32*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = rnd.getBool() ? ~0u : 0u;
739 }
740 }
741
verify(int numValues,int inputStride,const void * inputs,int outputStride,const void * outputs,int groupStride,const void * groupOutputs) const742 bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
743 {
744 const int workGroupSize = (int)product(m_workGroupSize);
745 const int numWorkGroups = numValues/workGroupSize;
746 const int numBits = m_precision == PRECISION_HIGHP ? 32 :
747 m_precision == PRECISION_MEDIUMP ? 16 : 8;
748 const deUint32 compareMask = numBits == 32 ? ~0u : (1u<<numBits)-1u;
749
750 for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
751 {
752 const int groupOffset = groupNdx*workGroupSize;
753 const deUint32 groupOutput = *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
754 const deUint32 randomValue = *(const int*)((const deUint8*)inputs + inputStride*groupOffset);
755 const deUint32 expected0 = randomValue ^ 0u;
756 const deUint32 expected1 = randomValue ^ ~0u;
757 int numXorZeros = (m_initialValue == 0) ? 1 : 0;
758
759 for (int localNdx = 1; localNdx < workGroupSize; localNdx++)
760 {
761 const deUint32 inputValue = *(const deUint32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
762 if (inputValue == 0)
763 numXorZeros += 1;
764 }
765
766 const deUint32 expected = (numXorZeros%2 == 0) ? expected0 : expected1;
767
768 if ((groupOutput & compareMask) != (expected & compareMask))
769 {
770 m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected " << tcu::toHex(expected0)
771 << " or " << tcu::toHex(expected1) << " (compare mask " << tcu::toHex(compareMask)
772 << "), got " << tcu::toHex(groupOutput) << TestLog::EndMessage;
773 return false;
774 }
775
776 for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
777 {
778 const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
779
780 if ((outputValue&compareMask) != 0 &&
781 (outputValue&compareMask) != compareMask &&
782 (outputValue&compareMask) != (expected0&compareMask) &&
783 (outputValue&compareMask) != (expected1&compareMask))
784 {
785 m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
786 << ": found unexpected value " << tcu::toHex(outputValue)
787 << TestLog::EndMessage;
788 return false;
789 }
790 }
791 }
792
793 return true;
794 }
795 };
796
797 class ShaderAtomicExchangeCase : public ShaderAtomicOpCase
798 {
799 public:
ShaderAtomicExchangeCase(Context & context,const char * name,AtomicOperandType operandType,DataType type,Precision precision)800 ShaderAtomicExchangeCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
801 : ShaderAtomicOpCase(context, name, "atomicExchange", operandType, type, precision, UVec3(3,2,1))
802 {
803 m_initialValue = 0;
804 }
805
806 protected:
getInputs(int numValues,int stride,void * inputs) const807 void getInputs (int numValues, int stride, void* inputs) const
808 {
809 const int workGroupSize = (int)product(m_workGroupSize);
810 const int numWorkGroups = numValues/workGroupSize;
811
812 for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
813 {
814 const int groupOffset = groupNdx*workGroupSize;
815
816 for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
817 *(int*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = localNdx+1;
818 }
819 }
820
verify(int numValues,int inputStride,const void * inputs,int outputStride,const void * outputs,int groupStride,const void * groupOutputs) const821 bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
822 {
823 const int workGroupSize = (int)product(m_workGroupSize);
824 const int numWorkGroups = numValues/workGroupSize;
825
826 DE_UNREF(inputStride && inputs);
827
828 for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
829 {
830 const int groupOffset = groupNdx*workGroupSize;
831 const int groupOutput = *(const int*)((const deUint8*)groupOutputs + groupNdx*groupStride);
832 set<int> usedValues;
833
834 for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
835 {
836 const int outputValue = *(const int*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
837
838 if (!de::inRange(outputValue, 0, workGroupSize) || usedValues.find(outputValue) != usedValues.end())
839 {
840 m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
841 << ": found unexpected value " << outputValue
842 << TestLog::EndMessage;
843 return false;
844 }
845 usedValues.insert(outputValue);
846 }
847
848 if (!de::inRange(groupOutput, 0, workGroupSize) || usedValues.find(groupOutput) != usedValues.end())
849 {
850 m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": unexpected final value" << groupOutput << TestLog::EndMessage;
851 return false;
852 }
853 }
854
855 return true;
856 }
857 };
858
859 class ShaderAtomicCompSwapCase : public TestCase
860 {
861 public:
862 ShaderAtomicCompSwapCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision);
863 ~ShaderAtomicCompSwapCase (void);
864
865 void init (void);
866 void deinit (void);
867 IterateResult iterate (void);
868
869 protected:
870
871 private:
872 ShaderAtomicCompSwapCase (const ShaderAtomicCompSwapCase& other);
873 ShaderAtomicCompSwapCase& operator= (const ShaderAtomicCompSwapCase& other);
874
875 const AtomicOperandType m_operandType;
876 const DataType m_type;
877 const Precision m_precision;
878
879 const UVec3 m_workGroupSize;
880 const UVec3 m_numWorkGroups;
881
882 ShaderProgram* m_program;
883 };
884
ShaderAtomicCompSwapCase(Context & context,const char * name,AtomicOperandType operandType,DataType type,Precision precision)885 ShaderAtomicCompSwapCase::ShaderAtomicCompSwapCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
886 : TestCase (context, name, "atomicCompSwap() Test")
887 , m_operandType (operandType)
888 , m_type (type)
889 , m_precision (precision)
890 , m_workGroupSize (3,2,1)
891 , m_numWorkGroups (4,4,4)
892 , m_program (DE_NULL)
893 {
894 }
895
~ShaderAtomicCompSwapCase(void)896 ShaderAtomicCompSwapCase::~ShaderAtomicCompSwapCase (void)
897 {
898 ShaderAtomicCompSwapCase::deinit();
899 }
900
init(void)901 void ShaderAtomicCompSwapCase::init (void)
902 {
903 const bool isSSBO = m_operandType == ATOMIC_OPERAND_BUFFER_VARIABLE;
904 const char* precName = getPrecisionName(m_precision);
905 const char* typeName = getDataTypeName(m_type);
906 const deUint32 numValues = product(m_workGroupSize)*product(m_numWorkGroups);
907 std::ostringstream src;
908
909 src << "#version 310 es\n"
910 << "layout(local_size_x = " << m_workGroupSize.x()
911 << ", local_size_y = " << m_workGroupSize.y()
912 << ", local_size_z = " << m_workGroupSize.z() << ") in;\n"
913 << "layout(binding = 0) buffer InOut\n"
914 << "{\n"
915 << " " << precName << " " << typeName << " compareValues[" << numValues << "];\n"
916 << " " << precName << " " << typeName << " exchangeValues[" << numValues << "];\n"
917 << " " << precName << " " << typeName << " outputValues[" << numValues << "];\n"
918 << " " << (isSSBO ? "coherent " : "") << precName << " " << typeName << " groupValues[" << product(m_numWorkGroups) << "];\n"
919 << "} sb_inout;\n";
920
921 if (!isSSBO)
922 src << "shared " << precName << " " << typeName << " s_var;\n";
923
924 src << "\n"
925 << "void main (void)\n"
926 << "{\n"
927 << " uint localSize = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z;\n"
928 << " uint globalNdx = gl_NumWorkGroups.x*gl_NumWorkGroups.y*gl_WorkGroupID.z + gl_NumWorkGroups.x*gl_WorkGroupID.y + gl_WorkGroupID.x;\n"
929 << " uint globalOffs = localSize*globalNdx;\n"
930 << " uint offset = globalOffs + gl_LocalInvocationIndex;\n"
931 << "\n";
932
933 if (!isSSBO)
934 {
935 src << " if (gl_LocalInvocationIndex == 0u)\n"
936 << " s_var = " << typeName << "(" << 0 << ");\n"
937 << "\n";
938 }
939
940 src << " " << precName << " " << typeName << " compare = sb_inout.compareValues[offset];\n"
941 << " " << precName << " " << typeName << " exchange = sb_inout.exchangeValues[offset];\n"
942 << " " << precName << " " << typeName << " result;\n"
943 << " bool swapDone = false;\n"
944 << "\n"
945 << " for (uint ndx = 0u; ndx < localSize; ndx++)\n"
946 << " {\n"
947 << " barrier();\n"
948 << " if (!swapDone)\n"
949 << " {\n"
950 << " result = atomicCompSwap(" << (isSSBO ? "sb_inout.groupValues[globalNdx]" : "s_var") << ", compare, exchange);\n"
951 << " if (result == compare)\n"
952 << " swapDone = true;\n"
953 << " }\n"
954 << " }\n"
955 << "\n"
956 << " sb_inout.outputValues[offset] = result;\n";
957
958 if (!isSSBO)
959 {
960 src << " barrier();\n"
961 << " if (gl_LocalInvocationIndex == 0u)\n"
962 << " sb_inout.groupValues[globalNdx] = s_var;\n";
963 }
964
965 src << "}\n";
966
967 DE_ASSERT(!m_program);
968 m_program = new ShaderProgram(m_context.getRenderContext(), ProgramSources() << ComputeSource(src.str()));
969
970 m_testCtx.getLog() << *m_program;
971
972 if (!m_program->isOk())
973 {
974 delete m_program;
975 m_program = DE_NULL;
976 throw tcu::TestError("Compile failed");
977 }
978 }
979
deinit(void)980 void ShaderAtomicCompSwapCase::deinit (void)
981 {
982 delete m_program;
983 m_program = DE_NULL;
984 }
985
iterate(void)986 ShaderAtomicOpCase::IterateResult ShaderAtomicCompSwapCase::iterate (void)
987 {
988 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
989 const deUint32 program = m_program->getProgram();
990 const Buffer inoutBuffer (m_context.getRenderContext());
991 const deUint32 blockNdx = gl.getProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, "InOut");
992 const InterfaceBlockInfo blockInfo = getProgramInterfaceBlockInfo(gl, program, GL_SHADER_STORAGE_BLOCK, blockNdx);
993 const deUint32 cmpVarNdx = gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.compareValues[0]");
994 const InterfaceVariableInfo cmpVarInfo = getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, cmpVarNdx);
995 const deUint32 exhVarNdx = gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.exchangeValues[0]");
996 const InterfaceVariableInfo exhVarInfo = getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, exhVarNdx);
997 const deUint32 outVarNdx = gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.outputValues[0]");
998 const InterfaceVariableInfo outVarInfo = getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, outVarNdx);
999 const deUint32 groupVarNdx = gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.groupValues[0]");
1000 const InterfaceVariableInfo groupVarInfo = getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, groupVarNdx);
1001 const deUint32 numValues = product(m_workGroupSize)*product(m_numWorkGroups);
1002
1003 TCU_CHECK(cmpVarInfo.arraySize == numValues &&
1004 exhVarInfo.arraySize == numValues &&
1005 outVarInfo.arraySize == numValues &&
1006 groupVarInfo.arraySize == product(m_numWorkGroups));
1007
1008 gl.useProgram(program);
1009
1010 // \todo [2013-09-05 pyry] Use randomized input values!
1011
1012 // Setup buffer.
1013 {
1014 const deUint32 workGroupSize = product(m_workGroupSize);
1015 vector<deUint8> bufData (blockInfo.dataSize);
1016
1017 std::fill(bufData.begin(), bufData.end(), 0);
1018
1019 for (deUint32 ndx = 0; ndx < numValues; ndx++)
1020 *(deUint32*)(&bufData[0] + cmpVarInfo.offset + cmpVarInfo.arrayStride*ndx) = ndx%workGroupSize;
1021
1022 for (deUint32 ndx = 0; ndx < numValues; ndx++)
1023 *(deUint32*)(&bufData[0] + exhVarInfo.offset + exhVarInfo.arrayStride*ndx) = (ndx%workGroupSize)+1;
1024
1025 gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *inoutBuffer);
1026 gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockInfo.dataSize, &bufData[0], GL_STATIC_READ);
1027 gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *inoutBuffer);
1028 GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
1029 }
1030
1031 gl.dispatchCompute(m_numWorkGroups.x(), m_numWorkGroups.y(), m_numWorkGroups.z());
1032
1033 // Read back and compare
1034 {
1035 const void* resPtr = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, blockInfo.dataSize, GL_MAP_READ_BIT);
1036 const int numWorkGroups = (int)product(m_numWorkGroups);
1037 const int workGroupSize = (int)product(m_workGroupSize);
1038 bool isOk = true;
1039
1040 GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
1041 TCU_CHECK(resPtr);
1042
1043 for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
1044 {
1045 const int groupOffset = groupNdx*workGroupSize;
1046 const int groupOutput = *(const int*)((const deUint8*)resPtr + groupVarInfo.offset + groupNdx*groupVarInfo.arrayStride);
1047
1048 for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
1049 {
1050 const int refValue = localNdx;
1051 const int outputValue = *(const int*)((const deUint8*)resPtr + outVarInfo.offset + outVarInfo.arrayStride*(groupOffset+localNdx));
1052
1053 if (outputValue != refValue)
1054 {
1055 m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
1056 << ": expected " << refValue << ", got " << outputValue
1057 << TestLog::EndMessage;
1058 isOk = false;
1059 break;
1060 }
1061 }
1062
1063 if (groupOutput != workGroupSize)
1064 {
1065 m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected" << workGroupSize << ", got " << groupOutput << TestLog::EndMessage;
1066 isOk = false;
1067 break;
1068 }
1069 }
1070
1071 gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
1072 GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer()");
1073
1074 m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
1075 isOk ? "Pass" : "Comparison failed");
1076 }
1077
1078 return STOP;
1079 }
1080
ShaderAtomicOpTests(Context & context,const char * name,AtomicOperandType operandType)1081 ShaderAtomicOpTests::ShaderAtomicOpTests (Context& context, const char* name, AtomicOperandType operandType)
1082 : TestCaseGroup (context, name, "Atomic Operation Tests")
1083 , m_operandType (operandType)
1084 {
1085 }
1086
~ShaderAtomicOpTests(void)1087 ShaderAtomicOpTests::~ShaderAtomicOpTests (void)
1088 {
1089 }
1090
1091 template<typename T>
createAtomicOpGroup(Context & context,AtomicOperandType operandType,const char * groupName)1092 static tcu::TestCaseGroup* createAtomicOpGroup (Context& context, AtomicOperandType operandType, const char* groupName)
1093 {
1094 tcu::TestCaseGroup *const group = new tcu::TestCaseGroup(context.getTestContext(), groupName, (string("Atomic ") + groupName).c_str());
1095 try
1096 {
1097 for (int precNdx = 0; precNdx < PRECISION_LAST; precNdx++)
1098 {
1099 for (int typeNdx = 0; typeNdx < 2; typeNdx++)
1100 {
1101 const Precision precision = Precision(precNdx);
1102 const DataType type = typeNdx > 0 ? TYPE_INT : TYPE_UINT;
1103 const string caseName = string(getPrecisionName(precision)) + "_" + getDataTypeName(type);
1104
1105 group->addChild(new T(context, caseName.c_str(), operandType, type, precision));
1106 }
1107 }
1108
1109 return group;
1110 }
1111 catch (...)
1112 {
1113 delete group;
1114 throw;
1115 }
1116 }
1117
init(void)1118 void ShaderAtomicOpTests::init (void)
1119 {
1120 addChild(createAtomicOpGroup<ShaderAtomicAddCase> (m_context, m_operandType, "add"));
1121 addChild(createAtomicOpGroup<ShaderAtomicMinCase> (m_context, m_operandType, "min"));
1122 addChild(createAtomicOpGroup<ShaderAtomicMaxCase> (m_context, m_operandType, "max"));
1123 addChild(createAtomicOpGroup<ShaderAtomicAndCase> (m_context, m_operandType, "and"));
1124 addChild(createAtomicOpGroup<ShaderAtomicOrCase> (m_context, m_operandType, "or"));
1125 addChild(createAtomicOpGroup<ShaderAtomicXorCase> (m_context, m_operandType, "xor"));
1126 addChild(createAtomicOpGroup<ShaderAtomicExchangeCase> (m_context, m_operandType, "exchange"));
1127 addChild(createAtomicOpGroup<ShaderAtomicCompSwapCase> (m_context, m_operandType, "compswap"));
1128 }
1129
1130 } // Functional
1131 } // gles31
1132 } // deqp
1133