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> 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 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 109 ShaderAtomicOpCase::~ShaderAtomicOpCase (void) 110 { 111 ShaderAtomicOpCase::deinit(); 112 } 113 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 184 void ShaderAtomicOpCase::deinit (void) 185 { 186 delete m_program; 187 m_program = DE_NULL; 188 } 189 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: 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: 265 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 277 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 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 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 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 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 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: 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: 419 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 430 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: 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: 501 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 512 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: 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: 585 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 604 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: 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: 655 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 672 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: 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: 723 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 742 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: 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: 807 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 821 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 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 896 ShaderAtomicCompSwapCase::~ShaderAtomicCompSwapCase (void) 897 { 898 ShaderAtomicCompSwapCase::deinit(); 899 } 900 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 980 void ShaderAtomicCompSwapCase::deinit (void) 981 { 982 delete m_program; 983 m_program = DE_NULL; 984 } 985 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 1081 ShaderAtomicOpTests::ShaderAtomicOpTests (Context& context, const char* name, AtomicOperandType operandType) 1082 : TestCaseGroup (context, name, "Atomic Operation Tests") 1083 , m_operandType (operandType) 1084 { 1085 } 1086 1087 ShaderAtomicOpTests::~ShaderAtomicOpTests (void) 1088 { 1089 } 1090 1091 template<typename T> 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 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