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