1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.1 Module
3 * -------------------------------------------------
5 * Copyright 2014 The Android Open Source Project
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief Indirect compute dispatch tests.
22 *//*--------------------------------------------------------------------*/
24 #include "es31fIndirectComputeDispatchTests.hpp"
25 #include "gluObjectWrapper.hpp"
26 #include "gluRenderContext.hpp"
27 #include "gluShaderProgram.hpp"
28 #include "glwFunctions.hpp"
29 #include "glwEnums.hpp"
30 #include "tcuVector.hpp"
31 #include "tcuStringTemplate.hpp"
32 #include "tcuTestLog.hpp"
33 #include "deStringUtil.hpp"
52 // \todo [2014-02-17 pyry] Should be extended with following:
55 // - no active shader program
56 // - indirect negative or not aligned
57 // - indirect + size outside buffer bounds
58 // - no buffer bound to DRAW_INDIRECT_BUFFER
59 // - (implict) buffer mapped
62 // - lot of small work group launches
63 // - very large work group size
64 // - no synchronization, touched by gpu
65 // - compute program overwiting buffer
72 RESULT_BLOCK_BASE_SIZE = (3+1)*(int)sizeof(deUint32), // uvec3 + uint
73 RESULT_BLOCK_EXPECTED_COUNT_OFFSET = 0,
74 RESULT_BLOCK_NUM_PASSED_OFFSET = 3*(int)sizeof(deUint32),
76 INDIRECT_COMMAND_SIZE = 3*(int)sizeof(deUint32)
81 GEN_BUFFER_UPLOAD = 0,
87 glu::ProgramSources genVerifySources (const UVec3& workGroupSize)
89 static const char* s_verifyDispatchTmpl =
91 "layout(local_size_x = ${LOCAL_SIZE_X}, local_size_y = ${LOCAL_SIZE_Y}, local_size_z = ${LOCAL_SIZE_Z}) in;\n"
92 "layout(binding = 0, std430) buffer Result\n"
94 " uvec3 expectedGroupCount;\n"
95 " coherent uint numPassed;\n"
99 " if (all(equal(result.expectedGroupCount, gl_NumWorkGroups)))\n"
100 " atomicAdd(result.numPassed, 1u);\n"
103 map<string, string> args;
105 args["LOCAL_SIZE_X"] = de::toString(workGroupSize.x());
106 args["LOCAL_SIZE_Y"] = de::toString(workGroupSize.y());
107 args["LOCAL_SIZE_Z"] = de::toString(workGroupSize.z());
109 return glu::ProgramSources() << glu::ComputeSource(tcu::StringTemplate(s_verifyDispatchTmpl).specialize(args));
112 class IndirectDispatchCase : public TestCase
115 IndirectDispatchCase (Context& context, const char* name, const char* description, GenBuffer genBuffer);
116 ~IndirectDispatchCase (void);
118 IterateResult iterate (void);
121 struct DispatchCommand
126 DispatchCommand (void) : offset(0) {}
127 DispatchCommand (deIntptr offset_, const UVec3& numWorkGroups_) : offset(offset_), numWorkGroups(numWorkGroups_) {}
130 GenBuffer m_genBuffer;
131 deUintptr m_bufferSize;
132 UVec3 m_workGroupSize;
133 vector<DispatchCommand> m_commands;
135 void createCommandBuffer (deUint32 buffer) const;
136 void createResultBuffer (deUint32 buffer) const;
138 bool verifyResultBuffer (deUint32 buffer);
140 void createCmdBufferUpload (deUint32 buffer) const;
141 void createCmdBufferCompute (deUint32 buffer) const;
144 IndirectDispatchCase (const IndirectDispatchCase&);
145 IndirectDispatchCase& operator= (const IndirectDispatchCase&);
148 IndirectDispatchCase::IndirectDispatchCase (Context& context, const char* name, const char* description, GenBuffer genBuffer)
149 : TestCase (context, name, description)
150 , m_genBuffer (genBuffer)
155 IndirectDispatchCase::~IndirectDispatchCase (void)
159 static int getResultBlockAlignedSize (const glw::Functions& gl)
161 const int baseSize = RESULT_BLOCK_BASE_SIZE;
163 gl.getIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &alignment);
165 if (alignment == 0 || (baseSize % alignment == 0))
168 return (baseSize/alignment + 1)*alignment;
171 void IndirectDispatchCase::createCommandBuffer (deUint32 buffer) const
175 case GEN_BUFFER_UPLOAD: createCmdBufferUpload (buffer); break;
176 case GEN_BUFFER_COMPUTE: createCmdBufferCompute (buffer); break;
182 void IndirectDispatchCase::createCmdBufferUpload (deUint32 buffer) const
184 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
185 vector<deUint8> data (m_bufferSize);
187 for (vector<DispatchCommand>::const_iterator cmdIter = m_commands.begin(); cmdIter != m_commands.end(); ++cmdIter)
189 DE_STATIC_ASSERT(INDIRECT_COMMAND_SIZE >= sizeof(deUint32)*3);
190 DE_ASSERT(cmdIter->offset >= 0);
191 DE_ASSERT(cmdIter->offset%sizeof(deUint32) == 0);
192 DE_ASSERT(cmdIter->offset + INDIRECT_COMMAND_SIZE <= (deIntptr)m_bufferSize);
194 deUint32* const dstPtr = (deUint32*)&data[cmdIter->offset];
196 dstPtr[0] = cmdIter->numWorkGroups[0];
197 dstPtr[1] = cmdIter->numWorkGroups[1];
198 dstPtr[2] = cmdIter->numWorkGroups[2];
201 gl.bindBuffer(GL_DISPATCH_INDIRECT_BUFFER, buffer);
202 gl.bufferData(GL_DISPATCH_INDIRECT_BUFFER, (glw::GLsizeiptr)data.size(), &data[0], GL_STATIC_DRAW);
205 void IndirectDispatchCase::createCmdBufferCompute (deUint32 buffer) const
207 std::ostringstream src;
212 "layout(local_size_x = 1) in;\n"
213 "layout(std430, binding = 1) buffer Out\n"
215 " highp uint data[];\n"
217 "void writeCmd (uint offset, uvec3 numWorkGroups)\n"
219 " data[offset+0u] = numWorkGroups.x;\n"
220 " data[offset+1u] = numWorkGroups.y;\n"
221 " data[offset+2u] = numWorkGroups.z;\n"
227 for (vector<DispatchCommand>::const_iterator cmdIter = m_commands.begin(); cmdIter != m_commands.end(); ++cmdIter)
229 const deUint32 offs = (deUint32)(cmdIter->offset/4);
230 DE_ASSERT((deIntptr)offs*4 == cmdIter->offset);
232 src << "\twriteCmd(" << offs << "u, uvec3("
233 << cmdIter->numWorkGroups.x() << "u, "
234 << cmdIter->numWorkGroups.y() << "u, "
235 << cmdIter->numWorkGroups.z() << "u));\n";
241 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
242 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(src.str()));
244 m_testCtx.getLog() << program;
246 TCU_FAIL("Compile failed");
248 gl.useProgram(program.getProgram());
250 gl.bindBuffer(GL_DISPATCH_INDIRECT_BUFFER, buffer);
251 gl.bufferData(GL_DISPATCH_INDIRECT_BUFFER, (glw::GLsizeiptr)m_bufferSize, DE_NULL, GL_STATIC_DRAW);
252 gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, buffer);
253 GLU_EXPECT_NO_ERROR(gl.getError(), "Buffer setup failed");
255 gl.dispatchCompute(1,1,1);
256 GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute() failed");
258 gl.memoryBarrier(GL_COMMAND_BARRIER_BIT);
259 GLU_EXPECT_NO_ERROR(gl.getError(), "glMemoryBarrier(GL_COMMAND_BARRIER_BIT) failed");
263 void IndirectDispatchCase::createResultBuffer (deUint32 buffer) const
265 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
266 const int resultBlockSize = getResultBlockAlignedSize(gl);
267 const int resultBufferSize = resultBlockSize*(int)m_commands.size();
268 vector<deUint8> data (resultBufferSize);
270 for (size_t cmdNdx = 0; cmdNdx < m_commands.size(); cmdNdx++)
272 deUint8* const dstPtr = &data[resultBlockSize*cmdNdx];
274 *(deUint32*)(dstPtr + RESULT_BLOCK_EXPECTED_COUNT_OFFSET + 0*4) = m_commands[cmdNdx].numWorkGroups[0];
275 *(deUint32*)(dstPtr + RESULT_BLOCK_EXPECTED_COUNT_OFFSET + 1*4) = m_commands[cmdNdx].numWorkGroups[1];
276 *(deUint32*)(dstPtr + RESULT_BLOCK_EXPECTED_COUNT_OFFSET + 2*4) = m_commands[cmdNdx].numWorkGroups[2];
277 *(deUint32*)(dstPtr + RESULT_BLOCK_NUM_PASSED_OFFSET) = 0;
280 gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
281 gl.bufferData(GL_SHADER_STORAGE_BUFFER, (glw::GLsizei)data.size(), &data[0], GL_STATIC_READ);
284 deUint32 computeInvocationCount (const UVec3& workGroupSize, const UVec3& numWorkGroups)
286 const int numInvocationsPerGroup = workGroupSize[0]*workGroupSize[1]*workGroupSize[2];
287 const int numGroups = numWorkGroups[0]*numWorkGroups[1]*numWorkGroups[2];
289 return numInvocationsPerGroup*numGroups;
292 bool IndirectDispatchCase::verifyResultBuffer (deUint32 buffer)
294 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
296 const int resultBlockSize = getResultBlockAlignedSize(gl);
297 const int resultBufferSize = resultBlockSize*(int)m_commands.size();
299 void* mapPtr = DE_NULL;
304 gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
305 mapPtr = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, resultBufferSize, GL_MAP_READ_BIT);
307 GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() failed");
310 for (size_t cmdNdx = 0; cmdNdx < m_commands.size(); cmdNdx++)
312 const DispatchCommand& cmd = m_commands[cmdNdx];
313 const deUint8* const srcPtr = (const deUint8*)mapPtr + cmdNdx*resultBlockSize;
314 const deUint32 numPassed = *(const deUint32*)(srcPtr + RESULT_BLOCK_NUM_PASSED_OFFSET);
315 const deUint32 expectedCount = computeInvocationCount(m_workGroupSize, cmd.numWorkGroups);
318 if (numPassed != expectedCount)
320 m_testCtx.getLog() << TestLog::Message << "ERROR: got invalid result for invocation " << cmdNdx
321 << ": got numPassed = " << numPassed << ", expected " << expectedCount
322 << TestLog::EndMessage;
330 gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
333 gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
334 GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() failed");
339 IndirectDispatchCase::IterateResult IndirectDispatchCase::iterate (void)
341 const glu::RenderContext& renderCtx = m_context.getRenderContext();
342 const glw::Functions& gl = renderCtx.getFunctions();
344 const glu::ShaderProgram program (renderCtx, genVerifySources(m_workGroupSize));
346 glu::Buffer cmdBuffer (renderCtx);
347 glu::Buffer resultBuffer (renderCtx);
349 m_testCtx.getLog() << program;
350 TCU_CHECK_MSG(program.isOk(), "Compile failed");
352 m_testCtx.getLog() << TestLog::Message << "GL_DISPATCH_INDIRECT_BUFFER size = " << m_bufferSize << TestLog::EndMessage;
354 tcu::ScopedLogSection section(m_testCtx.getLog(), "Commands", "Indirect Dispatch Commands (" + de::toString(m_commands.size()) + " in total)");
356 for (size_t cmdNdx = 0; cmdNdx < m_commands.size(); cmdNdx++)
357 m_testCtx.getLog() << TestLog::Message << cmdNdx << ": " << "offset = " << m_commands[cmdNdx].offset
358 << ", numWorkGroups = " << m_commands[cmdNdx].numWorkGroups
359 << TestLog::EndMessage;
362 createResultBuffer(*resultBuffer);
363 createCommandBuffer(*cmdBuffer);
365 gl.useProgram(program.getProgram());
366 gl.bindBuffer(GL_DISPATCH_INDIRECT_BUFFER, *cmdBuffer);
367 GLU_EXPECT_NO_ERROR(gl.getError(), "State setup failed");
370 const int resultBlockAlignedSize = getResultBlockAlignedSize(gl);
371 deIntptr curOffset = 0;
373 for (vector<DispatchCommand>::const_iterator cmdIter = m_commands.begin(); cmdIter != m_commands.end(); ++cmdIter)
375 gl.bindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, *resultBuffer, (glw::GLintptr)curOffset, resultBlockAlignedSize);
376 gl.dispatchComputeIndirect((glw::GLintptr)cmdIter->offset);
378 curOffset += resultBlockAlignedSize;
382 GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchComputeIndirect() failed");
384 if (verifyResultBuffer(*resultBuffer))
385 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
387 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid values in result buffer");
392 class SingleDispatchCase : public IndirectDispatchCase
395 SingleDispatchCase (Context& context, const char* name, const char* description, GenBuffer genBuffer, deUintptr bufferSize, deUintptr offset, const UVec3& workGroupSize, const UVec3& numWorkGroups)
396 : IndirectDispatchCase(context, name, description, genBuffer)
398 m_bufferSize = bufferSize;
399 m_workGroupSize = workGroupSize;
400 m_commands.push_back(DispatchCommand(offset, numWorkGroups));
404 class MultiDispatchCase : public IndirectDispatchCase
407 MultiDispatchCase (Context& context, GenBuffer genBuffer)
408 : IndirectDispatchCase(context, "multi_dispatch", "Dispatch multiple compute commands from single buffer", genBuffer)
410 m_bufferSize = 1<<10;
411 m_workGroupSize = UVec3(3,1,2);
413 m_commands.push_back(DispatchCommand(0, UVec3(1,1,1)));
414 m_commands.push_back(DispatchCommand(INDIRECT_COMMAND_SIZE, UVec3(2,1,1)));
415 m_commands.push_back(DispatchCommand(104, UVec3(1,3,1)));
416 m_commands.push_back(DispatchCommand(40, UVec3(1,1,7)));
417 m_commands.push_back(DispatchCommand(52, UVec3(1,1,4)));
421 class MultiDispatchReuseCommandCase : public IndirectDispatchCase
424 MultiDispatchReuseCommandCase (Context& context, GenBuffer genBuffer)
425 : IndirectDispatchCase(context, "multi_dispatch_reuse_command", "Dispatch multiple compute commands from single buffer", genBuffer)
427 m_bufferSize = 1<<10;
428 m_workGroupSize = UVec3(3,1,2);
430 m_commands.push_back(DispatchCommand(0, UVec3(1,1,1)));
431 m_commands.push_back(DispatchCommand(0, UVec3(1,1,1)));
432 m_commands.push_back(DispatchCommand(0, UVec3(1,1,1)));
433 m_commands.push_back(DispatchCommand(104, UVec3(1,3,1)));
434 m_commands.push_back(DispatchCommand(104, UVec3(1,3,1)));
435 m_commands.push_back(DispatchCommand(52, UVec3(1,1,4)));
436 m_commands.push_back(DispatchCommand(52, UVec3(1,1,4)));
442 IndirectComputeDispatchTests::IndirectComputeDispatchTests (Context& context)
443 : TestCaseGroup(context, "indirect_dispatch", "Indirect dispatch tests")
447 IndirectComputeDispatchTests::~IndirectComputeDispatchTests (void)
451 void IndirectComputeDispatchTests::init (void)
459 { "upload_buffer", GEN_BUFFER_UPLOAD },
460 { "gen_in_compute", GEN_BUFFER_COMPUTE }
466 const char* description;
467 deUintptr bufferSize;
471 } s_singleDispatchCases[] =
473 // Name Desc BufferSize Offs WorkGroupSize NumWorkGroups
474 { "single_invocation", "Single invocation only from offset 0", INDIRECT_COMMAND_SIZE, 0, UVec3(1,1,1), UVec3(1,1,1) },
475 { "multiple_groups", "Multiple groups dispatched from offset 0", INDIRECT_COMMAND_SIZE, 0, UVec3(1,1,1), UVec3(2,3,5) },
476 { "multiple_groups_multiple_invocations", "Multiple groups of size 2x3x1 from offset 0", INDIRECT_COMMAND_SIZE, 0, UVec3(2,3,1), UVec3(1,2,3) },
477 { "small_offset", "Small offset", 16+INDIRECT_COMMAND_SIZE, 16, UVec3(1,1,1), UVec3(1,1,1) },
478 { "large_offset", "Large offset", (2<<20), (1<<20) + 12, UVec3(1,1,1), UVec3(1,1,1) },
479 { "large_offset_multiple_invocations", "Large offset, multiple invocations", (2<<20), (1<<20) + 12, UVec3(2,3,1), UVec3(1,2,3) },
480 { "empty_command", "Empty command", INDIRECT_COMMAND_SIZE, 0, UVec3(1,1,1), UVec3(0,0,0) },
483 for (int genNdx = 0; genNdx < DE_LENGTH_OF_ARRAY(s_genBuffer); genNdx++)
485 const GenBuffer genBuf = s_genBuffer[genNdx].gen;
486 tcu::TestCaseGroup* const genGroup = new tcu::TestCaseGroup(m_testCtx, s_genBuffer[genNdx].name, "");
489 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_singleDispatchCases); ndx++)
490 genGroup->addChild(new SingleDispatchCase(m_context,
491 s_singleDispatchCases[ndx].name,
492 s_singleDispatchCases[ndx].description,
494 s_singleDispatchCases[ndx].bufferSize,
495 s_singleDispatchCases[ndx].offset,
496 s_singleDispatchCases[ndx].workGroupSize,
497 s_singleDispatchCases[ndx].numWorkGroups));
499 genGroup->addChild(new MultiDispatchCase (m_context, genBuf));
500 genGroup->addChild(new MultiDispatchReuseCommandCase (m_context, genBuf));