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 Compute Shader Built-in variable tests. 22 *//*--------------------------------------------------------------------*/ 23 24#include "es31fComputeShaderBuiltinVarTests.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 "deSharedPtr.hpp" 34#include "deStringUtil.hpp" 35#include "glwFunctions.hpp" 36#include "glwEnums.hpp" 37 38#include <map> 39 40namespace deqp 41{ 42namespace gles31 43{ 44namespace Functional 45{ 46 47using std::string; 48using std::vector; 49using std::map; 50using tcu::TestLog; 51using tcu::UVec3; 52using tcu::IVec3; 53 54using namespace glu; 55 56template<typename T, int Size> 57struct LexicalCompareVec 58{ 59 inline bool operator() (const tcu::Vector<T, Size>& a, const tcu::Vector<T, Size>& b) const 60 { 61 for (int ndx = 0; ndx < Size; ndx++) 62 { 63 if (a[ndx] < b[ndx]) 64 return true; 65 else if (a[ndx] > b[ndx]) 66 return false; 67 } 68 return false; 69 } 70}; 71 72typedef de::SharedPtr<glu::ShaderProgram> ShaderProgramSp; 73typedef std::map<tcu::UVec3, ShaderProgramSp, LexicalCompareVec<deUint32, 3> > LocalSizeProgramMap; 74 75class ComputeBuiltinVarCase : public TestCase 76{ 77public: 78 ComputeBuiltinVarCase (Context& context, const char* name, const char* varName, DataType varType); 79 ~ComputeBuiltinVarCase (void); 80 81 void init (void); 82 void deinit (void); 83 IterateResult iterate (void); 84 85 virtual UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const = 0; 86 87protected: 88 struct SubCase 89 { 90 UVec3 localSize; 91 UVec3 numWorkGroups; 92 93 SubCase (void) {} 94 SubCase (const UVec3& localSize_, const UVec3& numWorkGroups_) : localSize(localSize_), numWorkGroups(numWorkGroups_) {} 95 }; 96 97 vector<SubCase> m_subCases; 98 99private: 100 ComputeBuiltinVarCase (const ComputeBuiltinVarCase& other); 101 ComputeBuiltinVarCase& operator= (const ComputeBuiltinVarCase& other); 102 103 deUint32 getProgram (const UVec3& localSize); 104 105 const string m_varName; 106 const DataType m_varType; 107 108 LocalSizeProgramMap m_progMap; 109 int m_subCaseNdx; 110}; 111 112ComputeBuiltinVarCase::ComputeBuiltinVarCase (Context& context, const char* name, const char* varName, DataType varType) 113 : TestCase (context, name, varName) 114 , m_varName (varName) 115 , m_varType (varType) 116 , m_subCaseNdx (0) 117{ 118} 119 120ComputeBuiltinVarCase::~ComputeBuiltinVarCase (void) 121{ 122 ComputeBuiltinVarCase::deinit(); 123} 124 125void ComputeBuiltinVarCase::init (void) 126{ 127 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 128 m_subCaseNdx = 0; 129} 130 131void ComputeBuiltinVarCase::deinit (void) 132{ 133 m_progMap.clear(); 134} 135 136static string genBuiltinVarSource (const string& varName, DataType varType, const UVec3& localSize) 137{ 138 std::ostringstream src; 139 140 src << "#version 310 es\n" 141 << "layout (local_size_x = " << localSize.x() << ", local_size_y = " << localSize.y() << ", local_size_z = " << localSize.z() << ") in;\n" 142 << "uniform highp uvec2 u_stride;\n" 143 << "layout(binding = 0) buffer Output\n" 144 << "{\n" 145 << " " << glu::getDataTypeName(varType) << " result[];\n" 146 << "} sb_out;\n" 147 << "\n" 148 << "void main (void)\n" 149 << "{\n" 150 << " highp uint offset = u_stride.x*gl_GlobalInvocationID.z + u_stride.y*gl_GlobalInvocationID.y + gl_GlobalInvocationID.x;\n" 151 << " sb_out.result[offset] = " << varName << ";\n" 152 << "}\n"; 153 154 return src.str(); 155} 156 157deUint32 ComputeBuiltinVarCase::getProgram (const UVec3& localSize) 158{ 159 LocalSizeProgramMap::const_iterator cachePos = m_progMap.find(localSize); 160 if (cachePos != m_progMap.end()) 161 return cachePos->second->getProgram(); 162 else 163 { 164 ShaderProgramSp program(new ShaderProgram(m_context.getRenderContext(), 165 ProgramSources() << ComputeSource(genBuiltinVarSource(m_varName, m_varType, localSize)))); 166 167 // Log all compiled programs. 168 m_testCtx.getLog() << *program; 169 if (!program->isOk()) 170 throw tcu::TestError("Compile failed"); 171 172 m_progMap[localSize] = program; 173 return program->getProgram(); 174 } 175} 176 177static inline UVec3 readResultVec (const deUint32* ptr, int numComps) 178{ 179 UVec3 res; 180 for (int ndx = 0; ndx < numComps; ndx++) 181 res[ndx] = ptr[ndx]; 182 return res; 183} 184 185static inline bool compareComps (const UVec3& a, const UVec3& b, int numComps) 186{ 187 DE_ASSERT(numComps == 1 || numComps == 3); 188 return numComps == 3 ? tcu::allEqual(a, b) : a.x() == b.x(); 189} 190 191struct LogComps 192{ 193 const UVec3& v; 194 int numComps; 195 196 LogComps (const UVec3& v_, int numComps_) : v(v_), numComps(numComps_) {} 197}; 198 199static inline std::ostream& operator<< (std::ostream& str, const LogComps& c) 200{ 201 DE_ASSERT(c.numComps == 1 || c.numComps == 3); 202 return c.numComps == 3 ? str << c.v : str << c.v.x(); 203} 204 205ComputeBuiltinVarCase::IterateResult ComputeBuiltinVarCase::iterate (void) 206{ 207 const tcu::ScopedLogSection section (m_testCtx.getLog(), string("Iteration") + de::toString(m_subCaseNdx), string("Iteration ") + de::toString(m_subCaseNdx)); 208 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 209 const SubCase& subCase = m_subCases[m_subCaseNdx]; 210 const deUint32 program = getProgram(subCase.localSize); 211 212 const tcu::UVec3 globalSize = subCase.localSize*subCase.numWorkGroups; 213 const tcu::UVec2 stride (globalSize[0]*globalSize[1], globalSize[0]); 214 const deUint32 numInvocations = subCase.localSize[0]*subCase.localSize[1]*subCase.localSize[2]*subCase.numWorkGroups[0]*subCase.numWorkGroups[1]*subCase.numWorkGroups[2]; 215 216 const deUint32 outVarIndex = gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "Output.result"); 217 const InterfaceVariableInfo outVarInfo = getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, outVarIndex); 218 const deUint32 bufferSize = numInvocations*outVarInfo.arrayStride; 219 Buffer outputBuffer (m_context.getRenderContext()); 220 221 TCU_CHECK(outVarInfo.arraySize == 0); // Unsized variable. 222 223 m_testCtx.getLog() << TestLog::Message << "Number of work groups = " << subCase.numWorkGroups << TestLog::EndMessage 224 << TestLog::Message << "Work group size = " << subCase.localSize << TestLog::EndMessage; 225 226 gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer); 227 gl.bufferData(GL_SHADER_STORAGE_BUFFER, (glw::GLsizeiptr)bufferSize, DE_NULL, GL_STREAM_READ); 228 gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *outputBuffer); 229 GLU_EXPECT_NO_ERROR(gl.getError(), "Buffer setup failed"); 230 231 gl.useProgram(program); 232 gl.uniform2uiv(gl.getUniformLocation(program, "u_stride"), 1, stride.getPtr()); 233 GLU_EXPECT_NO_ERROR(gl.getError(), "Program setup failed"); 234 235 gl.dispatchCompute(subCase.numWorkGroups[0], subCase.numWorkGroups[1], subCase.numWorkGroups[2]); 236 GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute() failed"); 237 238 { 239 const void* ptr = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, bufferSize, GL_MAP_READ_BIT); 240 int numFailed = 0; 241 const int numScalars = getDataTypeScalarSize(m_varType); 242 const int maxLogPrints = 10; 243 244 GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() failed"); 245 TCU_CHECK(ptr); 246 247 for (deUint32 groupZ = 0; groupZ < subCase.numWorkGroups.z(); groupZ++) 248 for (deUint32 groupY = 0; groupY < subCase.numWorkGroups.y(); groupY++) 249 for (deUint32 groupX = 0; groupX < subCase.numWorkGroups.x(); groupX++) 250 for (deUint32 localZ = 0; localZ < subCase.localSize.z(); localZ++) 251 for (deUint32 localY = 0; localY < subCase.localSize.y(); localY++) 252 for (deUint32 localX = 0; localX < subCase.localSize.x(); localX++) 253 { 254 const UVec3 refGroupID (groupX, groupY, groupZ); 255 const UVec3 refLocalID (localX, localY, localZ); 256 const UVec3 refGlobalID = refGroupID * subCase.localSize + refLocalID; 257 const deUint32 refOffset = stride.x()*refGlobalID.z() + stride.y()*refGlobalID.y() + refGlobalID.x(); 258 const UVec3 refValue = computeReference(subCase.numWorkGroups, subCase.localSize, refGroupID, refLocalID); 259 260 const deUint32* resPtr = (const deUint32*)((const deUint8*)ptr + refOffset*outVarInfo.arrayStride); 261 const UVec3 resValue = readResultVec(resPtr, numScalars); 262 263 if (!compareComps(refValue, resValue, numScalars)) 264 { 265 if (numFailed < maxLogPrints) 266 m_testCtx.getLog() << TestLog::Message << "ERROR: comparison failed at offset " << refOffset 267 << ": expected " << LogComps(refValue, numScalars) 268 << ", got " << LogComps(resValue, numScalars) 269 << TestLog::EndMessage; 270 else if (numFailed == maxLogPrints) 271 m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage; 272 273 numFailed += 1; 274 } 275 } 276 277 m_testCtx.getLog() << TestLog::Message << (numInvocations-numFailed) << " / " << numInvocations << " values passed" << TestLog::EndMessage; 278 279 if (numFailed > 0) 280 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Comparison failed"); 281 282 gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER); 283 } 284 285 m_subCaseNdx += 1; 286 return (m_subCaseNdx < (int)m_subCases.size() && m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) ? CONTINUE : STOP; 287} 288 289// Test cases 290 291class NumWorkGroupsCase : public ComputeBuiltinVarCase 292{ 293public: 294 NumWorkGroupsCase (Context& context) 295 : ComputeBuiltinVarCase(context, "num_work_groups", "gl_NumWorkGroups", TYPE_UINT_VEC3) 296 { 297 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1))); 298 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(52,1,1))); 299 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,39,1))); 300 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,78))); 301 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(4,7,11))); 302 m_subCases.push_back(SubCase(UVec3(2,3,4), UVec3(4,7,11))); 303 } 304 305 UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const 306 { 307 DE_UNREF(numWorkGroups); 308 DE_UNREF(workGroupSize); 309 DE_UNREF(workGroupID); 310 DE_UNREF(localInvocationID); 311 return numWorkGroups; 312 } 313}; 314 315class WorkGroupSizeCase : public ComputeBuiltinVarCase 316{ 317public: 318 WorkGroupSizeCase (Context& context) 319 : ComputeBuiltinVarCase(context, "work_group_size", "gl_WorkGroupSize", TYPE_UINT_VEC3) 320 { 321 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1))); 322 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(2,7,3))); 323 m_subCases.push_back(SubCase(UVec3(2,1,1), UVec3(1,1,1))); 324 m_subCases.push_back(SubCase(UVec3(2,1,1), UVec3(1,3,5))); 325 m_subCases.push_back(SubCase(UVec3(1,3,1), UVec3(1,1,1))); 326 m_subCases.push_back(SubCase(UVec3(1,1,7), UVec3(1,1,1))); 327 m_subCases.push_back(SubCase(UVec3(1,1,7), UVec3(3,3,1))); 328 m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(1,1,1))); 329 m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(3,1,2))); 330 } 331 332 UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const 333 { 334 DE_UNREF(numWorkGroups); 335 DE_UNREF(workGroupID); 336 DE_UNREF(localInvocationID); 337 return workGroupSize; 338 } 339}; 340 341class WorkGroupIDCase : public ComputeBuiltinVarCase 342{ 343public: 344 WorkGroupIDCase (Context& context) 345 : ComputeBuiltinVarCase(context, "work_group_id", "gl_WorkGroupID", TYPE_UINT_VEC3) 346 { 347 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1))); 348 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(52,1,1))); 349 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,39,1))); 350 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,78))); 351 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(4,7,11))); 352 m_subCases.push_back(SubCase(UVec3(2,3,4), UVec3(4,7,11))); 353 } 354 355 UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const 356 { 357 DE_UNREF(numWorkGroups); 358 DE_UNREF(workGroupSize); 359 DE_UNREF(localInvocationID); 360 return workGroupID; 361 } 362}; 363 364class LocalInvocationIDCase : public ComputeBuiltinVarCase 365{ 366public: 367 LocalInvocationIDCase (Context& context) 368 : ComputeBuiltinVarCase(context, "local_invocation_id", "gl_LocalInvocationID", TYPE_UINT_VEC3) 369 { 370 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1))); 371 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(2,7,3))); 372 m_subCases.push_back(SubCase(UVec3(2,1,1), UVec3(1,1,1))); 373 m_subCases.push_back(SubCase(UVec3(2,1,1), UVec3(1,3,5))); 374 m_subCases.push_back(SubCase(UVec3(1,3,1), UVec3(1,1,1))); 375 m_subCases.push_back(SubCase(UVec3(1,1,7), UVec3(1,1,1))); 376 m_subCases.push_back(SubCase(UVec3(1,1,7), UVec3(3,3,1))); 377 m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(1,1,1))); 378 m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(3,1,2))); 379 } 380 381 UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const 382 { 383 DE_UNREF(numWorkGroups); 384 DE_UNREF(workGroupSize); 385 DE_UNREF(workGroupID); 386 return localInvocationID; 387 } 388}; 389 390class GlobalInvocationIDCase : public ComputeBuiltinVarCase 391{ 392public: 393 GlobalInvocationIDCase (Context& context) 394 : ComputeBuiltinVarCase(context, "global_invocation_id", "gl_GlobalInvocationID", TYPE_UINT_VEC3) 395 { 396 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1))); 397 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(52,1,1))); 398 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,39,1))); 399 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,78))); 400 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(4,7,11))); 401 m_subCases.push_back(SubCase(UVec3(2,3,4), UVec3(4,7,11))); 402 m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(1,1,1))); 403 m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(3,1,2))); 404 } 405 406 UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const 407 { 408 DE_UNREF(numWorkGroups); 409 return workGroupID * workGroupSize + localInvocationID; 410 } 411}; 412 413class LocalInvocationIndexCase : public ComputeBuiltinVarCase 414{ 415public: 416 LocalInvocationIndexCase (Context& context) 417 : ComputeBuiltinVarCase(context, "local_invocation_index", "gl_LocalInvocationIndex", TYPE_UINT) 418 { 419 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1))); 420 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,39,1))); 421 m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(4,7,11))); 422 m_subCases.push_back(SubCase(UVec3(2,3,4), UVec3(4,7,11))); 423 m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(1,1,1))); 424 m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(3,1,2))); 425 } 426 427 UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const 428 { 429 DE_UNREF(workGroupID); 430 DE_UNREF(numWorkGroups); 431 return UVec3(localInvocationID.z()*workGroupSize.x()*workGroupSize.y() + localInvocationID.y()*workGroupSize.x() + localInvocationID.x(), 0, 0); 432 } 433}; 434 435ComputeShaderBuiltinVarTests::ComputeShaderBuiltinVarTests (Context& context) 436 : TestCaseGroup(context, "compute", "Compute Shader Builtin Variables") 437{ 438} 439 440ComputeShaderBuiltinVarTests::~ComputeShaderBuiltinVarTests (void) 441{ 442} 443 444void ComputeShaderBuiltinVarTests::init (void) 445{ 446 addChild(new NumWorkGroupsCase (m_context)); 447 addChild(new WorkGroupSizeCase (m_context)); 448 addChild(new WorkGroupIDCase (m_context)); 449 addChild(new LocalInvocationIDCase (m_context)); 450 addChild(new GlobalInvocationIDCase (m_context)); 451 addChild(new LocalInvocationIndexCase (m_context)); 452} 453 454} // Functional 455} // gles31 456} // deqp 457