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::string;
48 using std::vector;
49 using std::map;
50 using tcu::TestLog;
51 using tcu::UVec3;
52 using tcu::IVec3;
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<deUint32, 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, const UVec3& localInvocationID) const = 0;
86
87 protected:
88 struct SubCase
89 {
90 UVec3 localSize;
91 UVec3 numWorkGroups;
92
SubCasedeqp::gles31::Functional::ComputeBuiltinVarCase::SubCase93 SubCase (void) {}
SubCasedeqp::gles31::Functional::ComputeBuiltinVarCase::SubCase94 SubCase (const UVec3& localSize_, const UVec3& numWorkGroups_) : localSize(localSize_), numWorkGroups(numWorkGroups_) {}
95 };
96
97 vector<SubCase> m_subCases;
98
99 private:
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
ComputeBuiltinVarCase(Context & context,const char * name,const char * varName,DataType varType)112 ComputeBuiltinVarCase::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
~ComputeBuiltinVarCase(void)120 ComputeBuiltinVarCase::~ComputeBuiltinVarCase (void)
121 {
122 ComputeBuiltinVarCase::deinit();
123 }
124
init(void)125 void ComputeBuiltinVarCase::init (void)
126 {
127 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
128 m_subCaseNdx = 0;
129 }
130
deinit(void)131 void ComputeBuiltinVarCase::deinit (void)
132 {
133 m_progMap.clear();
134 }
135
genBuiltinVarSource(const string & varName,DataType varType,const UVec3 & localSize)136 static 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
getProgram(const UVec3 & localSize)157 deUint32 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
readResultVec(const deUint32 * ptr,int numComps)177 static 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
compareComps(const UVec3 & a,const UVec3 & b,int numComps)185 static 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
191 struct LogComps
192 {
193 const UVec3& v;
194 int numComps;
195
LogCompsdeqp::gles31::Functional::LogComps196 LogComps (const UVec3& v_, int numComps_) : v(v_), numComps(numComps_) {}
197 };
198
operator <<(std::ostream & str,const LogComps & c)199 static 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
iterate(void)205 ComputeBuiltinVarCase::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
291 class NumWorkGroupsCase : public ComputeBuiltinVarCase
292 {
293 public:
NumWorkGroupsCase(Context & context)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
computeReference(const UVec3 & numWorkGroups,const UVec3 & workGroupSize,const UVec3 & workGroupID,const UVec3 & localInvocationID) const305 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
315 class WorkGroupSizeCase : public ComputeBuiltinVarCase
316 {
317 public:
WorkGroupSizeCase(Context & context)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
computeReference(const UVec3 & numWorkGroups,const UVec3 & workGroupSize,const UVec3 & workGroupID,const UVec3 & localInvocationID) const332 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
341 class WorkGroupIDCase : public ComputeBuiltinVarCase
342 {
343 public:
WorkGroupIDCase(Context & context)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
computeReference(const UVec3 & numWorkGroups,const UVec3 & workGroupSize,const UVec3 & workGroupID,const UVec3 & localInvocationID) const355 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
364 class LocalInvocationIDCase : public ComputeBuiltinVarCase
365 {
366 public:
LocalInvocationIDCase(Context & context)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
computeReference(const UVec3 & numWorkGroups,const UVec3 & workGroupSize,const UVec3 & workGroupID,const UVec3 & localInvocationID) const381 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
390 class GlobalInvocationIDCase : public ComputeBuiltinVarCase
391 {
392 public:
GlobalInvocationIDCase(Context & context)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
computeReference(const UVec3 & numWorkGroups,const UVec3 & workGroupSize,const UVec3 & workGroupID,const UVec3 & localInvocationID) const406 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
413 class LocalInvocationIndexCase : public ComputeBuiltinVarCase
414 {
415 public:
LocalInvocationIndexCase(Context & context)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
computeReference(const UVec3 & numWorkGroups,const UVec3 & workGroupSize,const UVec3 & workGroupID,const UVec3 & localInvocationID) const427 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
ComputeShaderBuiltinVarTests(Context & context)435 ComputeShaderBuiltinVarTests::ComputeShaderBuiltinVarTests (Context& context)
436 : TestCaseGroup(context, "compute", "Compute Shader Builtin Variables")
437 {
438 }
439
~ComputeShaderBuiltinVarTests(void)440 ComputeShaderBuiltinVarTests::~ComputeShaderBuiltinVarTests (void)
441 {
442 }
443
init(void)444 void 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