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