xref: /aosp_15_r20/external/deqp/modules/gles31/functional/es31fSynchronizationTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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 Synchronization Tests
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es31fSynchronizationTests.hpp"
25 #include "tcuTestLog.hpp"
26 #include "tcuStringTemplate.hpp"
27 #include "tcuSurface.hpp"
28 #include "tcuRenderTarget.hpp"
29 #include "gluRenderContext.hpp"
30 #include "gluShaderProgram.hpp"
31 #include "gluObjectWrapper.hpp"
32 #include "gluPixelTransfer.hpp"
33 #include "gluContextInfo.hpp"
34 #include "glwFunctions.hpp"
35 #include "glwEnums.hpp"
36 #include "deStringUtil.hpp"
37 #include "deSharedPtr.hpp"
38 #include "deMemory.h"
39 #include "deRandom.hpp"
40 
41 #include <map>
42 
43 namespace deqp
44 {
45 namespace gles31
46 {
47 namespace Functional
48 {
49 namespace
50 {
51 
checkSupport(Context & ctx)52 static bool checkSupport(Context &ctx)
53 {
54     auto ctxType = ctx.getRenderContext().getType();
55     return contextSupports(ctxType, glu::ApiType::es(3, 2)) || contextSupports(ctxType, glu::ApiType::core(4, 5)) ||
56            ctx.getContextInfo().isExtensionSupported("GL_OES_shader_image_atomic");
57 }
58 
validateSortedAtomicRampAdditionValueChain(const std::vector<uint32_t> & valueChain,uint32_t sumValue,int & invalidOperationNdx,uint32_t & errorDelta,uint32_t & errorExpected)59 static bool validateSortedAtomicRampAdditionValueChain(const std::vector<uint32_t> &valueChain, uint32_t sumValue,
60                                                        int &invalidOperationNdx, uint32_t &errorDelta,
61                                                        uint32_t &errorExpected)
62 {
63     std::vector<uint32_t> chainDelta(valueChain.size());
64 
65     for (int callNdx = 0; callNdx < (int)valueChain.size(); ++callNdx)
66         chainDelta[callNdx] =
67             ((callNdx + 1 == (int)valueChain.size()) ? (sumValue) : (valueChain[callNdx + 1])) - valueChain[callNdx];
68 
69     // chainDelta contains now the actual additions applied to the value
70     // check there exists an addition ramp form 1 to ...
71     std::sort(chainDelta.begin(), chainDelta.end());
72 
73     for (int callNdx = 0; callNdx < (int)valueChain.size(); ++callNdx)
74     {
75         if ((int)chainDelta[callNdx] != callNdx + 1)
76         {
77             invalidOperationNdx = callNdx;
78             errorDelta          = chainDelta[callNdx];
79             errorExpected       = callNdx + 1;
80 
81             return false;
82         }
83     }
84 
85     return true;
86 }
87 
readBuffer(const glw::Functions & gl,uint32_t target,int numElements,std::vector<uint32_t> & result)88 static void readBuffer(const glw::Functions &gl, uint32_t target, int numElements, std::vector<uint32_t> &result)
89 {
90     const void *ptr = gl.mapBufferRange(target, 0, (int)(sizeof(uint32_t) * numElements), GL_MAP_READ_BIT);
91     GLU_EXPECT_NO_ERROR(gl.getError(), "map");
92 
93     if (!ptr)
94         throw tcu::TestError("mapBufferRange returned NULL");
95 
96     result.resize(numElements);
97     memcpy(&result[0], ptr, sizeof(uint32_t) * numElements);
98 
99     if (gl.unmapBuffer(target) == GL_FALSE)
100         throw tcu::TestError("unmapBuffer returned false");
101 }
102 
readBufferUint32(const glw::Functions & gl,uint32_t target)103 static uint32_t readBufferUint32(const glw::Functions &gl, uint32_t target)
104 {
105     std::vector<uint32_t> vec;
106 
107     readBuffer(gl, target, 1, vec);
108 
109     return vec[0];
110 }
111 
112 //! Generate a ramp of values from 1 to numElements, and shuffle it
generateShuffledRamp(int numElements,std::vector<int> & ramp)113 void generateShuffledRamp(int numElements, std::vector<int> &ramp)
114 {
115     de::Random rng(0xabcd);
116 
117     // some positive (non-zero) unique values
118     ramp.resize(numElements);
119     for (int callNdx = 0; callNdx < numElements; ++callNdx)
120         ramp[callNdx] = callNdx + 1;
121 
122     rng.shuffle(ramp.begin(), ramp.end());
123 }
124 
specializeShader(Context & context,const char * code)125 static std::string specializeShader(Context &context, const char *code)
126 {
127     auto ctxType            = context.getRenderContext().getType();
128     const bool isES32orGL45 = glu::contextSupports(ctxType, glu::ApiType::es(3, 2)) ||
129                               glu::contextSupports(ctxType, glu::ApiType::core(4, 5));
130     const glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(ctxType);
131 
132     std::map<std::string, std::string> specializationMap;
133     specializationMap["GLSL_VERSION_DECL"] = glu::getGLSLVersionDeclaration(glslVersion);
134     specializationMap["SHADER_IMAGE_ATOMIC_REQUIRE"] =
135         isES32orGL45 ? "" : "#extension GL_OES_shader_image_atomic : require";
136 
137     return tcu::StringTemplate(code).specialize(specializationMap);
138 }
139 
140 class InterInvocationTestCase : public TestCase
141 {
142 public:
143     enum StorageType
144     {
145         STORAGE_BUFFER = 0,
146         STORAGE_IMAGE,
147 
148         STORAGE_LAST
149     };
150     enum CaseFlags
151     {
152         FLAG_ATOMIC            = 0x1,
153         FLAG_ALIASING_STORAGES = 0x2,
154         FLAG_IN_GROUP          = 0x4,
155     };
156 
157     InterInvocationTestCase(Context &context, const char *name, const char *desc, StorageType storage, int flags = 0);
158     ~InterInvocationTestCase(void);
159 
160 private:
161     void init(void);
162     void deinit(void);
163     IterateResult iterate(void);
164 
165     void runCompute(void);
166     bool verifyResults(void);
167     virtual std::string genShaderSource(void) const = 0;
168 
169 protected:
170     std::string genBarrierSource(void) const;
171 
172     const StorageType m_storage;
173     const bool m_useAtomic;
174     const bool m_aliasingStorages;
175     const bool m_syncWithGroup;
176     const int m_workWidth;             // !< total work width
177     const int m_workHeight;            // !<     ...    height
178     const int m_localWidth;            // !< group width
179     const int m_localHeight;           // !< group height
180     const int m_elementsPerInvocation; // !< elements accessed by a single invocation
181 
182 private:
183     glw::GLuint m_storageBuf;
184     glw::GLuint m_storageTex;
185     glw::GLuint m_resultBuf;
186     glu::ShaderProgram *m_program;
187 };
188 
InterInvocationTestCase(Context & context,const char * name,const char * desc,StorageType storage,int flags)189 InterInvocationTestCase::InterInvocationTestCase(Context &context, const char *name, const char *desc,
190                                                  StorageType storage, int flags)
191     : TestCase(context, name, desc)
192     , m_storage(storage)
193     , m_useAtomic((flags & FLAG_ATOMIC) != 0)
194     , m_aliasingStorages((flags & FLAG_ALIASING_STORAGES) != 0)
195     , m_syncWithGroup((flags & FLAG_IN_GROUP) != 0)
196     , m_workWidth(256)
197     , m_workHeight(256)
198     , m_localWidth(16)
199     , m_localHeight(8)
200     , m_elementsPerInvocation(8)
201     , m_storageBuf(0)
202     , m_storageTex(0)
203     , m_resultBuf(0)
204     , m_program(DE_NULL)
205 {
206     DE_ASSERT(m_storage < STORAGE_LAST);
207     DE_ASSERT(m_localWidth * m_localHeight <= 128); // minimum MAX_COMPUTE_WORK_GROUP_INVOCATIONS value
208 }
209 
~InterInvocationTestCase(void)210 InterInvocationTestCase::~InterInvocationTestCase(void)
211 {
212     deinit();
213 }
214 
init(void)215 void InterInvocationTestCase::init(void)
216 {
217     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
218 
219     // requirements
220 
221     if (m_useAtomic && m_storage == STORAGE_IMAGE && !checkSupport(m_context))
222         throw tcu::NotSupportedError("Test requires GL_OES_shader_image_atomic extension");
223 
224     // program
225 
226     m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
227                                                                          << glu::ComputeSource(genShaderSource()));
228     m_testCtx.getLog() << *m_program;
229     if (!m_program->isOk())
230         throw tcu::TestError("could not build program");
231 
232     // source
233 
234     if (m_storage == STORAGE_BUFFER)
235     {
236         const int bufferElements = m_workWidth * m_workHeight * m_elementsPerInvocation;
237         const int bufferSize     = bufferElements * (int)sizeof(uint32_t);
238         std::vector<uint32_t> zeroBuffer(bufferElements, 0);
239 
240         m_testCtx.getLog() << tcu::TestLog::Message << "Allocating zero-filled buffer for storage, size "
241                            << bufferElements << " elements, " << bufferSize << " bytes." << tcu::TestLog::EndMessage;
242 
243         gl.genBuffers(1, &m_storageBuf);
244         gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_storageBuf);
245         gl.bufferData(GL_SHADER_STORAGE_BUFFER, bufferSize, &zeroBuffer[0], GL_STATIC_DRAW);
246         GLU_EXPECT_NO_ERROR(gl.getError(), "gen storage buf");
247     }
248     else if (m_storage == STORAGE_IMAGE)
249     {
250         const int bufferElements = m_workWidth * m_workHeight * m_elementsPerInvocation;
251         const int bufferSize     = bufferElements * (int)sizeof(uint32_t);
252 
253         m_testCtx.getLog() << tcu::TestLog::Message << "Allocating image for storage, size " << m_workWidth << "x"
254                            << m_workHeight * m_elementsPerInvocation << ", " << bufferSize << " bytes."
255                            << tcu::TestLog::EndMessage;
256 
257         gl.genTextures(1, &m_storageTex);
258         gl.bindTexture(GL_TEXTURE_2D, m_storageTex);
259         gl.texStorage2D(GL_TEXTURE_2D, 1, GL_R32I, m_workWidth, m_workHeight * m_elementsPerInvocation);
260         gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
261         gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
262         GLU_EXPECT_NO_ERROR(gl.getError(), "gen storage image");
263 
264         // Zero-fill
265         m_testCtx.getLog() << tcu::TestLog::Message << "Filling image with 0." << tcu::TestLog::EndMessage;
266 
267         {
268             const std::vector<int32_t> zeroBuffer(m_workWidth * m_workHeight * m_elementsPerInvocation, 0);
269             gl.texSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_workWidth, m_workHeight * m_elementsPerInvocation,
270                              GL_RED_INTEGER, GL_INT, &zeroBuffer[0]);
271             GLU_EXPECT_NO_ERROR(gl.getError(), "specify image contents");
272         }
273     }
274     else
275         DE_ASSERT(false);
276 
277     // destination
278 
279     {
280         const int bufferElements = m_workWidth * m_workHeight;
281         const int bufferSize     = bufferElements * (int)sizeof(uint32_t);
282         std::vector<int32_t> negativeBuffer(bufferElements, -1);
283 
284         m_testCtx.getLog() << tcu::TestLog::Message << "Allocating -1 filled buffer for results, size "
285                            << bufferElements << " elements, " << bufferSize << " bytes." << tcu::TestLog::EndMessage;
286 
287         gl.genBuffers(1, &m_resultBuf);
288         gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_resultBuf);
289         gl.bufferData(GL_SHADER_STORAGE_BUFFER, bufferSize, &negativeBuffer[0], GL_STATIC_DRAW);
290         GLU_EXPECT_NO_ERROR(gl.getError(), "gen storage buf");
291     }
292 }
293 
deinit(void)294 void InterInvocationTestCase::deinit(void)
295 {
296     if (m_storageBuf)
297     {
298         m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_storageBuf);
299         m_storageBuf = DE_NULL;
300     }
301 
302     if (m_storageTex)
303     {
304         m_context.getRenderContext().getFunctions().deleteTextures(1, &m_storageTex);
305         m_storageTex = DE_NULL;
306     }
307 
308     if (m_resultBuf)
309     {
310         m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_resultBuf);
311         m_resultBuf = DE_NULL;
312     }
313 
314     delete m_program;
315     m_program = DE_NULL;
316 }
317 
iterate(void)318 InterInvocationTestCase::IterateResult InterInvocationTestCase::iterate(void)
319 {
320     // Dispatch
321     runCompute();
322 
323     // Verify buffer contents
324     if (verifyResults())
325         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
326     else
327         m_testCtx.setTestResult(
328             QP_TEST_RESULT_FAIL,
329             (std::string((m_storage == STORAGE_BUFFER) ? ("buffer") : ("image")) + " content verification failed")
330                 .c_str());
331 
332     return STOP;
333 }
334 
runCompute(void)335 void InterInvocationTestCase::runCompute(void)
336 {
337     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
338     const int groupsX        = m_workWidth / m_localWidth;
339     const int groupsY        = m_workHeight / m_localHeight;
340 
341     DE_ASSERT((m_workWidth % m_localWidth) == 0);
342     DE_ASSERT((m_workHeight % m_localHeight) == 0);
343 
344     m_testCtx.getLog() << tcu::TestLog::Message << "Dispatching compute.\n"
345                        << "    group size: " << m_localWidth << "x" << m_localHeight << "\n"
346                        << "    dispatch size: " << groupsX << "x" << groupsY << "\n"
347                        << "    total work size: " << m_workWidth << "x" << m_workHeight << "\n"
348                        << tcu::TestLog::EndMessage;
349 
350     gl.useProgram(m_program->getProgram());
351 
352     // source
353     if (m_storage == STORAGE_BUFFER && !m_aliasingStorages)
354     {
355         gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_storageBuf);
356         GLU_EXPECT_NO_ERROR(gl.getError(), "bind source buf");
357     }
358     else if (m_storage == STORAGE_BUFFER && m_aliasingStorages)
359     {
360         gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_storageBuf);
361         gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, m_storageBuf);
362         GLU_EXPECT_NO_ERROR(gl.getError(), "bind source buf");
363 
364         m_testCtx.getLog() << tcu::TestLog::Message << "Binding same buffer object to buffer storages."
365                            << tcu::TestLog::EndMessage;
366     }
367     else if (m_storage == STORAGE_IMAGE && !m_aliasingStorages)
368     {
369         gl.bindImageTexture(1, m_storageTex, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32I);
370         GLU_EXPECT_NO_ERROR(gl.getError(), "bind result buf");
371     }
372     else if (m_storage == STORAGE_IMAGE && m_aliasingStorages)
373     {
374         gl.bindImageTexture(1, m_storageTex, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32I);
375         gl.bindImageTexture(2, m_storageTex, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32I);
376 
377         GLU_EXPECT_NO_ERROR(gl.getError(), "bind result buf");
378 
379         m_testCtx.getLog() << tcu::TestLog::Message << "Binding same texture level to image storages."
380                            << tcu::TestLog::EndMessage;
381     }
382     else
383         DE_ASSERT(false);
384 
385     // destination
386     gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_resultBuf);
387     GLU_EXPECT_NO_ERROR(gl.getError(), "bind result buf");
388 
389     // dispatch
390     gl.dispatchCompute(groupsX, groupsY, 1);
391     GLU_EXPECT_NO_ERROR(gl.getError(), "dispatchCompute");
392 }
393 
verifyResults(void)394 bool InterInvocationTestCase::verifyResults(void)
395 {
396     const glw::Functions &gl      = m_context.getRenderContext().getFunctions();
397     const int errorFloodThreshold = 5;
398     int numErrorsLogged           = 0;
399     const void *mapped            = DE_NULL;
400     std::vector<int32_t> results(m_workWidth * m_workHeight);
401     bool error = false;
402 
403     gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_resultBuf);
404     gl.memoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
405     mapped =
406         gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, m_workWidth * m_workHeight * sizeof(int32_t), GL_MAP_READ_BIT);
407     GLU_EXPECT_NO_ERROR(gl.getError(), "map buffer");
408 
409     // copy to properly aligned array
410     deMemcpy(&results[0], mapped, m_workWidth * m_workHeight * sizeof(uint32_t));
411 
412     if (gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER) != GL_TRUE)
413         throw tcu::TestError("memory map store corrupted");
414 
415     // check the results
416     for (int ndx = 0; ndx < (int)results.size(); ++ndx)
417     {
418         if (results[ndx] != 1)
419         {
420             error = true;
421 
422             if (numErrorsLogged == 0)
423                 m_testCtx.getLog() << tcu::TestLog::Message << "Result buffer failed, got unexpected values.\n"
424                                    << tcu::TestLog::EndMessage;
425             if (numErrorsLogged++ < errorFloodThreshold)
426                 m_testCtx.getLog() << tcu::TestLog::Message << "    Error at index " << ndx << ": expected 1, got "
427                                    << results[ndx] << ".\n"
428                                    << tcu::TestLog::EndMessage;
429             else
430             {
431                 // after N errors, no point continuing verification
432                 m_testCtx.getLog() << tcu::TestLog::Message << "    -- too many errors, skipping verification --\n"
433                                    << tcu::TestLog::EndMessage;
434                 break;
435             }
436         }
437     }
438 
439     if (!error)
440         m_testCtx.getLog() << tcu::TestLog::Message << "Result buffer ok." << tcu::TestLog::EndMessage;
441     return !error;
442 }
443 
genBarrierSource(void) const444 std::string InterInvocationTestCase::genBarrierSource(void) const
445 {
446     std::ostringstream buf;
447 
448     if (m_syncWithGroup)
449     {
450         // Wait until all invocations in this work group have their texture/buffer read/write operations complete
451         // \note We could also use memoryBarrierBuffer() or memoryBarrierImage() in place of groupMemoryBarrier() but
452         //       we only require intra-workgroup synchronization.
453         buf << "\n"
454             << "    groupMemoryBarrier();\n"
455             << "    barrier();\n"
456             << "\n";
457     }
458     else if (m_storage == STORAGE_BUFFER)
459     {
460         DE_ASSERT(!m_syncWithGroup);
461 
462         // Waiting only for data written by this invocation. Since all buffer reads and writes are
463         // processed in order (within a single invocation), we don't have to do anything.
464         buf << "\n";
465     }
466     else if (m_storage == STORAGE_IMAGE)
467     {
468         DE_ASSERT(!m_syncWithGroup);
469 
470         // Waiting only for data written by this invocation. But since operations complete in undefined
471         // order, we have to wait for them to complete.
472         buf << "\n"
473             << "    memoryBarrierImage();\n"
474             << "\n";
475     }
476     else
477         DE_ASSERT(false);
478 
479     return buf.str();
480 }
481 
482 class InvocationBasicCase : public InterInvocationTestCase
483 {
484 public:
485     InvocationBasicCase(Context &context, const char *name, const char *desc, StorageType storage, int flags);
486 
487 private:
488     std::string genShaderSource(void) const;
489     virtual std::string genShaderMainBlock(void) const = 0;
490 };
491 
InvocationBasicCase(Context & context,const char * name,const char * desc,StorageType storage,int flags)492 InvocationBasicCase::InvocationBasicCase(Context &context, const char *name, const char *desc, StorageType storage,
493                                          int flags)
494     : InterInvocationTestCase(context, name, desc, storage, flags)
495 {
496 }
497 
genShaderSource(void) const498 std::string InvocationBasicCase::genShaderSource(void) const
499 {
500     const bool useImageAtomics = m_useAtomic && m_storage == STORAGE_IMAGE;
501     std::ostringstream buf;
502 
503     buf << "${GLSL_VERSION_DECL}\n"
504         << ((useImageAtomics) ? ("${SHADER_IMAGE_ATOMIC_REQUIRE}\n") : ("")) << "layout (local_size_x=" << m_localWidth
505         << ", local_size_y=" << m_localHeight << ") in;\n"
506         << "layout(binding=0, std430) buffer Output\n"
507         << "{\n"
508         << "    highp int values[];\n"
509         << "} sb_result;\n";
510 
511     if (m_storage == STORAGE_BUFFER)
512         buf << "layout(binding=1, std430) coherent buffer Storage\n"
513             << "{\n"
514             << "    highp int values[];\n"
515             << "} sb_store;\n"
516             << "\n"
517             << "highp int getIndex (in highp uvec2 localID, in highp int element)\n"
518             << "{\n"
519             << "    highp uint groupNdx = gl_NumWorkGroups.x * gl_WorkGroupID.y + gl_WorkGroupID.x;\n"
520             << "    return int((localID.y * gl_NumWorkGroups.x * gl_NumWorkGroups.y * gl_WorkGroupSize.x) + (groupNdx "
521                "* gl_WorkGroupSize.x) + localID.x) * "
522             << m_elementsPerInvocation << " + element;\n"
523             << "}\n";
524     else if (m_storage == STORAGE_IMAGE)
525         buf << "layout(r32i, binding=1) coherent uniform highp iimage2D u_image;\n"
526             << "\n"
527             << "highp ivec2 getCoord (in highp uvec2 localID, in highp int element)\n"
528             << "{\n"
529             << "    return ivec2(int(gl_WorkGroupID.x * gl_WorkGroupSize.x + localID.x), int(gl_WorkGroupID.y * "
530                "gl_WorkGroupSize.y + localID.y) + element * "
531             << m_workHeight << ");\n"
532             << "}\n";
533     else
534         DE_ASSERT(false);
535 
536     buf << "\n"
537         << "void main (void)\n"
538         << "{\n"
539         << "    int resultNdx   = int(gl_GlobalInvocationID.y * gl_NumWorkGroups.x * gl_WorkGroupSize.x + "
540            "gl_GlobalInvocationID.x);\n"
541         << "    int groupNdx    = int(gl_NumWorkGroups.x * gl_WorkGroupID.y + gl_WorkGroupID.x);\n"
542         << "    bool allOk      = true;\n"
543         << "\n"
544         << genShaderMainBlock() << "\n"
545         << "    sb_result.values[resultNdx] = (allOk) ? (1) : (0);\n"
546         << "}\n";
547 
548     return specializeShader(m_context, buf.str().c_str());
549 }
550 
551 class InvocationWriteReadCase : public InvocationBasicCase
552 {
553 public:
554     InvocationWriteReadCase(Context &context, const char *name, const char *desc, StorageType storage, int flags);
555 
556 private:
557     std::string genShaderMainBlock(void) const;
558 };
559 
InvocationWriteReadCase(Context & context,const char * name,const char * desc,StorageType storage,int flags)560 InvocationWriteReadCase::InvocationWriteReadCase(Context &context, const char *name, const char *desc,
561                                                  StorageType storage, int flags)
562     : InvocationBasicCase(context, name, desc, storage, flags)
563 {
564 }
565 
genShaderMainBlock(void) const566 std::string InvocationWriteReadCase::genShaderMainBlock(void) const
567 {
568     std::ostringstream buf;
569 
570     // write
571 
572     for (int ndx = 0; ndx < m_elementsPerInvocation; ++ndx)
573     {
574         if (m_storage == STORAGE_BUFFER && m_useAtomic)
575             buf << "\tatomicAdd(sb_store.values[getIndex(gl_LocalInvocationID.xy, " << ndx << ")], groupNdx);\n";
576         else if (m_storage == STORAGE_BUFFER && !m_useAtomic)
577             buf << "\tsb_store.values[getIndex(gl_LocalInvocationID.xy, " << ndx << ")] = groupNdx;\n";
578         else if (m_storage == STORAGE_IMAGE && m_useAtomic)
579             buf << "\timageAtomicAdd(u_image, getCoord(gl_LocalInvocationID.xy, " << ndx << "), int(groupNdx));\n";
580         else if (m_storage == STORAGE_IMAGE && !m_useAtomic)
581             buf << "\timageStore(u_image, getCoord(gl_LocalInvocationID.xy, " << ndx
582                 << "), ivec4(int(groupNdx), 0, 0, 0));\n";
583         else
584             DE_ASSERT(false);
585     }
586 
587     // barrier
588 
589     buf << genBarrierSource();
590 
591     // read
592 
593     for (int ndx = 0; ndx < m_elementsPerInvocation; ++ndx)
594     {
595         const std::string localID = (m_syncWithGroup) ? ("(gl_LocalInvocationID.xy + uvec2(" + de::toString(ndx + 1) +
596                                                          ", " + de::toString(2 * ndx) + ")) % gl_WorkGroupSize.xy") :
597                                                         ("gl_LocalInvocationID.xy");
598 
599         if (m_storage == STORAGE_BUFFER && m_useAtomic)
600             buf << "\tallOk = allOk && (atomicExchange(sb_store.values[getIndex(" << localID << ", " << ndx
601                 << ")], 0) == groupNdx);\n";
602         else if (m_storage == STORAGE_BUFFER && !m_useAtomic)
603             buf << "\tallOk = allOk && (sb_store.values[getIndex(" << localID << ", " << ndx << ")] == groupNdx);\n";
604         else if (m_storage == STORAGE_IMAGE && m_useAtomic)
605             buf << "\tallOk = allOk && (imageAtomicExchange(u_image, getCoord(" << localID << ", " << ndx
606                 << "), 0) == groupNdx);\n";
607         else if (m_storage == STORAGE_IMAGE && !m_useAtomic)
608             buf << "\tallOk = allOk && (imageLoad(u_image, getCoord(" << localID << ", " << ndx
609                 << ")).x == groupNdx);\n";
610         else
611             DE_ASSERT(false);
612     }
613 
614     return buf.str();
615 }
616 
617 class InvocationReadWriteCase : public InvocationBasicCase
618 {
619 public:
620     InvocationReadWriteCase(Context &context, const char *name, const char *desc, StorageType storage, int flags);
621 
622 private:
623     std::string genShaderMainBlock(void) const;
624 };
625 
InvocationReadWriteCase(Context & context,const char * name,const char * desc,StorageType storage,int flags)626 InvocationReadWriteCase::InvocationReadWriteCase(Context &context, const char *name, const char *desc,
627                                                  StorageType storage, int flags)
628     : InvocationBasicCase(context, name, desc, storage, flags)
629 {
630 }
631 
genShaderMainBlock(void) const632 std::string InvocationReadWriteCase::genShaderMainBlock(void) const
633 {
634     std::ostringstream buf;
635 
636     // read
637 
638     for (int ndx = 0; ndx < m_elementsPerInvocation; ++ndx)
639     {
640         const std::string localID = (m_syncWithGroup) ? ("(gl_LocalInvocationID.xy + uvec2(" + de::toString(ndx + 1) +
641                                                          ", " + de::toString(2 * ndx) + ")) % gl_WorkGroupSize.xy") :
642                                                         ("gl_LocalInvocationID.xy");
643 
644         if (m_storage == STORAGE_BUFFER && m_useAtomic)
645             buf << "\tallOk = allOk && (atomicExchange(sb_store.values[getIndex(" << localID << ", " << ndx
646                 << ")], 123) == 0);\n";
647         else if (m_storage == STORAGE_BUFFER && !m_useAtomic)
648             buf << "\tallOk = allOk && (sb_store.values[getIndex(" << localID << ", " << ndx << ")] == 0);\n";
649         else if (m_storage == STORAGE_IMAGE && m_useAtomic)
650             buf << "\tallOk = allOk && (imageAtomicExchange(u_image, getCoord(" << localID << ", " << ndx
651                 << "), 123) == 0);\n";
652         else if (m_storage == STORAGE_IMAGE && !m_useAtomic)
653             buf << "\tallOk = allOk && (imageLoad(u_image, getCoord(" << localID << ", " << ndx << ")).x == 0);\n";
654         else
655             DE_ASSERT(false);
656     }
657 
658     // barrier
659 
660     buf << genBarrierSource();
661 
662     // write
663 
664     for (int ndx = 0; ndx < m_elementsPerInvocation; ++ndx)
665     {
666         if (m_storage == STORAGE_BUFFER && m_useAtomic)
667             buf << "\tatomicAdd(sb_store.values[getIndex(gl_LocalInvocationID.xy, " << ndx << ")], groupNdx);\n";
668         else if (m_storage == STORAGE_BUFFER && !m_useAtomic)
669             buf << "\tsb_store.values[getIndex(gl_LocalInvocationID.xy, " << ndx << ")] = groupNdx;\n";
670         else if (m_storage == STORAGE_IMAGE && m_useAtomic)
671             buf << "\timageAtomicAdd(u_image, getCoord(gl_LocalInvocationID.xy, " << ndx << "), int(groupNdx));\n";
672         else if (m_storage == STORAGE_IMAGE && !m_useAtomic)
673             buf << "\timageStore(u_image, getCoord(gl_LocalInvocationID.xy, " << ndx
674                 << "), ivec4(int(groupNdx), 0, 0, 0));\n";
675         else
676             DE_ASSERT(false);
677     }
678 
679     return buf.str();
680 }
681 
682 class InvocationOverWriteCase : public InvocationBasicCase
683 {
684 public:
685     InvocationOverWriteCase(Context &context, const char *name, const char *desc, StorageType storage, int flags);
686 
687 private:
688     std::string genShaderMainBlock(void) const;
689 };
690 
InvocationOverWriteCase(Context & context,const char * name,const char * desc,StorageType storage,int flags)691 InvocationOverWriteCase::InvocationOverWriteCase(Context &context, const char *name, const char *desc,
692                                                  StorageType storage, int flags)
693     : InvocationBasicCase(context, name, desc, storage, flags)
694 {
695 }
696 
genShaderMainBlock(void) const697 std::string InvocationOverWriteCase::genShaderMainBlock(void) const
698 {
699     std::ostringstream buf;
700 
701     // write
702 
703     for (int ndx = 0; ndx < m_elementsPerInvocation; ++ndx)
704     {
705         if (m_storage == STORAGE_BUFFER && m_useAtomic)
706             buf << "\tatomicAdd(sb_store.values[getIndex(gl_LocalInvocationID.xy, " << ndx << ")], 456);\n";
707         else if (m_storage == STORAGE_BUFFER && !m_useAtomic)
708             buf << "\tsb_store.values[getIndex(gl_LocalInvocationID.xy, " << ndx << ")] = 456;\n";
709         else if (m_storage == STORAGE_IMAGE && m_useAtomic)
710             buf << "\timageAtomicAdd(u_image, getCoord(gl_LocalInvocationID.xy, " << ndx << "), 456);\n";
711         else if (m_storage == STORAGE_IMAGE && !m_useAtomic)
712             buf << "\timageStore(u_image, getCoord(gl_LocalInvocationID.xy, " << ndx << "), ivec4(456, 0, 0, 0));\n";
713         else
714             DE_ASSERT(false);
715     }
716 
717     // barrier
718 
719     buf << genBarrierSource();
720 
721     // write over
722 
723     for (int ndx = 0; ndx < m_elementsPerInvocation; ++ndx)
724     {
725         // write another invocation's value or our own value depending on test type
726         const std::string localID = (m_syncWithGroup) ? ("(gl_LocalInvocationID.xy + uvec2(" + de::toString(ndx + 4) +
727                                                          ", " + de::toString(3 * ndx) + ")) % gl_WorkGroupSize.xy") :
728                                                         ("gl_LocalInvocationID.xy");
729 
730         if (m_storage == STORAGE_BUFFER && m_useAtomic)
731             buf << "\tatomicExchange(sb_store.values[getIndex(" << localID << ", " << ndx << ")], groupNdx);\n";
732         else if (m_storage == STORAGE_BUFFER && !m_useAtomic)
733             buf << "\tsb_store.values[getIndex(" << localID << ", " << ndx << ")] = groupNdx;\n";
734         else if (m_storage == STORAGE_IMAGE && m_useAtomic)
735             buf << "\timageAtomicExchange(u_image, getCoord(" << localID << ", " << ndx << "), groupNdx);\n";
736         else if (m_storage == STORAGE_IMAGE && !m_useAtomic)
737             buf << "\timageStore(u_image, getCoord(" << localID << ", " << ndx << "), ivec4(groupNdx, 0, 0, 0));\n";
738         else
739             DE_ASSERT(false);
740     }
741 
742     // barrier
743 
744     buf << genBarrierSource();
745 
746     // read
747 
748     for (int ndx = 0; ndx < m_elementsPerInvocation; ++ndx)
749     {
750         // check another invocation's value or our own value depending on test type
751         const std::string localID = (m_syncWithGroup) ? ("(gl_LocalInvocationID.xy + uvec2(" + de::toString(ndx + 1) +
752                                                          ", " + de::toString(2 * ndx) + ")) % gl_WorkGroupSize.xy") :
753                                                         ("gl_LocalInvocationID.xy");
754 
755         if (m_storage == STORAGE_BUFFER && m_useAtomic)
756             buf << "\tallOk = allOk && (atomicExchange(sb_store.values[getIndex(" << localID << ", " << ndx
757                 << ")], 123) == groupNdx);\n";
758         else if (m_storage == STORAGE_BUFFER && !m_useAtomic)
759             buf << "\tallOk = allOk && (sb_store.values[getIndex(" << localID << ", " << ndx << ")] == groupNdx);\n";
760         else if (m_storage == STORAGE_IMAGE && m_useAtomic)
761             buf << "\tallOk = allOk && (imageAtomicExchange(u_image, getCoord(" << localID << ", " << ndx
762                 << "), 123) == groupNdx);\n";
763         else if (m_storage == STORAGE_IMAGE && !m_useAtomic)
764             buf << "\tallOk = allOk && (imageLoad(u_image, getCoord(" << localID << ", " << ndx
765                 << ")).x == groupNdx);\n";
766         else
767             DE_ASSERT(false);
768     }
769 
770     return buf.str();
771 }
772 
773 class InvocationAliasWriteCase : public InterInvocationTestCase
774 {
775 public:
776     enum TestType
777     {
778         TYPE_WRITE = 0,
779         TYPE_OVERWRITE,
780 
781         TYPE_LAST
782     };
783 
784     InvocationAliasWriteCase(Context &context, const char *name, const char *desc, TestType type, StorageType storage,
785                              int flags);
786 
787 private:
788     std::string genShaderSource(void) const;
789 
790     const TestType m_type;
791 };
792 
InvocationAliasWriteCase(Context & context,const char * name,const char * desc,TestType type,StorageType storage,int flags)793 InvocationAliasWriteCase::InvocationAliasWriteCase(Context &context, const char *name, const char *desc, TestType type,
794                                                    StorageType storage, int flags)
795     : InterInvocationTestCase(context, name, desc, storage, flags | FLAG_ALIASING_STORAGES)
796     , m_type(type)
797 {
798     DE_ASSERT(type < TYPE_LAST);
799 }
800 
genShaderSource(void) const801 std::string InvocationAliasWriteCase::genShaderSource(void) const
802 {
803     const bool useImageAtomics = m_useAtomic && m_storage == STORAGE_IMAGE;
804     std::ostringstream buf;
805 
806     buf << "${GLSL_VERSION_DECL}\n"
807         << ((useImageAtomics) ? ("${SHADER_IMAGE_ATOMIC_REQUIRE}\n") : ("")) << "layout (local_size_x=" << m_localWidth
808         << ", local_size_y=" << m_localHeight << ") in;\n"
809         << "layout(binding=0, std430) buffer Output\n"
810         << "{\n"
811         << "    highp int values[];\n"
812         << "} sb_result;\n";
813 
814     if (m_storage == STORAGE_BUFFER)
815         buf << "layout(binding=1, std430) coherent buffer Storage0\n"
816             << "{\n"
817             << "    highp int values[];\n"
818             << "} sb_store0;\n"
819             << "layout(binding=2, std430) coherent buffer Storage1\n"
820             << "{\n"
821             << "    highp int values[];\n"
822             << "} sb_store1;\n"
823             << "\n"
824             << "highp int getIndex (in highp uvec2 localID, in highp int element)\n"
825             << "{\n"
826             << "    highp uint groupNdx = gl_NumWorkGroups.x * gl_WorkGroupID.y + gl_WorkGroupID.x;\n"
827             << "    return int((localID.y * gl_NumWorkGroups.x * gl_NumWorkGroups.y * gl_WorkGroupSize.x) + (groupNdx "
828                "* gl_WorkGroupSize.x) + localID.x) * "
829             << m_elementsPerInvocation << " + element;\n"
830             << "}\n";
831     else if (m_storage == STORAGE_IMAGE)
832         buf << "layout(r32i, binding=1) coherent uniform highp iimage2D u_image0;\n"
833             << "layout(r32i, binding=2) coherent uniform highp iimage2D u_image1;\n"
834             << "\n"
835             << "highp ivec2 getCoord (in highp uvec2 localID, in highp int element)\n"
836             << "{\n"
837             << "    return ivec2(int(gl_WorkGroupID.x * gl_WorkGroupSize.x + localID.x), int(gl_WorkGroupID.y * "
838                "gl_WorkGroupSize.y + localID.y) + element * "
839             << m_workHeight << ");\n"
840             << "}\n";
841     else
842         DE_ASSERT(false);
843 
844     buf << "\n"
845         << "void main (void)\n"
846         << "{\n"
847         << "    int resultNdx   = int(gl_GlobalInvocationID.y * gl_NumWorkGroups.x * gl_WorkGroupSize.x + "
848            "gl_GlobalInvocationID.x);\n"
849         << "    int groupNdx    = int(gl_NumWorkGroups.x * gl_WorkGroupID.y + gl_WorkGroupID.x);\n"
850         << "    bool allOk      = true;\n"
851         << "\n";
852 
853     if (m_type == TYPE_OVERWRITE)
854     {
855         // write
856 
857         for (int ndx = 0; ndx < m_elementsPerInvocation; ++ndx)
858         {
859             if (m_storage == STORAGE_BUFFER && m_useAtomic)
860                 buf << "\tatomicAdd(sb_store0.values[getIndex(gl_LocalInvocationID.xy, " << ndx << ")], 456);\n";
861             else if (m_storage == STORAGE_BUFFER && !m_useAtomic)
862                 buf << "\tsb_store0.values[getIndex(gl_LocalInvocationID.xy, " << ndx << ")] = 456;\n";
863             else if (m_storage == STORAGE_IMAGE && m_useAtomic)
864                 buf << "\timageAtomicAdd(u_image0, getCoord(gl_LocalInvocationID.xy, " << ndx << "), 456);\n";
865             else if (m_storage == STORAGE_IMAGE && !m_useAtomic)
866                 buf << "\timageStore(u_image0, getCoord(gl_LocalInvocationID.xy, " << ndx
867                     << "), ivec4(456, 0, 0, 0));\n";
868             else
869                 DE_ASSERT(false);
870         }
871 
872         // barrier
873 
874         buf << genBarrierSource();
875     }
876     else
877         DE_ASSERT(m_type == TYPE_WRITE);
878 
879     // write (again)
880 
881     for (int ndx = 0; ndx < m_elementsPerInvocation; ++ndx)
882     {
883         const std::string localID = (m_syncWithGroup) ? ("(gl_LocalInvocationID.xy + uvec2(" + de::toString(ndx + 2) +
884                                                          ", " + de::toString(2 * ndx) + ")) % gl_WorkGroupSize.xy") :
885                                                         ("gl_LocalInvocationID.xy");
886 
887         if (m_storage == STORAGE_BUFFER && m_useAtomic)
888             buf << "\tatomicExchange(sb_store1.values[getIndex(" << localID << ", " << ndx << ")], groupNdx);\n";
889         else if (m_storage == STORAGE_BUFFER && !m_useAtomic)
890             buf << "\tsb_store1.values[getIndex(" << localID << ", " << ndx << ")] = groupNdx;\n";
891         else if (m_storage == STORAGE_IMAGE && m_useAtomic)
892             buf << "\timageAtomicExchange(u_image1, getCoord(" << localID << ", " << ndx << "), groupNdx);\n";
893         else if (m_storage == STORAGE_IMAGE && !m_useAtomic)
894             buf << "\timageStore(u_image1, getCoord(" << localID << ", " << ndx << "), ivec4(groupNdx, 0, 0, 0));\n";
895         else
896             DE_ASSERT(false);
897     }
898 
899     // barrier
900 
901     buf << genBarrierSource();
902 
903     // read
904 
905     for (int ndx = 0; ndx < m_elementsPerInvocation; ++ndx)
906     {
907         if (m_storage == STORAGE_BUFFER && m_useAtomic)
908             buf << "\tallOk = allOk && (atomicExchange(sb_store0.values[getIndex(gl_LocalInvocationID.xy, " << ndx
909                 << ")], 123) == groupNdx);\n";
910         else if (m_storage == STORAGE_BUFFER && !m_useAtomic)
911             buf << "\tallOk = allOk && (sb_store0.values[getIndex(gl_LocalInvocationID.xy, " << ndx
912                 << ")] == groupNdx);\n";
913         else if (m_storage == STORAGE_IMAGE && m_useAtomic)
914             buf << "\tallOk = allOk && (imageAtomicExchange(u_image0, getCoord(gl_LocalInvocationID.xy, " << ndx
915                 << "), 123) == groupNdx);\n";
916         else if (m_storage == STORAGE_IMAGE && !m_useAtomic)
917             buf << "\tallOk = allOk && (imageLoad(u_image0, getCoord(gl_LocalInvocationID.xy, " << ndx
918                 << ")).x == groupNdx);\n";
919         else
920             DE_ASSERT(false);
921     }
922 
923     // return result
924 
925     buf << "\n"
926         << "    sb_result.values[resultNdx] = (allOk) ? (1) : (0);\n"
927         << "}\n";
928 
929     return specializeShader(m_context, buf.str().c_str());
930 }
931 
932 namespace op
933 {
934 
935 struct WriteData
936 {
937     int targetHandle;
938     int seed;
939 
Generatedeqp::gles31::Functional::__anon966269800111::op::WriteData940     static WriteData Generate(int targetHandle, int seed)
941     {
942         WriteData retVal;
943 
944         retVal.targetHandle = targetHandle;
945         retVal.seed         = seed;
946 
947         return retVal;
948     }
949 };
950 
951 struct ReadData
952 {
953     int targetHandle;
954     int seed;
955 
Generatedeqp::gles31::Functional::__anon966269800111::op::ReadData956     static ReadData Generate(int targetHandle, int seed)
957     {
958         ReadData retVal;
959 
960         retVal.targetHandle = targetHandle;
961         retVal.seed         = seed;
962 
963         return retVal;
964     }
965 };
966 
967 struct Barrier
968 {
969 };
970 
971 struct WriteDataInterleaved
972 {
973     int targetHandle;
974     int seed;
975     bool evenOdd;
976 
Generatedeqp::gles31::Functional::__anon966269800111::op::WriteDataInterleaved977     static WriteDataInterleaved Generate(int targetHandle, int seed, bool evenOdd)
978     {
979         WriteDataInterleaved retVal;
980 
981         retVal.targetHandle = targetHandle;
982         retVal.seed         = seed;
983         retVal.evenOdd      = evenOdd;
984 
985         return retVal;
986     }
987 };
988 
989 struct ReadDataInterleaved
990 {
991     int targetHandle;
992     int seed0;
993     int seed1;
994 
Generatedeqp::gles31::Functional::__anon966269800111::op::ReadDataInterleaved995     static ReadDataInterleaved Generate(int targetHandle, int seed0, int seed1)
996     {
997         ReadDataInterleaved retVal;
998 
999         retVal.targetHandle = targetHandle;
1000         retVal.seed0        = seed0;
1001         retVal.seed1        = seed1;
1002 
1003         return retVal;
1004     }
1005 };
1006 
1007 struct ReadMultipleData
1008 {
1009     int targetHandle0;
1010     int seed0;
1011     int targetHandle1;
1012     int seed1;
1013 
Generatedeqp::gles31::Functional::__anon966269800111::op::ReadMultipleData1014     static ReadMultipleData Generate(int targetHandle0, int seed0, int targetHandle1, int seed1)
1015     {
1016         ReadMultipleData retVal;
1017 
1018         retVal.targetHandle0 = targetHandle0;
1019         retVal.seed0         = seed0;
1020         retVal.targetHandle1 = targetHandle1;
1021         retVal.seed1         = seed1;
1022 
1023         return retVal;
1024     }
1025 };
1026 
1027 struct ReadZeroData
1028 {
1029     int targetHandle;
1030 
Generatedeqp::gles31::Functional::__anon966269800111::op::ReadZeroData1031     static ReadZeroData Generate(int targetHandle)
1032     {
1033         ReadZeroData retVal;
1034 
1035         retVal.targetHandle = targetHandle;
1036 
1037         return retVal;
1038     }
1039 };
1040 
1041 } // namespace op
1042 
1043 class InterCallTestCase;
1044 
1045 class InterCallOperations
1046 {
1047 public:
1048     InterCallOperations &operator<<(const op::WriteData &);
1049     InterCallOperations &operator<<(const op::ReadData &);
1050     InterCallOperations &operator<<(const op::Barrier &);
1051     InterCallOperations &operator<<(const op::ReadMultipleData &);
1052     InterCallOperations &operator<<(const op::WriteDataInterleaved &);
1053     InterCallOperations &operator<<(const op::ReadDataInterleaved &);
1054     InterCallOperations &operator<<(const op::ReadZeroData &);
1055 
1056 private:
1057     struct Command
1058     {
1059         enum CommandType
1060         {
1061             TYPE_WRITE = 0,
1062             TYPE_READ,
1063             TYPE_BARRIER,
1064             TYPE_READ_MULTIPLE,
1065             TYPE_WRITE_INTERLEAVE,
1066             TYPE_READ_INTERLEAVE,
1067             TYPE_READ_ZERO,
1068 
1069             TYPE_LAST
1070         };
1071 
1072         CommandType type;
1073 
1074         union CommandUnion
1075         {
1076             op::WriteData write;
1077             op::ReadData read;
1078             op::Barrier barrier;
1079             op::ReadMultipleData readMulti;
1080             op::WriteDataInterleaved writeInterleave;
1081             op::ReadDataInterleaved readInterleave;
1082             op::ReadZeroData readZero;
1083         } u_cmd;
1084     };
1085 
1086     friend class InterCallTestCase;
1087 
1088     std::vector<Command> m_cmds;
1089 };
1090 
operator <<(const op::WriteData & cmd)1091 InterCallOperations &InterCallOperations::operator<<(const op::WriteData &cmd)
1092 {
1093     m_cmds.push_back(Command());
1094     m_cmds.back().type        = Command::TYPE_WRITE;
1095     m_cmds.back().u_cmd.write = cmd;
1096 
1097     return *this;
1098 }
1099 
operator <<(const op::ReadData & cmd)1100 InterCallOperations &InterCallOperations::operator<<(const op::ReadData &cmd)
1101 {
1102     m_cmds.push_back(Command());
1103     m_cmds.back().type       = Command::TYPE_READ;
1104     m_cmds.back().u_cmd.read = cmd;
1105 
1106     return *this;
1107 }
1108 
operator <<(const op::Barrier & cmd)1109 InterCallOperations &InterCallOperations::operator<<(const op::Barrier &cmd)
1110 {
1111     m_cmds.push_back(Command());
1112     m_cmds.back().type          = Command::TYPE_BARRIER;
1113     m_cmds.back().u_cmd.barrier = cmd;
1114 
1115     return *this;
1116 }
1117 
operator <<(const op::ReadMultipleData & cmd)1118 InterCallOperations &InterCallOperations::operator<<(const op::ReadMultipleData &cmd)
1119 {
1120     m_cmds.push_back(Command());
1121     m_cmds.back().type            = Command::TYPE_READ_MULTIPLE;
1122     m_cmds.back().u_cmd.readMulti = cmd;
1123 
1124     return *this;
1125 }
1126 
operator <<(const op::WriteDataInterleaved & cmd)1127 InterCallOperations &InterCallOperations::operator<<(const op::WriteDataInterleaved &cmd)
1128 {
1129     m_cmds.push_back(Command());
1130     m_cmds.back().type                  = Command::TYPE_WRITE_INTERLEAVE;
1131     m_cmds.back().u_cmd.writeInterleave = cmd;
1132 
1133     return *this;
1134 }
1135 
operator <<(const op::ReadDataInterleaved & cmd)1136 InterCallOperations &InterCallOperations::operator<<(const op::ReadDataInterleaved &cmd)
1137 {
1138     m_cmds.push_back(Command());
1139     m_cmds.back().type                 = Command::TYPE_READ_INTERLEAVE;
1140     m_cmds.back().u_cmd.readInterleave = cmd;
1141 
1142     return *this;
1143 }
1144 
operator <<(const op::ReadZeroData & cmd)1145 InterCallOperations &InterCallOperations::operator<<(const op::ReadZeroData &cmd)
1146 {
1147     m_cmds.push_back(Command());
1148     m_cmds.back().type           = Command::TYPE_READ_ZERO;
1149     m_cmds.back().u_cmd.readZero = cmd;
1150 
1151     return *this;
1152 }
1153 
1154 class InterCallTestCase : public TestCase
1155 {
1156 public:
1157     enum StorageType
1158     {
1159         STORAGE_BUFFER = 0,
1160         STORAGE_IMAGE,
1161 
1162         STORAGE_LAST
1163     };
1164     enum Flags
1165     {
1166         FLAG_USE_ATOMIC = 1,
1167         FLAG_USE_INT    = 2,
1168     };
1169     InterCallTestCase(Context &context, const char *name, const char *desc, StorageType storage, int flags,
1170                       const InterCallOperations &ops);
1171     ~InterCallTestCase(void);
1172 
1173 private:
1174     void init(void);
1175     void deinit(void);
1176     IterateResult iterate(void);
1177     bool verifyResults(void);
1178 
1179     void runCommand(const op::WriteData &cmd, int stepNdx, int &programFriendlyName);
1180     void runCommand(const op::ReadData &cmd, int stepNdx, int &programFriendlyName, int &resultStorageFriendlyName);
1181     void runCommand(const op::Barrier &);
1182     void runCommand(const op::ReadMultipleData &cmd, int stepNdx, int &programFriendlyName,
1183                     int &resultStorageFriendlyName);
1184     void runCommand(const op::WriteDataInterleaved &cmd, int stepNdx, int &programFriendlyName);
1185     void runCommand(const op::ReadDataInterleaved &cmd, int stepNdx, int &programFriendlyName,
1186                     int &resultStorageFriendlyName);
1187     void runCommand(const op::ReadZeroData &cmd, int stepNdx, int &programFriendlyName, int &resultStorageFriendlyName);
1188     void runSingleRead(int targetHandle, int stepNdx, int &programFriendlyName, int &resultStorageFriendlyName);
1189 
1190     glw::GLuint genStorage(int friendlyName);
1191     glw::GLuint genResultStorage(void);
1192     glu::ShaderProgram *genWriteProgram(int seed);
1193     glu::ShaderProgram *genReadProgram(int seed);
1194     glu::ShaderProgram *genReadMultipleProgram(int seed0, int seed1);
1195     glu::ShaderProgram *genWriteInterleavedProgram(int seed, bool evenOdd);
1196     glu::ShaderProgram *genReadInterleavedProgram(int seed0, int seed1);
1197     glu::ShaderProgram *genReadZeroProgram(void);
1198 
1199     const StorageType m_storage;
1200     const int m_invocationGridSize; // !< width and height of the two dimensional work dispatch
1201     const int m_perInvocationSize;  // !< number of elements accessed in single invocation
1202     const std::vector<InterCallOperations::Command> m_cmds;
1203     const bool m_useAtomic;
1204     const bool m_formatInteger;
1205 
1206     std::vector<glu::ShaderProgram *> m_operationPrograms;
1207     std::vector<glw::GLuint> m_operationResultStorages;
1208     std::map<int, glw::GLuint> m_storageIDs;
1209 };
1210 
InterCallTestCase(Context & context,const char * name,const char * desc,StorageType storage,int flags,const InterCallOperations & ops)1211 InterCallTestCase::InterCallTestCase(Context &context, const char *name, const char *desc, StorageType storage,
1212                                      int flags, const InterCallOperations &ops)
1213     : TestCase(context, name, desc)
1214     , m_storage(storage)
1215     , m_invocationGridSize(512)
1216     , m_perInvocationSize(2)
1217     , m_cmds(ops.m_cmds)
1218     , m_useAtomic((flags & FLAG_USE_ATOMIC) != 0)
1219     , m_formatInteger((flags & FLAG_USE_INT) != 0)
1220 {
1221 }
1222 
~InterCallTestCase(void)1223 InterCallTestCase::~InterCallTestCase(void)
1224 {
1225     deinit();
1226 }
1227 
init(void)1228 void InterCallTestCase::init(void)
1229 {
1230     int programFriendlyName = 0;
1231 
1232     // requirements
1233 
1234     if (m_useAtomic && m_storage == STORAGE_IMAGE && !checkSupport(m_context))
1235         throw tcu::NotSupportedError("Test requires GL_OES_shader_image_atomic extension");
1236 
1237     // generate resources and validate command list
1238 
1239     m_operationPrograms.resize(m_cmds.size(), DE_NULL);
1240     m_operationResultStorages.resize(m_cmds.size(), 0);
1241 
1242     for (int step = 0; step < (int)m_cmds.size(); ++step)
1243     {
1244         switch (m_cmds[step].type)
1245         {
1246         case InterCallOperations::Command::TYPE_WRITE:
1247         {
1248             const op::WriteData &cmd = m_cmds[step].u_cmd.write;
1249 
1250             // new storage handle?
1251             if (m_storageIDs.find(cmd.targetHandle) == m_storageIDs.end())
1252                 m_storageIDs[cmd.targetHandle] = genStorage(cmd.targetHandle);
1253 
1254             // program
1255             {
1256                 glu::ShaderProgram *program = genWriteProgram(cmd.seed);
1257 
1258                 m_testCtx.getLog() << tcu::TestLog::Message << "Program #" << ++programFriendlyName
1259                                    << tcu::TestLog::EndMessage;
1260                 m_testCtx.getLog() << *program;
1261 
1262                 if (!program->isOk())
1263                     throw tcu::TestError("could not build program");
1264 
1265                 m_operationPrograms[step] = program;
1266             }
1267             break;
1268         }
1269 
1270         case InterCallOperations::Command::TYPE_READ:
1271         {
1272             const op::ReadData &cmd = m_cmds[step].u_cmd.read;
1273             DE_ASSERT(m_storageIDs.find(cmd.targetHandle) != m_storageIDs.end());
1274 
1275             // program and result storage
1276             {
1277                 glu::ShaderProgram *program = genReadProgram(cmd.seed);
1278 
1279                 m_testCtx.getLog() << tcu::TestLog::Message << "Program #" << ++programFriendlyName
1280                                    << tcu::TestLog::EndMessage;
1281                 m_testCtx.getLog() << *program;
1282 
1283                 if (!program->isOk())
1284                     throw tcu::TestError("could not build program");
1285 
1286                 m_operationPrograms[step]       = program;
1287                 m_operationResultStorages[step] = genResultStorage();
1288             }
1289             break;
1290         }
1291 
1292         case InterCallOperations::Command::TYPE_BARRIER:
1293         {
1294             break;
1295         }
1296 
1297         case InterCallOperations::Command::TYPE_READ_MULTIPLE:
1298         {
1299             const op::ReadMultipleData &cmd = m_cmds[step].u_cmd.readMulti;
1300             DE_ASSERT(m_storageIDs.find(cmd.targetHandle0) != m_storageIDs.end());
1301             DE_ASSERT(m_storageIDs.find(cmd.targetHandle1) != m_storageIDs.end());
1302 
1303             // program
1304             {
1305                 glu::ShaderProgram *program = genReadMultipleProgram(cmd.seed0, cmd.seed1);
1306 
1307                 m_testCtx.getLog() << tcu::TestLog::Message << "Program #" << ++programFriendlyName
1308                                    << tcu::TestLog::EndMessage;
1309                 m_testCtx.getLog() << *program;
1310 
1311                 if (!program->isOk())
1312                     throw tcu::TestError("could not build program");
1313 
1314                 m_operationPrograms[step]       = program;
1315                 m_operationResultStorages[step] = genResultStorage();
1316             }
1317             break;
1318         }
1319 
1320         case InterCallOperations::Command::TYPE_WRITE_INTERLEAVE:
1321         {
1322             const op::WriteDataInterleaved &cmd = m_cmds[step].u_cmd.writeInterleave;
1323 
1324             // new storage handle?
1325             if (m_storageIDs.find(cmd.targetHandle) == m_storageIDs.end())
1326                 m_storageIDs[cmd.targetHandle] = genStorage(cmd.targetHandle);
1327 
1328             // program
1329             {
1330                 glu::ShaderProgram *program = genWriteInterleavedProgram(cmd.seed, cmd.evenOdd);
1331 
1332                 m_testCtx.getLog() << tcu::TestLog::Message << "Program #" << ++programFriendlyName
1333                                    << tcu::TestLog::EndMessage;
1334                 m_testCtx.getLog() << *program;
1335 
1336                 if (!program->isOk())
1337                     throw tcu::TestError("could not build program");
1338 
1339                 m_operationPrograms[step] = program;
1340             }
1341             break;
1342         }
1343 
1344         case InterCallOperations::Command::TYPE_READ_INTERLEAVE:
1345         {
1346             const op::ReadDataInterleaved &cmd = m_cmds[step].u_cmd.readInterleave;
1347             DE_ASSERT(m_storageIDs.find(cmd.targetHandle) != m_storageIDs.end());
1348 
1349             // program
1350             {
1351                 glu::ShaderProgram *program = genReadInterleavedProgram(cmd.seed0, cmd.seed1);
1352 
1353                 m_testCtx.getLog() << tcu::TestLog::Message << "Program #" << ++programFriendlyName
1354                                    << tcu::TestLog::EndMessage;
1355                 m_testCtx.getLog() << *program;
1356 
1357                 if (!program->isOk())
1358                     throw tcu::TestError("could not build program");
1359 
1360                 m_operationPrograms[step]       = program;
1361                 m_operationResultStorages[step] = genResultStorage();
1362             }
1363             break;
1364         }
1365 
1366         case InterCallOperations::Command::TYPE_READ_ZERO:
1367         {
1368             const op::ReadZeroData &cmd = m_cmds[step].u_cmd.readZero;
1369 
1370             // new storage handle?
1371             if (m_storageIDs.find(cmd.targetHandle) == m_storageIDs.end())
1372                 m_storageIDs[cmd.targetHandle] = genStorage(cmd.targetHandle);
1373 
1374             // program
1375             {
1376                 glu::ShaderProgram *program = genReadZeroProgram();
1377 
1378                 m_testCtx.getLog() << tcu::TestLog::Message << "Program #" << ++programFriendlyName
1379                                    << tcu::TestLog::EndMessage;
1380                 m_testCtx.getLog() << *program;
1381 
1382                 if (!program->isOk())
1383                     throw tcu::TestError("could not build program");
1384 
1385                 m_operationPrograms[step]       = program;
1386                 m_operationResultStorages[step] = genResultStorage();
1387             }
1388             break;
1389         }
1390 
1391         default:
1392             DE_ASSERT(false);
1393         }
1394     }
1395 }
1396 
deinit(void)1397 void InterCallTestCase::deinit(void)
1398 {
1399     // programs
1400     for (int ndx = 0; ndx < (int)m_operationPrograms.size(); ++ndx)
1401         delete m_operationPrograms[ndx];
1402     m_operationPrograms.clear();
1403 
1404     // result storages
1405     for (int ndx = 0; ndx < (int)m_operationResultStorages.size(); ++ndx)
1406     {
1407         if (m_operationResultStorages[ndx])
1408             m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_operationResultStorages[ndx]);
1409     }
1410     m_operationResultStorages.clear();
1411 
1412     // storage
1413     for (std::map<int, glw::GLuint>::const_iterator it = m_storageIDs.begin(); it != m_storageIDs.end(); ++it)
1414     {
1415         const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1416 
1417         if (m_storage == STORAGE_BUFFER)
1418             gl.deleteBuffers(1, &it->second);
1419         else if (m_storage == STORAGE_IMAGE)
1420             gl.deleteTextures(1, &it->second);
1421         else
1422             DE_ASSERT(false);
1423     }
1424     m_storageIDs.clear();
1425 }
1426 
iterate(void)1427 InterCallTestCase::IterateResult InterCallTestCase::iterate(void)
1428 {
1429     int programFriendlyName       = 0;
1430     int resultStorageFriendlyName = 0;
1431 
1432     m_testCtx.getLog() << tcu::TestLog::Message << "Running operations:" << tcu::TestLog::EndMessage;
1433 
1434     // run steps
1435 
1436     for (int step = 0; step < (int)m_cmds.size(); ++step)
1437     {
1438         switch (m_cmds[step].type)
1439         {
1440         case InterCallOperations::Command::TYPE_WRITE:
1441             runCommand(m_cmds[step].u_cmd.write, step, programFriendlyName);
1442             break;
1443         case InterCallOperations::Command::TYPE_READ:
1444             runCommand(m_cmds[step].u_cmd.read, step, programFriendlyName, resultStorageFriendlyName);
1445             break;
1446         case InterCallOperations::Command::TYPE_BARRIER:
1447             runCommand(m_cmds[step].u_cmd.barrier);
1448             break;
1449         case InterCallOperations::Command::TYPE_READ_MULTIPLE:
1450             runCommand(m_cmds[step].u_cmd.readMulti, step, programFriendlyName, resultStorageFriendlyName);
1451             break;
1452         case InterCallOperations::Command::TYPE_WRITE_INTERLEAVE:
1453             runCommand(m_cmds[step].u_cmd.writeInterleave, step, programFriendlyName);
1454             break;
1455         case InterCallOperations::Command::TYPE_READ_INTERLEAVE:
1456             runCommand(m_cmds[step].u_cmd.readInterleave, step, programFriendlyName, resultStorageFriendlyName);
1457             break;
1458         case InterCallOperations::Command::TYPE_READ_ZERO:
1459             runCommand(m_cmds[step].u_cmd.readZero, step, programFriendlyName, resultStorageFriendlyName);
1460             break;
1461         default:
1462             DE_ASSERT(false);
1463         }
1464     }
1465 
1466     // read results from result buffers
1467     if (verifyResults())
1468         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1469     else
1470         m_testCtx.setTestResult(
1471             QP_TEST_RESULT_FAIL,
1472             (std::string((m_storage == STORAGE_BUFFER) ? ("buffer") : ("image")) + " content verification failed")
1473                 .c_str());
1474 
1475     return STOP;
1476 }
1477 
verifyResults(void)1478 bool InterCallTestCase::verifyResults(void)
1479 {
1480     int resultBufferFriendlyName = 0;
1481     bool allResultsOk            = true;
1482     bool anyResult               = false;
1483 
1484     m_testCtx.getLog() << tcu::TestLog::Message << "Reading verifier program results" << tcu::TestLog::EndMessage;
1485 
1486     for (int step = 0; step < (int)m_cmds.size(); ++step)
1487     {
1488         const int errorFloodThreshold = 5;
1489         int numErrorsLogged           = 0;
1490 
1491         if (m_operationResultStorages[step])
1492         {
1493             const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1494             const void *mapped       = DE_NULL;
1495             std::vector<int32_t> results(m_invocationGridSize * m_invocationGridSize);
1496             bool error = false;
1497 
1498             anyResult = true;
1499 
1500             gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_operationResultStorages[step]);
1501             mapped = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0,
1502                                        m_invocationGridSize * m_invocationGridSize * sizeof(uint32_t), GL_MAP_READ_BIT);
1503             GLU_EXPECT_NO_ERROR(gl.getError(), "map buffer");
1504 
1505             // copy to properly aligned array
1506             deMemcpy(&results[0], mapped, m_invocationGridSize * m_invocationGridSize * sizeof(uint32_t));
1507 
1508             if (gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER) != GL_TRUE)
1509                 throw tcu::TestError("memory map store corrupted");
1510 
1511             // check the results
1512             for (int ndx = 0; ndx < (int)results.size(); ++ndx)
1513             {
1514                 if (results[ndx] != 1)
1515                 {
1516                     error = true;
1517 
1518                     if (numErrorsLogged == 0)
1519                         m_testCtx.getLog() << tcu::TestLog::Message << "Result storage #" << ++resultBufferFriendlyName
1520                                            << " failed, got unexpected values.\n"
1521                                            << tcu::TestLog::EndMessage;
1522                     if (numErrorsLogged++ < errorFloodThreshold)
1523                         m_testCtx.getLog() << tcu::TestLog::Message << "    Error at index " << ndx
1524                                            << ": expected 1, got " << results[ndx] << ".\n"
1525                                            << tcu::TestLog::EndMessage;
1526                     else
1527                     {
1528                         // after N errors, no point continuing verification
1529                         m_testCtx.getLog()
1530                             << tcu::TestLog::Message << "    -- too many errors, skipping verification --\n"
1531                             << tcu::TestLog::EndMessage;
1532                         break;
1533                     }
1534                 }
1535             }
1536 
1537             if (error)
1538             {
1539                 allResultsOk = false;
1540             }
1541             else
1542                 m_testCtx.getLog() << tcu::TestLog::Message << "Result storage #" << ++resultBufferFriendlyName
1543                                    << " ok." << tcu::TestLog::EndMessage;
1544         }
1545     }
1546 
1547     DE_ASSERT(anyResult);
1548     DE_UNREF(anyResult);
1549 
1550     return allResultsOk;
1551 }
1552 
runCommand(const op::WriteData & cmd,int stepNdx,int & programFriendlyName)1553 void InterCallTestCase::runCommand(const op::WriteData &cmd, int stepNdx, int &programFriendlyName)
1554 {
1555     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1556 
1557     m_testCtx.getLog() << tcu::TestLog::Message << "Running program #" << ++programFriendlyName << " to write "
1558                        << ((m_storage == STORAGE_BUFFER) ? ("buffer") : ("image")) << " #" << cmd.targetHandle << ".\n"
1559                        << "    Dispatch size: " << m_invocationGridSize << "x" << m_invocationGridSize << "."
1560                        << tcu::TestLog::EndMessage;
1561 
1562     gl.useProgram(m_operationPrograms[stepNdx]->getProgram());
1563 
1564     // set destination
1565     if (m_storage == STORAGE_BUFFER)
1566     {
1567         DE_ASSERT(m_storageIDs[cmd.targetHandle]);
1568 
1569         gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_storageIDs[cmd.targetHandle]);
1570         GLU_EXPECT_NO_ERROR(gl.getError(), "bind destination buffer");
1571     }
1572     else if (m_storage == STORAGE_IMAGE)
1573     {
1574         DE_ASSERT(m_storageIDs[cmd.targetHandle]);
1575 
1576         gl.bindImageTexture(0, m_storageIDs[cmd.targetHandle], 0, GL_FALSE, 0,
1577                             (m_useAtomic) ? (GL_READ_WRITE) : (GL_WRITE_ONLY),
1578                             (m_formatInteger) ? (GL_R32I) : (GL_R32F));
1579         GLU_EXPECT_NO_ERROR(gl.getError(), "bind destination image");
1580     }
1581     else
1582         DE_ASSERT(false);
1583 
1584     // calc
1585     gl.dispatchCompute(m_invocationGridSize, m_invocationGridSize, 1);
1586     GLU_EXPECT_NO_ERROR(gl.getError(), "dispatch write");
1587 }
1588 
runCommand(const op::ReadData & cmd,int stepNdx,int & programFriendlyName,int & resultStorageFriendlyName)1589 void InterCallTestCase::runCommand(const op::ReadData &cmd, int stepNdx, int &programFriendlyName,
1590                                    int &resultStorageFriendlyName)
1591 {
1592     runSingleRead(cmd.targetHandle, stepNdx, programFriendlyName, resultStorageFriendlyName);
1593 }
1594 
runCommand(const op::Barrier & cmd)1595 void InterCallTestCase::runCommand(const op::Barrier &cmd)
1596 {
1597     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1598 
1599     DE_UNREF(cmd);
1600 
1601     if (m_storage == STORAGE_BUFFER)
1602     {
1603         m_testCtx.getLog() << tcu::TestLog::Message << "Memory Barrier\n\tbits = GL_SHADER_STORAGE_BARRIER_BIT"
1604                            << tcu::TestLog::EndMessage;
1605         gl.memoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
1606     }
1607     else if (m_storage == STORAGE_IMAGE)
1608     {
1609         m_testCtx.getLog() << tcu::TestLog::Message << "Memory Barrier\n\tbits = GL_SHADER_IMAGE_ACCESS_BARRIER_BIT"
1610                            << tcu::TestLog::EndMessage;
1611         gl.memoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
1612     }
1613     else
1614         DE_ASSERT(false);
1615 }
1616 
runCommand(const op::ReadMultipleData & cmd,int stepNdx,int & programFriendlyName,int & resultStorageFriendlyName)1617 void InterCallTestCase::runCommand(const op::ReadMultipleData &cmd, int stepNdx, int &programFriendlyName,
1618                                    int &resultStorageFriendlyName)
1619 {
1620     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1621 
1622     m_testCtx.getLog() << tcu::TestLog::Message << "Running program #" << ++programFriendlyName << " to verify "
1623                        << ((m_storage == STORAGE_BUFFER) ? ("buffers") : ("images")) << " #" << cmd.targetHandle0
1624                        << " and #" << cmd.targetHandle1 << ".\n"
1625                        << "    Writing results to result storage #" << ++resultStorageFriendlyName << ".\n"
1626                        << "    Dispatch size: " << m_invocationGridSize << "x" << m_invocationGridSize << "."
1627                        << tcu::TestLog::EndMessage;
1628 
1629     gl.useProgram(m_operationPrograms[stepNdx]->getProgram());
1630 
1631     // set sources
1632     if (m_storage == STORAGE_BUFFER)
1633     {
1634         DE_ASSERT(m_storageIDs[cmd.targetHandle0]);
1635         DE_ASSERT(m_storageIDs[cmd.targetHandle1]);
1636 
1637         gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_storageIDs[cmd.targetHandle0]);
1638         gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, m_storageIDs[cmd.targetHandle1]);
1639         GLU_EXPECT_NO_ERROR(gl.getError(), "bind source buffers");
1640     }
1641     else if (m_storage == STORAGE_IMAGE)
1642     {
1643         DE_ASSERT(m_storageIDs[cmd.targetHandle0]);
1644         DE_ASSERT(m_storageIDs[cmd.targetHandle1]);
1645 
1646         gl.bindImageTexture(1, m_storageIDs[cmd.targetHandle0], 0, GL_FALSE, 0,
1647                             (m_useAtomic) ? (GL_READ_WRITE) : (GL_READ_ONLY),
1648                             (m_formatInteger) ? (GL_R32I) : (GL_R32F));
1649         gl.bindImageTexture(2, m_storageIDs[cmd.targetHandle1], 0, GL_FALSE, 0,
1650                             (m_useAtomic) ? (GL_READ_WRITE) : (GL_READ_ONLY),
1651                             (m_formatInteger) ? (GL_R32I) : (GL_R32F));
1652         GLU_EXPECT_NO_ERROR(gl.getError(), "bind source images");
1653     }
1654     else
1655         DE_ASSERT(false);
1656 
1657     // set destination
1658     DE_ASSERT(m_operationResultStorages[stepNdx]);
1659     gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_operationResultStorages[stepNdx]);
1660     GLU_EXPECT_NO_ERROR(gl.getError(), "bind result buffer");
1661 
1662     // calc
1663     gl.dispatchCompute(m_invocationGridSize, m_invocationGridSize, 1);
1664     GLU_EXPECT_NO_ERROR(gl.getError(), "dispatch read multi");
1665 }
1666 
runCommand(const op::WriteDataInterleaved & cmd,int stepNdx,int & programFriendlyName)1667 void InterCallTestCase::runCommand(const op::WriteDataInterleaved &cmd, int stepNdx, int &programFriendlyName)
1668 {
1669     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1670 
1671     m_testCtx.getLog() << tcu::TestLog::Message << "Running program #" << ++programFriendlyName << " to write "
1672                        << ((m_storage == STORAGE_BUFFER) ? ("buffer") : ("image")) << " #" << cmd.targetHandle << ".\n"
1673                        << "    Writing to every " << ((cmd.evenOdd) ? ("even") : ("odd")) << " "
1674                        << ((m_storage == STORAGE_BUFFER) ? ("element") : ("column")) << ".\n"
1675                        << "    Dispatch size: " << m_invocationGridSize / 2 << "x" << m_invocationGridSize << "."
1676                        << tcu::TestLog::EndMessage;
1677 
1678     gl.useProgram(m_operationPrograms[stepNdx]->getProgram());
1679 
1680     // set destination
1681     if (m_storage == STORAGE_BUFFER)
1682     {
1683         DE_ASSERT(m_storageIDs[cmd.targetHandle]);
1684 
1685         gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_storageIDs[cmd.targetHandle]);
1686         GLU_EXPECT_NO_ERROR(gl.getError(), "bind destination buffer");
1687     }
1688     else if (m_storage == STORAGE_IMAGE)
1689     {
1690         DE_ASSERT(m_storageIDs[cmd.targetHandle]);
1691 
1692         gl.bindImageTexture(0, m_storageIDs[cmd.targetHandle], 0, GL_FALSE, 0,
1693                             (m_useAtomic) ? (GL_READ_WRITE) : (GL_WRITE_ONLY),
1694                             (m_formatInteger) ? (GL_R32I) : (GL_R32F));
1695         GLU_EXPECT_NO_ERROR(gl.getError(), "bind destination image");
1696     }
1697     else
1698         DE_ASSERT(false);
1699 
1700     // calc
1701     gl.dispatchCompute(m_invocationGridSize / 2, m_invocationGridSize, 1);
1702     GLU_EXPECT_NO_ERROR(gl.getError(), "dispatch write");
1703 }
1704 
runCommand(const op::ReadDataInterleaved & cmd,int stepNdx,int & programFriendlyName,int & resultStorageFriendlyName)1705 void InterCallTestCase::runCommand(const op::ReadDataInterleaved &cmd, int stepNdx, int &programFriendlyName,
1706                                    int &resultStorageFriendlyName)
1707 {
1708     runSingleRead(cmd.targetHandle, stepNdx, programFriendlyName, resultStorageFriendlyName);
1709 }
1710 
runCommand(const op::ReadZeroData & cmd,int stepNdx,int & programFriendlyName,int & resultStorageFriendlyName)1711 void InterCallTestCase::runCommand(const op::ReadZeroData &cmd, int stepNdx, int &programFriendlyName,
1712                                    int &resultStorageFriendlyName)
1713 {
1714     runSingleRead(cmd.targetHandle, stepNdx, programFriendlyName, resultStorageFriendlyName);
1715 }
1716 
runSingleRead(int targetHandle,int stepNdx,int & programFriendlyName,int & resultStorageFriendlyName)1717 void InterCallTestCase::runSingleRead(int targetHandle, int stepNdx, int &programFriendlyName,
1718                                       int &resultStorageFriendlyName)
1719 {
1720     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1721 
1722     m_testCtx.getLog() << tcu::TestLog::Message << "Running program #" << ++programFriendlyName << " to verify "
1723                        << ((m_storage == STORAGE_BUFFER) ? ("buffer") : ("image")) << " #" << targetHandle << ".\n"
1724                        << "    Writing results to result storage #" << ++resultStorageFriendlyName << ".\n"
1725                        << "    Dispatch size: " << m_invocationGridSize << "x" << m_invocationGridSize << "."
1726                        << tcu::TestLog::EndMessage;
1727 
1728     gl.useProgram(m_operationPrograms[stepNdx]->getProgram());
1729 
1730     // set source
1731     if (m_storage == STORAGE_BUFFER)
1732     {
1733         DE_ASSERT(m_storageIDs[targetHandle]);
1734 
1735         gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_storageIDs[targetHandle]);
1736         GLU_EXPECT_NO_ERROR(gl.getError(), "bind source buffer");
1737     }
1738     else if (m_storage == STORAGE_IMAGE)
1739     {
1740         DE_ASSERT(m_storageIDs[targetHandle]);
1741 
1742         gl.bindImageTexture(1, m_storageIDs[targetHandle], 0, GL_FALSE, 0,
1743                             (m_useAtomic) ? (GL_READ_WRITE) : (GL_READ_ONLY),
1744                             (m_formatInteger) ? (GL_R32I) : (GL_R32F));
1745         GLU_EXPECT_NO_ERROR(gl.getError(), "bind source image");
1746     }
1747     else
1748         DE_ASSERT(false);
1749 
1750     // set destination
1751     DE_ASSERT(m_operationResultStorages[stepNdx]);
1752     gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_operationResultStorages[stepNdx]);
1753     GLU_EXPECT_NO_ERROR(gl.getError(), "bind result buffer");
1754 
1755     // calc
1756     gl.dispatchCompute(m_invocationGridSize, m_invocationGridSize, 1);
1757     GLU_EXPECT_NO_ERROR(gl.getError(), "dispatch read");
1758 }
1759 
genStorage(int friendlyName)1760 glw::GLuint InterCallTestCase::genStorage(int friendlyName)
1761 {
1762     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1763 
1764     if (m_storage == STORAGE_BUFFER)
1765     {
1766         const int numElements = m_invocationGridSize * m_invocationGridSize * m_perInvocationSize;
1767         const int bufferSize  = numElements * (int)((m_formatInteger) ? (sizeof(int32_t)) : (sizeof(glw::GLfloat)));
1768         glw::GLuint retVal    = 0;
1769 
1770         m_testCtx.getLog() << tcu::TestLog::Message << "Creating buffer #" << friendlyName << ", size " << bufferSize
1771                            << " bytes." << tcu::TestLog::EndMessage;
1772 
1773         gl.genBuffers(1, &retVal);
1774         gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, retVal);
1775 
1776         if (m_formatInteger)
1777         {
1778             const std::vector<uint32_t> zeroBuffer(numElements, 0);
1779             gl.bufferData(GL_SHADER_STORAGE_BUFFER, bufferSize, &zeroBuffer[0], GL_STATIC_DRAW);
1780         }
1781         else
1782         {
1783             const std::vector<float> zeroBuffer(numElements, 0.0f);
1784             gl.bufferData(GL_SHADER_STORAGE_BUFFER, bufferSize, &zeroBuffer[0], GL_STATIC_DRAW);
1785         }
1786         GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffer");
1787 
1788         return retVal;
1789     }
1790     else if (m_storage == STORAGE_IMAGE)
1791     {
1792         const int imageWidth  = m_invocationGridSize;
1793         const int imageHeight = m_invocationGridSize * m_perInvocationSize;
1794         glw::GLuint retVal    = 0;
1795 
1796         m_testCtx.getLog() << tcu::TestLog::Message << "Creating image #" << friendlyName << ", size " << imageWidth
1797                            << "x" << imageHeight << ", internalformat = " << ((m_formatInteger) ? ("r32i") : ("r32f"))
1798                            << ", size = " << (imageWidth * imageHeight * sizeof(uint32_t)) << " bytes."
1799                            << tcu::TestLog::EndMessage;
1800 
1801         gl.genTextures(1, &retVal);
1802         gl.bindTexture(GL_TEXTURE_2D, retVal);
1803 
1804         if (m_formatInteger)
1805             gl.texStorage2D(GL_TEXTURE_2D, 1, GL_R32I, imageWidth, imageHeight);
1806         else
1807             gl.texStorage2D(GL_TEXTURE_2D, 1, GL_R32F, imageWidth, imageHeight);
1808 
1809         gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1810         gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1811         GLU_EXPECT_NO_ERROR(gl.getError(), "gen image");
1812 
1813         m_testCtx.getLog() << tcu::TestLog::Message << "Filling image with 0" << tcu::TestLog::EndMessage;
1814 
1815         if (m_formatInteger)
1816         {
1817             const std::vector<int32_t> zeroBuffer(imageWidth * imageHeight, 0);
1818             gl.texSubImage2D(GL_TEXTURE_2D, 0, 0, 0, imageWidth, imageHeight, GL_RED_INTEGER, GL_INT, &zeroBuffer[0]);
1819         }
1820         else
1821         {
1822             const std::vector<float> zeroBuffer(imageWidth * imageHeight, 0.0f);
1823             gl.texSubImage2D(GL_TEXTURE_2D, 0, 0, 0, imageWidth, imageHeight, GL_RED, GL_FLOAT, &zeroBuffer[0]);
1824         }
1825 
1826         GLU_EXPECT_NO_ERROR(gl.getError(), "specify image contents");
1827 
1828         return retVal;
1829     }
1830     else
1831     {
1832         DE_ASSERT(false);
1833         return 0;
1834     }
1835 }
1836 
genResultStorage(void)1837 glw::GLuint InterCallTestCase::genResultStorage(void)
1838 {
1839     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1840     glw::GLuint retVal       = 0;
1841 
1842     gl.genBuffers(1, &retVal);
1843     gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, retVal);
1844     gl.bufferData(GL_SHADER_STORAGE_BUFFER, m_invocationGridSize * m_invocationGridSize * sizeof(uint32_t), DE_NULL,
1845                   GL_STATIC_DRAW);
1846     GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffer");
1847 
1848     return retVal;
1849 }
1850 
genWriteProgram(int seed)1851 glu::ShaderProgram *InterCallTestCase::genWriteProgram(int seed)
1852 {
1853     const bool useImageAtomics = m_useAtomic && m_storage == STORAGE_IMAGE;
1854     std::ostringstream buf;
1855 
1856     buf << "${GLSL_VERSION_DECL}\n"
1857         << ((useImageAtomics) ? ("${SHADER_IMAGE_ATOMIC_REQUIRE}\n") : (""))
1858         << "layout (local_size_x = 1, local_size_y = 1) in;\n";
1859 
1860     if (m_storage == STORAGE_BUFFER)
1861         buf << "layout(binding=0, std430) " << ((m_useAtomic) ? ("coherent ") : ("")) << "buffer Buffer\n"
1862             << "{\n"
1863             << "    highp " << ((m_formatInteger) ? ("int") : ("float")) << " values[];\n"
1864             << "} sb_out;\n";
1865     else if (m_storage == STORAGE_IMAGE)
1866         buf << "layout(" << ((m_formatInteger) ? ("r32i") : ("r32f")) << ", binding=0) "
1867             << ((m_useAtomic) ? ("coherent ") : ("writeonly ")) << "uniform highp "
1868             << ((m_formatInteger) ? ("iimage2D") : ("image2D")) << " u_imageOut;\n";
1869     else
1870         DE_ASSERT(false);
1871 
1872     buf << "\n"
1873         << "void main (void)\n"
1874         << "{\n"
1875         << "    uvec3 size    = gl_NumWorkGroups * gl_WorkGroupSize;\n"
1876         << "    int groupNdx  = int(size.x * size.y * gl_GlobalInvocationID.z + size.x*gl_GlobalInvocationID.y + "
1877            "gl_GlobalInvocationID.x);\n"
1878         << "\n";
1879 
1880     // Write to buffer/image m_perInvocationSize elements
1881     if (m_storage == STORAGE_BUFFER)
1882     {
1883         for (int writeNdx = 0; writeNdx < m_perInvocationSize; ++writeNdx)
1884         {
1885             if (m_useAtomic)
1886                 buf << "    atomicExchange(";
1887             else
1888                 buf << "    ";
1889 
1890             buf << "sb_out.values[(groupNdx + " << seed + writeNdx * m_invocationGridSize * m_invocationGridSize
1891                 << ") % " << m_invocationGridSize * m_invocationGridSize * m_perInvocationSize << "]";
1892 
1893             if (m_useAtomic)
1894                 buf << ", " << ((m_formatInteger) ? ("int") : ("float")) << "(groupNdx));\n";
1895             else
1896                 buf << " = " << ((m_formatInteger) ? ("int") : ("float")) << "(groupNdx);\n";
1897         }
1898     }
1899     else if (m_storage == STORAGE_IMAGE)
1900     {
1901         for (int writeNdx = 0; writeNdx < m_perInvocationSize; ++writeNdx)
1902         {
1903             if (m_useAtomic)
1904                 buf << "    imageAtomicExchange";
1905             else
1906                 buf << "    imageStore";
1907 
1908             buf << "(u_imageOut, ivec2((int(gl_GlobalInvocationID.x) + " << (seed + writeNdx * 100) << ") % "
1909                 << m_invocationGridSize << ", int(gl_GlobalInvocationID.y) + " << writeNdx * m_invocationGridSize
1910                 << "), ";
1911 
1912             if (m_useAtomic)
1913                 buf << ((m_formatInteger) ? ("int") : ("float")) << "(groupNdx));\n";
1914             else
1915                 buf << ((m_formatInteger) ? ("ivec4(int(groupNdx), 0, 0, 0)") :
1916                                             ("vec4(float(groupNdx), 0.0, 0.0, 0.0)"))
1917                     << ");\n";
1918         }
1919     }
1920     else
1921         DE_ASSERT(false);
1922 
1923     buf << "}\n";
1924 
1925     return new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(
1926                                                                     specializeShader(m_context, buf.str().c_str())));
1927 }
1928 
genReadProgram(int seed)1929 glu::ShaderProgram *InterCallTestCase::genReadProgram(int seed)
1930 {
1931     const bool useImageAtomics = m_useAtomic && m_storage == STORAGE_IMAGE;
1932     std::ostringstream buf;
1933 
1934     buf << "${GLSL_VERSION_DECL}\n"
1935         << ((useImageAtomics) ? ("${SHADER_IMAGE_ATOMIC_REQUIRE}\n") : (""))
1936         << "layout (local_size_x = 1, local_size_y = 1) in;\n";
1937 
1938     if (m_storage == STORAGE_BUFFER)
1939         buf << "layout(binding=1, std430) " << ((m_useAtomic) ? ("coherent ") : ("")) << "buffer Buffer\n"
1940             << "{\n"
1941             << "    highp " << ((m_formatInteger) ? ("int") : ("float")) << " values[];\n"
1942             << "} sb_in;\n";
1943     else if (m_storage == STORAGE_IMAGE)
1944         buf << "layout(" << ((m_formatInteger) ? ("r32i") : ("r32f")) << ", binding=1) "
1945             << ((m_useAtomic) ? ("coherent ") : ("readonly ")) << "uniform highp "
1946             << ((m_formatInteger) ? ("iimage2D") : ("image2D")) << " u_imageIn;\n";
1947     else
1948         DE_ASSERT(false);
1949 
1950     buf << "layout(binding=0, std430) buffer ResultBuffer\n"
1951         << "{\n"
1952         << "    highp int resultOk[];\n"
1953         << "} sb_result;\n"
1954         << "\n"
1955         << "void main (void)\n"
1956         << "{\n"
1957         << "    uvec3 size = gl_NumWorkGroups * gl_WorkGroupSize;\n"
1958         << "    int groupNdx = int(size.x * size.y * gl_GlobalInvocationID.z + size.x*gl_GlobalInvocationID.y + "
1959            "gl_GlobalInvocationID.x);\n"
1960         << "    " << ((m_formatInteger) ? ("int") : ("float")) << " zero = " << ((m_formatInteger) ? ("0") : ("0.0"))
1961         << ";\n"
1962         << "    bool allOk = true;\n"
1963         << "\n";
1964 
1965     // Verify data
1966 
1967     if (m_storage == STORAGE_BUFFER)
1968     {
1969         for (int readNdx = 0; readNdx < m_perInvocationSize; ++readNdx)
1970         {
1971             if (!m_useAtomic)
1972                 buf << "    allOk = allOk && (sb_in.values[(groupNdx + "
1973                     << seed + readNdx * m_invocationGridSize * m_invocationGridSize << ") % "
1974                     << m_invocationGridSize * m_invocationGridSize * m_perInvocationSize
1975                     << "] == " << ((m_formatInteger) ? ("int") : ("float")) << "(groupNdx));\n";
1976             else
1977                 buf << "    allOk = allOk && (atomicExchange(sb_in.values[(groupNdx + "
1978                     << seed + readNdx * m_invocationGridSize * m_invocationGridSize << ") % "
1979                     << m_invocationGridSize * m_invocationGridSize * m_perInvocationSize
1980                     << "], zero) == " << ((m_formatInteger) ? ("int") : ("float")) << "(groupNdx));\n";
1981         }
1982     }
1983     else if (m_storage == STORAGE_IMAGE)
1984     {
1985         for (int readNdx = 0; readNdx < m_perInvocationSize; ++readNdx)
1986         {
1987             if (!m_useAtomic)
1988                 buf << "    allOk = allOk && (imageLoad(u_imageIn, ivec2((gl_GlobalInvocationID.x + "
1989                     << (seed + readNdx * 100) << "u) % " << m_invocationGridSize << "u, gl_GlobalInvocationID.y + "
1990                     << readNdx * m_invocationGridSize << "u)).x == " << ((m_formatInteger) ? ("int") : ("float"))
1991                     << "(groupNdx));\n";
1992             else
1993                 buf << "    allOk = allOk && (imageAtomicExchange(u_imageIn, ivec2((gl_GlobalInvocationID.x + "
1994                     << (seed + readNdx * 100) << "u) % " << m_invocationGridSize << "u, gl_GlobalInvocationID.y + "
1995                     << readNdx * m_invocationGridSize << "u), zero) == " << ((m_formatInteger) ? ("int") : ("float"))
1996                     << "(groupNdx));\n";
1997         }
1998     }
1999     else
2000         DE_ASSERT(false);
2001 
2002     buf << "    sb_result.resultOk[groupNdx] = (allOk) ? (1) : (0);\n"
2003         << "}\n";
2004 
2005     return new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(
2006                                                                     specializeShader(m_context, buf.str().c_str())));
2007 }
2008 
genReadMultipleProgram(int seed0,int seed1)2009 glu::ShaderProgram *InterCallTestCase::genReadMultipleProgram(int seed0, int seed1)
2010 {
2011     const bool useImageAtomics = m_useAtomic && m_storage == STORAGE_IMAGE;
2012     std::ostringstream buf;
2013 
2014     buf << "${GLSL_VERSION_DECL}\n"
2015         << ((useImageAtomics) ? ("${SHADER_IMAGE_ATOMIC_REQUIRE}\n") : (""))
2016         << "layout (local_size_x = 1, local_size_y = 1) in;\n";
2017 
2018     if (m_storage == STORAGE_BUFFER)
2019         buf << "layout(binding=1, std430) " << ((m_useAtomic) ? ("coherent ") : ("")) << "buffer Buffer0\n"
2020             << "{\n"
2021             << "    highp " << ((m_formatInteger) ? ("int") : ("float")) << " values[];\n"
2022             << "} sb_in0;\n"
2023             << "layout(binding=2, std430) " << ((m_useAtomic) ? ("coherent ") : ("")) << "buffer Buffer1\n"
2024             << "{\n"
2025             << "    highp " << ((m_formatInteger) ? ("int") : ("float")) << " values[];\n"
2026             << "} sb_in1;\n";
2027     else if (m_storage == STORAGE_IMAGE)
2028         buf << "layout(" << ((m_formatInteger) ? ("r32i") : ("r32f")) << ", binding=1) "
2029             << ((m_useAtomic) ? ("coherent ") : ("readonly ")) << "uniform highp "
2030             << ((m_formatInteger) ? ("iimage2D") : ("image2D")) << " u_imageIn0;\n"
2031             << "layout(" << ((m_formatInteger) ? ("r32i") : ("r32f")) << ", binding=2) "
2032             << ((m_useAtomic) ? ("coherent ") : ("readonly ")) << "uniform highp "
2033             << ((m_formatInteger) ? ("iimage2D") : ("image2D")) << " u_imageIn1;\n";
2034     else
2035         DE_ASSERT(false);
2036 
2037     buf << "layout(binding=0, std430) buffer ResultBuffer\n"
2038         << "{\n"
2039         << "    highp int resultOk[];\n"
2040         << "} sb_result;\n"
2041         << "\n"
2042         << "void main (void)\n"
2043         << "{\n"
2044         << "    uvec3 size = gl_NumWorkGroups * gl_WorkGroupSize;\n"
2045         << "    int groupNdx = int(size.x * size.y * gl_GlobalInvocationID.z + size.x*gl_GlobalInvocationID.y + "
2046            "gl_GlobalInvocationID.x);\n"
2047         << "    " << ((m_formatInteger) ? ("int") : ("float")) << " zero = " << ((m_formatInteger) ? ("0") : ("0.0"))
2048         << ";\n"
2049         << "    bool allOk = true;\n"
2050         << "\n";
2051 
2052     // Verify data
2053 
2054     if (m_storage == STORAGE_BUFFER)
2055     {
2056         for (int readNdx = 0; readNdx < m_perInvocationSize; ++readNdx)
2057             buf << "    allOk = allOk && (" << ((m_useAtomic) ? ("atomicExchange(") : (""))
2058                 << "sb_in0.values[(groupNdx + " << seed0 + readNdx * m_invocationGridSize * m_invocationGridSize
2059                 << ") % " << m_invocationGridSize * m_invocationGridSize * m_perInvocationSize << "]"
2060                 << ((m_useAtomic) ? (", zero)") : ("")) << " == " << ((m_formatInteger) ? ("int") : ("float"))
2061                 << "(groupNdx));\n"
2062                 << "    allOk = allOk && (" << ((m_useAtomic) ? ("atomicExchange(") : (""))
2063                 << "sb_in1.values[(groupNdx + " << seed1 + readNdx * m_invocationGridSize * m_invocationGridSize
2064                 << ") % " << m_invocationGridSize * m_invocationGridSize * m_perInvocationSize << "]"
2065                 << ((m_useAtomic) ? (", zero)") : ("")) << " == " << ((m_formatInteger) ? ("int") : ("float"))
2066                 << "(groupNdx));\n";
2067     }
2068     else if (m_storage == STORAGE_IMAGE)
2069     {
2070         for (int readNdx = 0; readNdx < m_perInvocationSize; ++readNdx)
2071             buf << "    allOk = allOk && (" << ((m_useAtomic) ? ("imageAtomicExchange") : ("imageLoad"))
2072                 << "(u_imageIn0, ivec2((gl_GlobalInvocationID.x + " << (seed0 + readNdx * 100) << "u) % "
2073                 << m_invocationGridSize << "u, gl_GlobalInvocationID.y + " << readNdx * m_invocationGridSize << "u)"
2074                 << ((m_useAtomic) ? (", zero)") : (").x")) << " == " << ((m_formatInteger) ? ("int") : ("float"))
2075                 << "(groupNdx));\n"
2076                 << "    allOk = allOk && (" << ((m_useAtomic) ? ("imageAtomicExchange") : ("imageLoad"))
2077                 << "(u_imageIn1, ivec2((gl_GlobalInvocationID.x + " << (seed1 + readNdx * 100) << "u) % "
2078                 << m_invocationGridSize << "u, gl_GlobalInvocationID.y + " << readNdx * m_invocationGridSize << "u)"
2079                 << ((m_useAtomic) ? (", zero)") : (").x")) << " == " << ((m_formatInteger) ? ("int") : ("float"))
2080                 << "(groupNdx));\n";
2081     }
2082     else
2083         DE_ASSERT(false);
2084 
2085     buf << "    sb_result.resultOk[groupNdx] = (allOk) ? (1) : (0);\n"
2086         << "}\n";
2087 
2088     return new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(
2089                                                                     specializeShader(m_context, buf.str().c_str())));
2090 }
2091 
genWriteInterleavedProgram(int seed,bool evenOdd)2092 glu::ShaderProgram *InterCallTestCase::genWriteInterleavedProgram(int seed, bool evenOdd)
2093 {
2094     const bool useImageAtomics = m_useAtomic && m_storage == STORAGE_IMAGE;
2095     std::ostringstream buf;
2096 
2097     buf << "${GLSL_VERSION_DECL}\n"
2098         << ((useImageAtomics) ? ("${SHADER_IMAGE_ATOMIC_REQUIRE}\n") : (""))
2099         << "layout (local_size_x = 1, local_size_y = 1) in;\n";
2100 
2101     if (m_storage == STORAGE_BUFFER)
2102         buf << "layout(binding=0, std430) " << ((m_useAtomic) ? ("coherent ") : ("")) << "buffer Buffer\n"
2103             << "{\n"
2104             << "    highp " << ((m_formatInteger) ? ("int") : ("float")) << " values[];\n"
2105             << "} sb_out;\n";
2106     else if (m_storage == STORAGE_IMAGE)
2107         buf << "layout(" << ((m_formatInteger) ? ("r32i") : ("r32f")) << ", binding=0) "
2108             << ((m_useAtomic) ? ("coherent ") : ("writeonly ")) << "uniform highp "
2109             << ((m_formatInteger) ? ("iimage2D") : ("image2D")) << " u_imageOut;\n";
2110     else
2111         DE_ASSERT(false);
2112 
2113     buf << "\n"
2114         << "void main (void)\n"
2115         << "{\n"
2116         << "    uvec3 size    = gl_NumWorkGroups * gl_WorkGroupSize;\n"
2117         << "    int groupNdx  = int(size.x * size.y * gl_GlobalInvocationID.z + size.x*gl_GlobalInvocationID.y + "
2118            "gl_GlobalInvocationID.x);\n"
2119         << "\n";
2120 
2121     // Write to buffer/image m_perInvocationSize elements
2122     if (m_storage == STORAGE_BUFFER)
2123     {
2124         for (int writeNdx = 0; writeNdx < m_perInvocationSize; ++writeNdx)
2125         {
2126             if (m_useAtomic)
2127                 buf << "    atomicExchange(";
2128             else
2129                 buf << "    ";
2130 
2131             buf << "sb_out.values[((groupNdx + " << seed + writeNdx * m_invocationGridSize * m_invocationGridSize / 2
2132                 << ") % " << m_invocationGridSize * m_invocationGridSize / 2 * m_perInvocationSize << ") * 2 + "
2133                 << ((evenOdd) ? (0) : (1)) << "]";
2134 
2135             if (m_useAtomic)
2136                 buf << ", " << ((m_formatInteger) ? ("int") : ("float")) << "(groupNdx));\n";
2137             else
2138                 buf << "= " << ((m_formatInteger) ? ("int") : ("float")) << "(groupNdx);\n";
2139         }
2140     }
2141     else if (m_storage == STORAGE_IMAGE)
2142     {
2143         for (int writeNdx = 0; writeNdx < m_perInvocationSize; ++writeNdx)
2144         {
2145             if (m_useAtomic)
2146                 buf << "    imageAtomicExchange";
2147             else
2148                 buf << "    imageStore";
2149 
2150             buf << "(u_imageOut, ivec2(((int(gl_GlobalInvocationID.x) + " << (seed + writeNdx * 100) << ") % "
2151                 << m_invocationGridSize / 2 << ") * 2 + " << ((evenOdd) ? (0) : (1))
2152                 << ", int(gl_GlobalInvocationID.y) + " << writeNdx * m_invocationGridSize << "), ";
2153 
2154             if (m_useAtomic)
2155                 buf << ((m_formatInteger) ? ("int") : ("float")) << "(groupNdx));\n";
2156             else
2157                 buf << ((m_formatInteger) ? ("ivec4(int(groupNdx), 0, 0, 0)") :
2158                                             ("vec4(float(groupNdx), 0.0, 0.0, 0.0)"))
2159                     << ");\n";
2160         }
2161     }
2162     else
2163         DE_ASSERT(false);
2164 
2165     buf << "}\n";
2166 
2167     return new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(
2168                                                                     specializeShader(m_context, buf.str().c_str())));
2169 }
2170 
genReadInterleavedProgram(int seed0,int seed1)2171 glu::ShaderProgram *InterCallTestCase::genReadInterleavedProgram(int seed0, int seed1)
2172 {
2173     const bool useImageAtomics = m_useAtomic && m_storage == STORAGE_IMAGE;
2174     std::ostringstream buf;
2175 
2176     buf << "${GLSL_VERSION_DECL}\n"
2177         << ((useImageAtomics) ? ("${SHADER_IMAGE_ATOMIC_REQUIRE}\n") : (""))
2178         << "layout (local_size_x = 1, local_size_y = 1) in;\n";
2179 
2180     if (m_storage == STORAGE_BUFFER)
2181         buf << "layout(binding=1, std430) " << ((m_useAtomic) ? ("coherent ") : ("")) << "buffer Buffer\n"
2182             << "{\n"
2183             << "    highp " << ((m_formatInteger) ? ("int") : ("float")) << " values[];\n"
2184             << "} sb_in;\n";
2185     else if (m_storage == STORAGE_IMAGE)
2186         buf << "layout(" << ((m_formatInteger) ? ("r32i") : ("r32f")) << ", binding=1) "
2187             << ((m_useAtomic) ? ("coherent ") : ("readonly ")) << "uniform highp "
2188             << ((m_formatInteger) ? ("iimage2D") : ("image2D")) << " u_imageIn;\n";
2189     else
2190         DE_ASSERT(false);
2191 
2192     buf << "layout(binding=0, std430) buffer ResultBuffer\n"
2193         << "{\n"
2194         << "    highp int resultOk[];\n"
2195         << "} sb_result;\n"
2196         << "\n"
2197         << "void main (void)\n"
2198         << "{\n"
2199         << "    uvec3 size = gl_NumWorkGroups * gl_WorkGroupSize;\n"
2200         << "    int groupNdx = int(size.x * size.y * gl_GlobalInvocationID.z + size.x*gl_GlobalInvocationID.y + "
2201            "gl_GlobalInvocationID.x);\n"
2202         << "    int interleavedGroupNdx = int((size.x >> 1U) * size.y * gl_GlobalInvocationID.z + (size.x >> 1U) * "
2203            "gl_GlobalInvocationID.y + (gl_GlobalInvocationID.x >> 1U));\n"
2204         << "    " << ((m_formatInteger) ? ("int") : ("float")) << " zero = " << ((m_formatInteger) ? ("0") : ("0.0"))
2205         << ";\n"
2206         << "    bool allOk = true;\n"
2207         << "\n";
2208 
2209     // Verify data
2210 
2211     if (m_storage == STORAGE_BUFFER)
2212     {
2213         buf << "    if (groupNdx % 2 == 0)\n"
2214             << "    {\n";
2215         for (int readNdx = 0; readNdx < m_perInvocationSize; ++readNdx)
2216             buf << "        allOk = allOk && (" << ((m_useAtomic) ? ("atomicExchange(") : (""))
2217                 << "sb_in.values[((interleavedGroupNdx + "
2218                 << seed0 + readNdx * m_invocationGridSize * m_invocationGridSize / 2 << ") % "
2219                 << m_invocationGridSize * m_invocationGridSize * m_perInvocationSize / 2 << ") * 2 + 0]"
2220                 << ((m_useAtomic) ? (", zero)") : ("")) << " == " << ((m_formatInteger) ? ("int") : ("float"))
2221                 << "(interleavedGroupNdx));\n";
2222         buf << "    }\n"
2223             << "    else\n"
2224             << "    {\n";
2225         for (int readNdx = 0; readNdx < m_perInvocationSize; ++readNdx)
2226             buf << "        allOk = allOk && (" << ((m_useAtomic) ? ("atomicExchange(") : (""))
2227                 << "sb_in.values[((interleavedGroupNdx + "
2228                 << seed1 + readNdx * m_invocationGridSize * m_invocationGridSize / 2 << ") % "
2229                 << m_invocationGridSize * m_invocationGridSize * m_perInvocationSize / 2 << ") * 2 + 1]"
2230                 << ((m_useAtomic) ? (", zero)") : ("")) << " == " << ((m_formatInteger) ? ("int") : ("float"))
2231                 << "(interleavedGroupNdx));\n";
2232         buf << "    }\n";
2233     }
2234     else if (m_storage == STORAGE_IMAGE)
2235     {
2236         buf << "    if (groupNdx % 2 == 0)\n"
2237             << "    {\n";
2238         for (int readNdx = 0; readNdx < m_perInvocationSize; ++readNdx)
2239             buf << "        allOk = allOk && (" << ((m_useAtomic) ? ("imageAtomicExchange") : ("imageLoad"))
2240                 << "(u_imageIn, ivec2(((int(gl_GlobalInvocationID.x >> 1U) + " << (seed0 + readNdx * 100) << ") % "
2241                 << m_invocationGridSize / 2 << ") * 2 + 0, int(gl_GlobalInvocationID.y) + "
2242                 << readNdx * m_invocationGridSize << ")" << ((m_useAtomic) ? (", zero)") : (").x"))
2243                 << " == " << ((m_formatInteger) ? ("int") : ("float")) << "(interleavedGroupNdx));\n";
2244         buf << "    }\n"
2245             << "    else\n"
2246             << "    {\n";
2247         for (int readNdx = 0; readNdx < m_perInvocationSize; ++readNdx)
2248             buf << "        allOk = allOk && (" << ((m_useAtomic) ? ("imageAtomicExchange") : ("imageLoad"))
2249                 << "(u_imageIn, ivec2(((int(gl_GlobalInvocationID.x >> 1U) + " << (seed1 + readNdx * 100) << ") % "
2250                 << m_invocationGridSize / 2 << ") * 2 + 1, int(gl_GlobalInvocationID.y) + "
2251                 << readNdx * m_invocationGridSize << ")" << ((m_useAtomic) ? (", zero)") : (").x"))
2252                 << " == " << ((m_formatInteger) ? ("int") : ("float")) << "(interleavedGroupNdx));\n";
2253         buf << "    }\n";
2254     }
2255     else
2256         DE_ASSERT(false);
2257 
2258     buf << "    sb_result.resultOk[groupNdx] = (allOk) ? (1) : (0);\n"
2259         << "}\n";
2260 
2261     return new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(
2262                                                                     specializeShader(m_context, buf.str().c_str())));
2263 }
2264 
genReadZeroProgram(void)2265 glu::ShaderProgram *InterCallTestCase::genReadZeroProgram(void)
2266 {
2267     const bool useImageAtomics = m_useAtomic && m_storage == STORAGE_IMAGE;
2268     std::ostringstream buf;
2269 
2270     buf << "${GLSL_VERSION_DECL}\n"
2271         << ((useImageAtomics) ? ("${SHADER_IMAGE_ATOMIC_REQUIRE}\n") : (""))
2272         << "layout (local_size_x = 1, local_size_y = 1) in;\n";
2273 
2274     if (m_storage == STORAGE_BUFFER)
2275         buf << "layout(binding=1, std430) " << ((m_useAtomic) ? ("coherent ") : ("")) << "buffer Buffer\n"
2276             << "{\n"
2277             << "    highp " << ((m_formatInteger) ? ("int") : ("float")) << " values[];\n"
2278             << "} sb_in;\n";
2279     else if (m_storage == STORAGE_IMAGE)
2280         buf << "layout(" << ((m_formatInteger) ? ("r32i") : ("r32f")) << ", binding=1) "
2281             << ((m_useAtomic) ? ("coherent ") : ("readonly ")) << "uniform highp "
2282             << ((m_formatInteger) ? ("iimage2D") : ("image2D")) << " u_imageIn;\n";
2283     else
2284         DE_ASSERT(false);
2285 
2286     buf << "layout(binding=0, std430) buffer ResultBuffer\n"
2287         << "{\n"
2288         << "    highp int resultOk[];\n"
2289         << "} sb_result;\n"
2290         << "\n"
2291         << "void main (void)\n"
2292         << "{\n"
2293         << "    uvec3 size = gl_NumWorkGroups * gl_WorkGroupSize;\n"
2294         << "    int groupNdx = int(size.x * size.y * gl_GlobalInvocationID.z + size.x*gl_GlobalInvocationID.y + "
2295            "gl_GlobalInvocationID.x);\n"
2296         << "    " << ((m_formatInteger) ? ("int") : ("float"))
2297         << " anything = " << ((m_formatInteger) ? ("5") : ("5.0")) << ";\n"
2298         << "    bool allOk = true;\n"
2299         << "\n";
2300 
2301     // Verify data
2302 
2303     if (m_storage == STORAGE_BUFFER)
2304     {
2305         for (int readNdx = 0; readNdx < m_perInvocationSize; ++readNdx)
2306             buf << "    allOk = allOk && (" << ((m_useAtomic) ? ("atomicExchange(") : (""))
2307                 << "sb_in.values[groupNdx * " << m_perInvocationSize << " + " << readNdx << "]"
2308                 << ((m_useAtomic) ? (", anything)") : ("")) << " == " << ((m_formatInteger) ? ("0") : ("0.0"))
2309                 << ");\n";
2310     }
2311     else if (m_storage == STORAGE_IMAGE)
2312     {
2313         for (int readNdx = 0; readNdx < m_perInvocationSize; ++readNdx)
2314             buf << "    allOk = allOk && (" << ((m_useAtomic) ? ("imageAtomicExchange") : ("imageLoad"))
2315                 << "(u_imageIn, ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y + "
2316                 << (readNdx * m_invocationGridSize) << "u)" << ((m_useAtomic) ? (", anything)") : (").x"))
2317                 << " == " << ((m_formatInteger) ? ("0") : ("0.0")) << ");\n";
2318     }
2319     else
2320         DE_ASSERT(false);
2321 
2322     buf << "    sb_result.resultOk[groupNdx] = (allOk) ? (1) : (0);\n"
2323         << "}\n";
2324 
2325     return new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(
2326                                                                     specializeShader(m_context, buf.str().c_str())));
2327 }
2328 
2329 class SSBOConcurrentAtomicCase : public TestCase
2330 {
2331 public:
2332     SSBOConcurrentAtomicCase(Context &context, const char *name, const char *description, int numCalls, int workSize);
2333     ~SSBOConcurrentAtomicCase(void);
2334 
2335     void init(void);
2336     void deinit(void);
2337     IterateResult iterate(void);
2338 
2339 private:
2340     std::string genComputeSource(void) const;
2341 
2342     const int m_numCalls;
2343     const int m_workSize;
2344     glu::ShaderProgram *m_program;
2345     uint32_t m_bufferID;
2346     std::vector<uint32_t> m_intermediateResultBuffers;
2347 };
2348 
SSBOConcurrentAtomicCase(Context & context,const char * name,const char * description,int numCalls,int workSize)2349 SSBOConcurrentAtomicCase::SSBOConcurrentAtomicCase(Context &context, const char *name, const char *description,
2350                                                    int numCalls, int workSize)
2351     : TestCase(context, name, description)
2352     , m_numCalls(numCalls)
2353     , m_workSize(workSize)
2354     , m_program(DE_NULL)
2355     , m_bufferID(DE_NULL)
2356 {
2357 }
2358 
~SSBOConcurrentAtomicCase(void)2359 SSBOConcurrentAtomicCase::~SSBOConcurrentAtomicCase(void)
2360 {
2361     deinit();
2362 }
2363 
init(void)2364 void SSBOConcurrentAtomicCase::init(void)
2365 {
2366     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
2367     std::vector<uint32_t> zeroData(m_workSize, 0);
2368 
2369     // gen buffers
2370 
2371     gl.genBuffers(1, &m_bufferID);
2372     gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_bufferID);
2373     gl.bufferData(GL_SHADER_STORAGE_BUFFER, sizeof(uint32_t) * m_workSize, &zeroData[0], GL_DYNAMIC_COPY);
2374 
2375     for (int ndx = 0; ndx < m_numCalls; ++ndx)
2376     {
2377         uint32_t buffer = 0;
2378 
2379         gl.genBuffers(1, &buffer);
2380         gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
2381         gl.bufferData(GL_SHADER_STORAGE_BUFFER, sizeof(uint32_t) * m_workSize, &zeroData[0], GL_DYNAMIC_COPY);
2382 
2383         m_intermediateResultBuffers.push_back(buffer);
2384         GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffers");
2385     }
2386 
2387     // gen program
2388 
2389     m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
2390                                                                          << glu::ComputeSource(genComputeSource()));
2391     m_testCtx.getLog() << *m_program;
2392     if (!m_program->isOk())
2393         throw tcu::TestError("could not build program");
2394 }
2395 
deinit(void)2396 void SSBOConcurrentAtomicCase::deinit(void)
2397 {
2398     if (m_bufferID)
2399     {
2400         m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_bufferID);
2401         m_bufferID = 0;
2402     }
2403 
2404     for (int ndx = 0; ndx < (int)m_intermediateResultBuffers.size(); ++ndx)
2405         m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_intermediateResultBuffers[ndx]);
2406     m_intermediateResultBuffers.clear();
2407 
2408     delete m_program;
2409     m_program = DE_NULL;
2410 }
2411 
iterate(void)2412 TestCase::IterateResult SSBOConcurrentAtomicCase::iterate(void)
2413 {
2414     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
2415     const uint32_t sumValue  = (uint32_t)(m_numCalls * (m_numCalls + 1) / 2);
2416     std::vector<int> deltas;
2417 
2418     // generate unique deltas
2419     generateShuffledRamp(m_numCalls, deltas);
2420 
2421     // invoke program N times, each with a different delta
2422     {
2423         const int deltaLocation = gl.getUniformLocation(m_program->getProgram(), "u_atomicDelta");
2424 
2425         m_testCtx.getLog() << tcu::TestLog::Message << "Running shader " << m_numCalls << " times.\n"
2426                            << "Num groups = (" << m_workSize << ", 1, 1)\n"
2427                            << "Setting u_atomicDelta to a unique value for each call.\n"
2428                            << tcu::TestLog::EndMessage;
2429 
2430         if (deltaLocation == -1)
2431             throw tcu::TestError("u_atomicDelta location was -1");
2432 
2433         gl.useProgram(m_program->getProgram());
2434         gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, m_bufferID);
2435 
2436         for (int callNdx = 0; callNdx < m_numCalls; ++callNdx)
2437         {
2438             m_testCtx.getLog() << tcu::TestLog::Message << "Call " << callNdx << ": u_atomicDelta = " << deltas[callNdx]
2439                                << tcu::TestLog::EndMessage;
2440 
2441             gl.uniform1ui(deltaLocation, deltas[callNdx]);
2442             gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_intermediateResultBuffers[callNdx]);
2443             gl.dispatchCompute(m_workSize, 1, 1);
2444         }
2445 
2446         GLU_EXPECT_NO_ERROR(gl.getError(), "post dispatch");
2447     }
2448 
2449     // Verify result
2450     {
2451         std::vector<uint32_t> result;
2452 
2453         m_testCtx.getLog() << tcu::TestLog::Message << "Verifying work buffer, it should be filled with value "
2454                            << sumValue << tcu::TestLog::EndMessage;
2455 
2456         gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_bufferID);
2457         readBuffer(gl, GL_SHADER_STORAGE_BUFFER, m_workSize, result);
2458 
2459         for (int ndx = 0; ndx < m_workSize; ++ndx)
2460         {
2461             if (result[ndx] != sumValue)
2462             {
2463                 m_testCtx.getLog() << tcu::TestLog::Message << "Work buffer error, at index " << ndx
2464                                    << " expected value " << (sumValue) << ", got " << result[ndx] << "\n"
2465                                    << "Work buffer contains invalid values." << tcu::TestLog::EndMessage;
2466 
2467                 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Buffer contents invalid");
2468                 return STOP;
2469             }
2470         }
2471 
2472         m_testCtx.getLog() << tcu::TestLog::Message << "Work buffer contents are valid." << tcu::TestLog::EndMessage;
2473     }
2474 
2475     // verify steps
2476     {
2477         std::vector<std::vector<uint32_t>> intermediateResults(m_numCalls);
2478         std::vector<uint32_t> valueChain(m_numCalls);
2479 
2480         m_testCtx.getLog() << tcu::TestLog::Message << "Verifying intermediate results. " << tcu::TestLog::EndMessage;
2481 
2482         // collect results
2483 
2484         for (int callNdx = 0; callNdx < m_numCalls; ++callNdx)
2485         {
2486             gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_intermediateResultBuffers[callNdx]);
2487             readBuffer(gl, GL_SHADER_STORAGE_BUFFER, m_workSize, intermediateResults[callNdx]);
2488         }
2489 
2490         // verify values
2491 
2492         for (int valueNdx = 0; valueNdx < m_workSize; ++valueNdx)
2493         {
2494             int invalidOperationNdx;
2495             uint32_t errorDelta;
2496             uint32_t errorExpected;
2497 
2498             // collect result chain for each element
2499             for (int callNdx = 0; callNdx < m_numCalls; ++callNdx)
2500                 valueChain[callNdx] = intermediateResults[callNdx][valueNdx];
2501 
2502             // check there exists a path from 0 to sumValue using each addition once
2503             // decompose cumulative results to addition operations (all additions positive => this works)
2504 
2505             std::sort(valueChain.begin(), valueChain.end());
2506 
2507             // validate chain
2508             if (!validateSortedAtomicRampAdditionValueChain(valueChain, sumValue, invalidOperationNdx, errorDelta,
2509                                                             errorExpected))
2510             {
2511                 m_testCtx.getLog() << tcu::TestLog::Message << "Intermediate buffer error, at value index " << valueNdx
2512                                    << ", applied operation index " << invalidOperationNdx << ", value was increased by "
2513                                    << errorDelta << ", but expected " << errorExpected << ".\n"
2514                                    << "Intermediate buffer contains invalid values. Values at index " << valueNdx
2515                                    << "\n"
2516                                    << tcu::TestLog::EndMessage;
2517 
2518                 for (int logCallNdx = 0; logCallNdx < m_numCalls; ++logCallNdx)
2519                     m_testCtx.getLog() << tcu::TestLog::Message << "Value[" << logCallNdx
2520                                        << "] = " << intermediateResults[logCallNdx][valueNdx]
2521                                        << tcu::TestLog::EndMessage;
2522                 m_testCtx.getLog() << tcu::TestLog::Message << "Result = " << sumValue << tcu::TestLog::EndMessage;
2523 
2524                 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Buffer contents invalid");
2525                 return STOP;
2526             }
2527         }
2528 
2529         m_testCtx.getLog() << tcu::TestLog::Message << "Intermediate buffers are valid." << tcu::TestLog::EndMessage;
2530     }
2531 
2532     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2533     return STOP;
2534 }
2535 
genComputeSource(void) const2536 std::string SSBOConcurrentAtomicCase::genComputeSource(void) const
2537 {
2538     std::ostringstream buf;
2539 
2540     buf << "${GLSL_VERSION_DECL}\n"
2541         << "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
2542         << "layout (binding = 1, std430) writeonly buffer IntermediateResults\n"
2543         << "{\n"
2544         << "    highp uint values[" << m_workSize << "];\n"
2545         << "} sb_ires;\n"
2546         << "\n"
2547         << "layout (binding = 2, std430) volatile buffer WorkBuffer\n"
2548         << "{\n"
2549         << "    highp uint values[" << m_workSize << "];\n"
2550         << "} sb_work;\n"
2551         << "uniform highp uint u_atomicDelta;\n"
2552         << "\n"
2553         << "void main ()\n"
2554         << "{\n"
2555         << "    highp uint invocationIndex = gl_GlobalInvocationID.x;\n"
2556         << "    sb_ires.values[invocationIndex] = atomicAdd(sb_work.values[invocationIndex], u_atomicDelta);\n"
2557         << "}";
2558 
2559     return specializeShader(m_context, buf.str().c_str());
2560 }
2561 
2562 class ConcurrentAtomicCounterCase : public TestCase
2563 {
2564 public:
2565     ConcurrentAtomicCounterCase(Context &context, const char *name, const char *description, int numCalls,
2566                                 int workSize);
2567     ~ConcurrentAtomicCounterCase(void);
2568 
2569     void init(void);
2570     void deinit(void);
2571     IterateResult iterate(void);
2572 
2573 private:
2574     std::string genComputeSource(bool evenOdd) const;
2575 
2576     const int m_numCalls;
2577     const int m_workSize;
2578     glu::ShaderProgram *m_evenProgram;
2579     glu::ShaderProgram *m_oddProgram;
2580     uint32_t m_counterBuffer;
2581     uint32_t m_intermediateResultBuffer;
2582 };
2583 
ConcurrentAtomicCounterCase(Context & context,const char * name,const char * description,int numCalls,int workSize)2584 ConcurrentAtomicCounterCase::ConcurrentAtomicCounterCase(Context &context, const char *name, const char *description,
2585                                                          int numCalls, int workSize)
2586     : TestCase(context, name, description)
2587     , m_numCalls(numCalls)
2588     , m_workSize(workSize)
2589     , m_evenProgram(DE_NULL)
2590     , m_oddProgram(DE_NULL)
2591     , m_counterBuffer(DE_NULL)
2592     , m_intermediateResultBuffer(DE_NULL)
2593 {
2594 }
2595 
~ConcurrentAtomicCounterCase(void)2596 ConcurrentAtomicCounterCase::~ConcurrentAtomicCounterCase(void)
2597 {
2598     deinit();
2599 }
2600 
init(void)2601 void ConcurrentAtomicCounterCase::init(void)
2602 {
2603     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
2604     const std::vector<uint32_t> zeroData(m_numCalls * m_workSize, 0);
2605 
2606     // gen buffer
2607 
2608     gl.genBuffers(1, &m_counterBuffer);
2609     gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_counterBuffer);
2610     gl.bufferData(GL_SHADER_STORAGE_BUFFER, sizeof(uint32_t), &zeroData[0], GL_DYNAMIC_COPY);
2611 
2612     gl.genBuffers(1, &m_intermediateResultBuffer);
2613     gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_intermediateResultBuffer);
2614     gl.bufferData(GL_SHADER_STORAGE_BUFFER, sizeof(uint32_t) * m_numCalls * m_workSize, &zeroData[0], GL_DYNAMIC_COPY);
2615 
2616     GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffers");
2617 
2618     // gen programs
2619 
2620     {
2621         const tcu::ScopedLogSection section(m_testCtx.getLog(), "EvenProgram", "Even program");
2622 
2623         m_evenProgram = new glu::ShaderProgram(m_context.getRenderContext(),
2624                                                glu::ProgramSources() << glu::ComputeSource(genComputeSource(true)));
2625         m_testCtx.getLog() << *m_evenProgram;
2626         if (!m_evenProgram->isOk())
2627             throw tcu::TestError("could not build program");
2628     }
2629     {
2630         const tcu::ScopedLogSection section(m_testCtx.getLog(), "OddProgram", "Odd program");
2631 
2632         m_oddProgram = new glu::ShaderProgram(m_context.getRenderContext(),
2633                                               glu::ProgramSources() << glu::ComputeSource(genComputeSource(false)));
2634         m_testCtx.getLog() << *m_oddProgram;
2635         if (!m_oddProgram->isOk())
2636             throw tcu::TestError("could not build program");
2637     }
2638 }
2639 
deinit(void)2640 void ConcurrentAtomicCounterCase::deinit(void)
2641 {
2642     if (m_counterBuffer)
2643     {
2644         m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_counterBuffer);
2645         m_counterBuffer = 0;
2646     }
2647     if (m_intermediateResultBuffer)
2648     {
2649         m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_intermediateResultBuffer);
2650         m_intermediateResultBuffer = 0;
2651     }
2652 
2653     delete m_evenProgram;
2654     m_evenProgram = DE_NULL;
2655 
2656     delete m_oddProgram;
2657     m_oddProgram = DE_NULL;
2658 }
2659 
iterate(void)2660 TestCase::IterateResult ConcurrentAtomicCounterCase::iterate(void)
2661 {
2662     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
2663 
2664     // invoke program N times, each with a different delta
2665     {
2666         const int evenCallNdxLocation = gl.getUniformLocation(m_evenProgram->getProgram(), "u_callNdx");
2667         const int oddCallNdxLocation  = gl.getUniformLocation(m_oddProgram->getProgram(), "u_callNdx");
2668 
2669         m_testCtx.getLog() << tcu::TestLog::Message << "Running shader pair (even & odd) " << m_numCalls << " times.\n"
2670                            << "Num groups = (" << m_workSize << ", 1, 1)\n"
2671                            << tcu::TestLog::EndMessage;
2672 
2673         if (evenCallNdxLocation == -1)
2674             throw tcu::TestError("u_callNdx location was -1");
2675         if (oddCallNdxLocation == -1)
2676             throw tcu::TestError("u_callNdx location was -1");
2677 
2678         gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_intermediateResultBuffer);
2679         gl.bindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, m_counterBuffer);
2680 
2681         for (int callNdx = 0; callNdx < m_numCalls; ++callNdx)
2682         {
2683             gl.useProgram(m_evenProgram->getProgram());
2684             gl.uniform1ui(evenCallNdxLocation, (uint32_t)callNdx);
2685             gl.dispatchCompute(m_workSize, 1, 1);
2686 
2687             gl.useProgram(m_oddProgram->getProgram());
2688             gl.uniform1ui(oddCallNdxLocation, (uint32_t)callNdx);
2689             gl.dispatchCompute(m_workSize, 1, 1);
2690         }
2691 
2692         GLU_EXPECT_NO_ERROR(gl.getError(), "post dispatch");
2693     }
2694 
2695     // Verify result
2696     {
2697         uint32_t result;
2698 
2699         m_testCtx.getLog() << tcu::TestLog::Message << "Verifying work buffer, it should be " << m_numCalls * m_workSize
2700                            << tcu::TestLog::EndMessage;
2701 
2702         gl.bindBuffer(GL_ATOMIC_COUNTER_BUFFER, m_counterBuffer);
2703         result = readBufferUint32(gl, GL_ATOMIC_COUNTER_BUFFER);
2704 
2705         if ((int)result != m_numCalls * m_workSize)
2706         {
2707             m_testCtx.getLog() << tcu::TestLog::Message << "Counter buffer error, expected value "
2708                                << (m_numCalls * m_workSize) << ", got " << result << "\n"
2709                                << tcu::TestLog::EndMessage;
2710 
2711             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Buffer contents invalid");
2712             return STOP;
2713         }
2714 
2715         m_testCtx.getLog() << tcu::TestLog::Message << "Counter buffer is valid." << tcu::TestLog::EndMessage;
2716     }
2717 
2718     // verify steps
2719     {
2720         std::vector<uint32_t> intermediateResults;
2721 
2722         m_testCtx.getLog() << tcu::TestLog::Message << "Verifying intermediate results. " << tcu::TestLog::EndMessage;
2723 
2724         // collect results
2725 
2726         gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_intermediateResultBuffer);
2727         readBuffer(gl, GL_SHADER_STORAGE_BUFFER, m_numCalls * m_workSize, intermediateResults);
2728 
2729         // verify values
2730 
2731         std::sort(intermediateResults.begin(), intermediateResults.end());
2732 
2733         for (int valueNdx = 0; valueNdx < m_workSize * m_numCalls; ++valueNdx)
2734         {
2735             if ((int)intermediateResults[valueNdx] != valueNdx)
2736             {
2737                 m_testCtx.getLog() << tcu::TestLog::Message << "Intermediate buffer error, at value index " << valueNdx
2738                                    << ", expected " << valueNdx << ", got " << intermediateResults[valueNdx] << ".\n"
2739                                    << "Intermediate buffer contains invalid values. Intermediate results:\n"
2740                                    << tcu::TestLog::EndMessage;
2741 
2742                 for (int logCallNdx = 0; logCallNdx < m_workSize * m_numCalls; ++logCallNdx)
2743                     m_testCtx.getLog() << tcu::TestLog::Message << "Value[" << logCallNdx
2744                                        << "] = " << intermediateResults[logCallNdx] << tcu::TestLog::EndMessage;
2745 
2746                 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Buffer contents invalid");
2747                 return STOP;
2748             }
2749         }
2750 
2751         m_testCtx.getLog() << tcu::TestLog::Message << "Intermediate buffers are valid." << tcu::TestLog::EndMessage;
2752     }
2753 
2754     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2755     return STOP;
2756 }
2757 
genComputeSource(bool evenOdd) const2758 std::string ConcurrentAtomicCounterCase::genComputeSource(bool evenOdd) const
2759 {
2760     std::ostringstream buf;
2761 
2762     buf << "${GLSL_VERSION_DECL}\n"
2763         << "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
2764         << "layout (binding = 1, std430) writeonly buffer IntermediateResults\n"
2765         << "{\n"
2766         << "    highp uint values[" << m_workSize * m_numCalls << "];\n"
2767         << "} sb_ires;\n"
2768         << "\n"
2769         << "layout (binding = 0, offset = 0) uniform atomic_uint u_counter;\n"
2770         << "uniform highp uint u_callNdx;\n"
2771         << "\n"
2772         << "void main ()\n"
2773         << "{\n"
2774         << "    highp uint dataNdx = u_callNdx * " << m_workSize << "u + gl_GlobalInvocationID.x;\n"
2775         << "    if ((dataNdx % 2u) == " << ((evenOdd) ? (0) : (1)) << "u)\n"
2776         << "        sb_ires.values[dataNdx] = atomicCounterIncrement(u_counter);\n"
2777         << "}";
2778 
2779     return specializeShader(m_context, buf.str().c_str());
2780 }
2781 
2782 class ConcurrentImageAtomicCase : public TestCase
2783 {
2784 public:
2785     ConcurrentImageAtomicCase(Context &context, const char *name, const char *description, int numCalls, int workSize);
2786     ~ConcurrentImageAtomicCase(void);
2787 
2788     void init(void);
2789     void deinit(void);
2790     IterateResult iterate(void);
2791 
2792 private:
2793     void readWorkImage(std::vector<uint32_t> &result);
2794 
2795     std::string genComputeSource(void) const;
2796     std::string genImageReadSource(void) const;
2797     std::string genImageClearSource(void) const;
2798 
2799     const int m_numCalls;
2800     const int m_workSize;
2801     glu::ShaderProgram *m_program;
2802     glu::ShaderProgram *m_imageReadProgram;
2803     glu::ShaderProgram *m_imageClearProgram;
2804     uint32_t m_imageID;
2805     std::vector<uint32_t> m_intermediateResultBuffers;
2806 };
2807 
ConcurrentImageAtomicCase(Context & context,const char * name,const char * description,int numCalls,int workSize)2808 ConcurrentImageAtomicCase::ConcurrentImageAtomicCase(Context &context, const char *name, const char *description,
2809                                                      int numCalls, int workSize)
2810     : TestCase(context, name, description)
2811     , m_numCalls(numCalls)
2812     , m_workSize(workSize)
2813     , m_program(DE_NULL)
2814     , m_imageReadProgram(DE_NULL)
2815     , m_imageClearProgram(DE_NULL)
2816     , m_imageID(DE_NULL)
2817 {
2818 }
2819 
~ConcurrentImageAtomicCase(void)2820 ConcurrentImageAtomicCase::~ConcurrentImageAtomicCase(void)
2821 {
2822     deinit();
2823 }
2824 
init(void)2825 void ConcurrentImageAtomicCase::init(void)
2826 {
2827     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
2828     std::vector<uint32_t> zeroData(m_workSize * m_workSize, 0);
2829 
2830     if (!checkSupport(m_context))
2831         throw tcu::NotSupportedError("Test requires GL_OES_shader_image_atomic");
2832 
2833     // gen image
2834 
2835     gl.genTextures(1, &m_imageID);
2836     gl.bindTexture(GL_TEXTURE_2D, m_imageID);
2837     gl.texStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, m_workSize, m_workSize);
2838     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2839     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2840     GLU_EXPECT_NO_ERROR(gl.getError(), "gen tex");
2841 
2842     // gen buffers
2843 
2844     for (int ndx = 0; ndx < m_numCalls; ++ndx)
2845     {
2846         uint32_t buffer = 0;
2847 
2848         gl.genBuffers(1, &buffer);
2849         gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
2850         gl.bufferData(GL_SHADER_STORAGE_BUFFER, sizeof(uint32_t) * m_workSize * m_workSize, &zeroData[0],
2851                       GL_DYNAMIC_COPY);
2852 
2853         m_intermediateResultBuffers.push_back(buffer);
2854         GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffers");
2855     }
2856 
2857     // gen programs
2858 
2859     m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
2860                                                                          << glu::ComputeSource(genComputeSource()));
2861     m_testCtx.getLog() << *m_program;
2862     if (!m_program->isOk())
2863         throw tcu::TestError("could not build program");
2864 
2865     m_imageReadProgram = new glu::ShaderProgram(m_context.getRenderContext(),
2866                                                 glu::ProgramSources() << glu::ComputeSource(genImageReadSource()));
2867     if (!m_imageReadProgram->isOk())
2868     {
2869         const tcu::ScopedLogSection section(m_testCtx.getLog(), "ImageReadProgram", "Image read program");
2870 
2871         m_testCtx.getLog() << *m_imageReadProgram;
2872         throw tcu::TestError("could not build program");
2873     }
2874 
2875     m_imageClearProgram = new glu::ShaderProgram(m_context.getRenderContext(),
2876                                                  glu::ProgramSources() << glu::ComputeSource(genImageClearSource()));
2877     if (!m_imageClearProgram->isOk())
2878     {
2879         const tcu::ScopedLogSection section(m_testCtx.getLog(), "ImageClearProgram", "Image read program");
2880 
2881         m_testCtx.getLog() << *m_imageClearProgram;
2882         throw tcu::TestError("could not build program");
2883     }
2884 }
2885 
deinit(void)2886 void ConcurrentImageAtomicCase::deinit(void)
2887 {
2888     if (m_imageID)
2889     {
2890         m_context.getRenderContext().getFunctions().deleteTextures(1, &m_imageID);
2891         m_imageID = 0;
2892     }
2893 
2894     for (int ndx = 0; ndx < (int)m_intermediateResultBuffers.size(); ++ndx)
2895         m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_intermediateResultBuffers[ndx]);
2896     m_intermediateResultBuffers.clear();
2897 
2898     delete m_program;
2899     m_program = DE_NULL;
2900 
2901     delete m_imageReadProgram;
2902     m_imageReadProgram = DE_NULL;
2903 
2904     delete m_imageClearProgram;
2905     m_imageClearProgram = DE_NULL;
2906 }
2907 
iterate(void)2908 TestCase::IterateResult ConcurrentImageAtomicCase::iterate(void)
2909 {
2910     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
2911     const uint32_t sumValue  = (uint32_t)(m_numCalls * (m_numCalls + 1) / 2);
2912     std::vector<int> deltas;
2913 
2914     // generate unique deltas
2915     generateShuffledRamp(m_numCalls, deltas);
2916 
2917     // clear image
2918     {
2919         m_testCtx.getLog() << tcu::TestLog::Message << "Clearing image contents" << tcu::TestLog::EndMessage;
2920 
2921         gl.useProgram(m_imageClearProgram->getProgram());
2922         gl.bindImageTexture(2, m_imageID, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32UI);
2923         gl.dispatchCompute(m_workSize, m_workSize, 1);
2924         gl.memoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
2925 
2926         GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
2927     }
2928 
2929     // invoke program N times, each with a different delta
2930     {
2931         const int deltaLocation = gl.getUniformLocation(m_program->getProgram(), "u_atomicDelta");
2932 
2933         m_testCtx.getLog() << tcu::TestLog::Message << "Running shader " << m_numCalls << " times.\n"
2934                            << "Num groups = (" << m_workSize << ", " << m_workSize << ", 1)\n"
2935                            << "Setting u_atomicDelta to a unique value for each call.\n"
2936                            << tcu::TestLog::EndMessage;
2937 
2938         if (deltaLocation == -1)
2939             throw tcu::TestError("u_atomicDelta location was -1");
2940 
2941         gl.useProgram(m_program->getProgram());
2942         gl.bindImageTexture(2, m_imageID, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32UI);
2943 
2944         for (int callNdx = 0; callNdx < m_numCalls; ++callNdx)
2945         {
2946             m_testCtx.getLog() << tcu::TestLog::Message << "Call " << callNdx << ": u_atomicDelta = " << deltas[callNdx]
2947                                << tcu::TestLog::EndMessage;
2948 
2949             gl.uniform1ui(deltaLocation, deltas[callNdx]);
2950             gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_intermediateResultBuffers[callNdx]);
2951             gl.dispatchCompute(m_workSize, m_workSize, 1);
2952         }
2953 
2954         GLU_EXPECT_NO_ERROR(gl.getError(), "post dispatch");
2955     }
2956 
2957     // Verify result
2958     {
2959         std::vector<uint32_t> result;
2960 
2961         m_testCtx.getLog() << tcu::TestLog::Message << "Verifying work image, it should be filled with value "
2962                            << sumValue << tcu::TestLog::EndMessage;
2963 
2964         readWorkImage(result);
2965 
2966         for (int ndx = 0; ndx < m_workSize * m_workSize; ++ndx)
2967         {
2968             if (result[ndx] != sumValue)
2969             {
2970                 m_testCtx.getLog() << tcu::TestLog::Message << "Work image error, at index (" << ndx % m_workSize
2971                                    << ", " << ndx / m_workSize << ") expected value " << (sumValue) << ", got "
2972                                    << result[ndx] << "\n"
2973                                    << "Work image contains invalid values." << tcu::TestLog::EndMessage;
2974 
2975                 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image contents invalid");
2976                 return STOP;
2977             }
2978         }
2979 
2980         m_testCtx.getLog() << tcu::TestLog::Message << "Work image contents are valid." << tcu::TestLog::EndMessage;
2981     }
2982 
2983     // verify steps
2984     {
2985         std::vector<std::vector<uint32_t>> intermediateResults(m_numCalls);
2986         std::vector<uint32_t> valueChain(m_numCalls);
2987         std::vector<uint32_t> chainDelta(m_numCalls);
2988 
2989         m_testCtx.getLog() << tcu::TestLog::Message << "Verifying intermediate results. " << tcu::TestLog::EndMessage;
2990 
2991         // collect results
2992 
2993         for (int callNdx = 0; callNdx < m_numCalls; ++callNdx)
2994         {
2995             gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_intermediateResultBuffers[callNdx]);
2996             readBuffer(gl, GL_SHADER_STORAGE_BUFFER, m_workSize * m_workSize, intermediateResults[callNdx]);
2997         }
2998 
2999         // verify values
3000 
3001         for (int valueNdx = 0; valueNdx < m_workSize; ++valueNdx)
3002         {
3003             int invalidOperationNdx;
3004             uint32_t errorDelta;
3005             uint32_t errorExpected;
3006 
3007             // collect result chain for each element
3008             for (int callNdx = 0; callNdx < m_numCalls; ++callNdx)
3009                 valueChain[callNdx] = intermediateResults[callNdx][valueNdx];
3010 
3011             // check there exists a path from 0 to sumValue using each addition once
3012             // decompose cumulative results to addition operations (all additions positive => this works)
3013 
3014             std::sort(valueChain.begin(), valueChain.end());
3015 
3016             for (int callNdx = 0; callNdx < m_numCalls; ++callNdx)
3017                 chainDelta[callNdx] =
3018                     ((callNdx + 1 == m_numCalls) ? (sumValue) : (valueChain[callNdx + 1])) - valueChain[callNdx];
3019 
3020             // chainDelta contains now the actual additions applied to the value
3021             std::sort(chainDelta.begin(), chainDelta.end());
3022 
3023             // validate chain
3024             if (!validateSortedAtomicRampAdditionValueChain(valueChain, sumValue, invalidOperationNdx, errorDelta,
3025                                                             errorExpected))
3026             {
3027                 m_testCtx.getLog() << tcu::TestLog::Message << "Intermediate buffer error, at index ("
3028                                    << valueNdx % m_workSize << ", " << valueNdx / m_workSize
3029                                    << "), applied operation index " << invalidOperationNdx
3030                                    << ", value was increased by " << errorDelta << ", but expected " << errorExpected
3031                                    << ".\n"
3032                                    << "Intermediate buffer contains invalid values. Values at index ("
3033                                    << valueNdx % m_workSize << ", " << valueNdx / m_workSize << ")\n"
3034                                    << tcu::TestLog::EndMessage;
3035 
3036                 for (int logCallNdx = 0; logCallNdx < m_numCalls; ++logCallNdx)
3037                     m_testCtx.getLog() << tcu::TestLog::Message << "Value[" << logCallNdx
3038                                        << "] = " << intermediateResults[logCallNdx][valueNdx]
3039                                        << tcu::TestLog::EndMessage;
3040                 m_testCtx.getLog() << tcu::TestLog::Message << "Result = " << sumValue << tcu::TestLog::EndMessage;
3041 
3042                 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Buffer contents invalid");
3043                 return STOP;
3044             }
3045         }
3046 
3047         m_testCtx.getLog() << tcu::TestLog::Message << "Intermediate buffers are valid." << tcu::TestLog::EndMessage;
3048     }
3049 
3050     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3051     return STOP;
3052 }
3053 
readWorkImage(std::vector<uint32_t> & result)3054 void ConcurrentImageAtomicCase::readWorkImage(std::vector<uint32_t> &result)
3055 {
3056     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
3057     glu::Buffer resultBuffer(m_context.getRenderContext());
3058 
3059     // Read image to an ssbo
3060 
3061     {
3062         const std::vector<uint32_t> zeroData(m_workSize * m_workSize, 0);
3063 
3064         gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *resultBuffer);
3065         gl.bufferData(GL_SHADER_STORAGE_BUFFER, (int)(sizeof(uint32_t) * m_workSize * m_workSize), &zeroData[0],
3066                       GL_DYNAMIC_COPY);
3067 
3068         gl.memoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
3069         gl.useProgram(m_imageReadProgram->getProgram());
3070 
3071         gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, *resultBuffer);
3072         gl.bindImageTexture(2, m_imageID, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32UI);
3073         gl.dispatchCompute(m_workSize, m_workSize, 1);
3074 
3075         GLU_EXPECT_NO_ERROR(gl.getError(), "read");
3076     }
3077 
3078     // Read ssbo
3079     {
3080         const void *ptr = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0,
3081                                             (int)(sizeof(uint32_t) * m_workSize * m_workSize), GL_MAP_READ_BIT);
3082         GLU_EXPECT_NO_ERROR(gl.getError(), "map");
3083 
3084         if (!ptr)
3085             throw tcu::TestError("mapBufferRange returned NULL");
3086 
3087         result.resize(m_workSize * m_workSize);
3088         memcpy(&result[0], ptr, sizeof(uint32_t) * m_workSize * m_workSize);
3089 
3090         if (gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER) == GL_FALSE)
3091             throw tcu::TestError("unmapBuffer returned false");
3092     }
3093 }
3094 
genComputeSource(void) const3095 std::string ConcurrentImageAtomicCase::genComputeSource(void) const
3096 {
3097     std::ostringstream buf;
3098 
3099     buf << "${GLSL_VERSION_DECL}\n"
3100         << "${SHADER_IMAGE_ATOMIC_REQUIRE}\n"
3101         << "\n"
3102         << "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
3103         << "layout (binding = 1, std430) writeonly buffer IntermediateResults\n"
3104         << "{\n"
3105         << "    highp uint values[" << m_workSize * m_workSize << "];\n"
3106         << "} sb_ires;\n"
3107         << "\n"
3108         << "layout (binding = 2, r32ui) volatile uniform highp uimage2D u_workImage;\n"
3109         << "uniform highp uint u_atomicDelta;\n"
3110         << "\n"
3111         << "void main ()\n"
3112         << "{\n"
3113         << "    highp uint invocationIndex = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * uint(" << m_workSize
3114         << ");\n"
3115         << "    sb_ires.values[invocationIndex] = imageAtomicAdd(u_workImage, ivec2(gl_GlobalInvocationID.xy), "
3116            "u_atomicDelta);\n"
3117         << "}";
3118 
3119     return specializeShader(m_context, buf.str().c_str());
3120 }
3121 
genImageReadSource(void) const3122 std::string ConcurrentImageAtomicCase::genImageReadSource(void) const
3123 {
3124     std::ostringstream buf;
3125 
3126     buf << "${GLSL_VERSION_DECL}\n"
3127         << "\n"
3128         << "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
3129         << "layout (binding = 1, std430) writeonly buffer ImageValues\n"
3130         << "{\n"
3131         << "    highp uint values[" << m_workSize * m_workSize << "];\n"
3132         << "} sb_res;\n"
3133         << "\n"
3134         << "layout (binding = 2, r32ui) readonly uniform highp uimage2D u_workImage;\n"
3135         << "\n"
3136         << "void main ()\n"
3137         << "{\n"
3138         << "    highp uint invocationIndex = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * uint(" << m_workSize
3139         << ");\n"
3140         << "    sb_res.values[invocationIndex] = imageLoad(u_workImage, ivec2(gl_GlobalInvocationID.xy)).x;\n"
3141         << "}";
3142 
3143     return specializeShader(m_context, buf.str().c_str());
3144 }
3145 
genImageClearSource(void) const3146 std::string ConcurrentImageAtomicCase::genImageClearSource(void) const
3147 {
3148     std::ostringstream buf;
3149 
3150     buf << "${GLSL_VERSION_DECL}\n"
3151         << "\n"
3152         << "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
3153         << "layout (binding = 2, r32ui) writeonly uniform highp uimage2D u_workImage;\n"
3154         << "\n"
3155         << "void main ()\n"
3156         << "{\n"
3157         << "    imageStore(u_workImage, ivec2(gl_GlobalInvocationID.xy), uvec4(0, 0, 0, 0));\n"
3158         << "}";
3159 
3160     return specializeShader(m_context, buf.str().c_str());
3161 }
3162 
3163 class ConcurrentSSBOAtomicCounterMixedCase : public TestCase
3164 {
3165 public:
3166     ConcurrentSSBOAtomicCounterMixedCase(Context &context, const char *name, const char *description, int numCalls,
3167                                          int workSize);
3168     ~ConcurrentSSBOAtomicCounterMixedCase(void);
3169 
3170     void init(void);
3171     void deinit(void);
3172     IterateResult iterate(void);
3173 
3174 private:
3175     std::string genSSBOComputeSource(void) const;
3176     std::string genAtomicCounterComputeSource(void) const;
3177 
3178     const int m_numCalls;
3179     const int m_workSize;
3180     uint32_t m_bufferID;
3181     glu::ShaderProgram *m_ssboAtomicProgram;
3182     glu::ShaderProgram *m_atomicCounterProgram;
3183 };
3184 
ConcurrentSSBOAtomicCounterMixedCase(Context & context,const char * name,const char * description,int numCalls,int workSize)3185 ConcurrentSSBOAtomicCounterMixedCase::ConcurrentSSBOAtomicCounterMixedCase(Context &context, const char *name,
3186                                                                            const char *description, int numCalls,
3187                                                                            int workSize)
3188     : TestCase(context, name, description)
3189     , m_numCalls(numCalls)
3190     , m_workSize(workSize)
3191     , m_bufferID(DE_NULL)
3192     , m_ssboAtomicProgram(DE_NULL)
3193     , m_atomicCounterProgram(DE_NULL)
3194 {
3195     // SSBO atomic XORs cancel out
3196     DE_ASSERT((workSize * numCalls) % (16 * 2) == 0);
3197 }
3198 
~ConcurrentSSBOAtomicCounterMixedCase(void)3199 ConcurrentSSBOAtomicCounterMixedCase::~ConcurrentSSBOAtomicCounterMixedCase(void)
3200 {
3201     deinit();
3202 }
3203 
init(void)3204 void ConcurrentSSBOAtomicCounterMixedCase::init(void)
3205 {
3206     const glw::Functions &gl  = m_context.getRenderContext().getFunctions();
3207     const uint32_t zeroBuf[2] = {0, 0};
3208 
3209     // gen buffer
3210 
3211     gl.genBuffers(1, &m_bufferID);
3212     gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_bufferID);
3213     gl.bufferData(GL_SHADER_STORAGE_BUFFER, (int)(sizeof(uint32_t) * 2), zeroBuf, GL_DYNAMIC_COPY);
3214 
3215     GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffers");
3216 
3217     // gen programs
3218 
3219     {
3220         const tcu::ScopedLogSection section(m_testCtx.getLog(), "SSBOProgram", "SSBO atomic program");
3221 
3222         m_ssboAtomicProgram = new glu::ShaderProgram(
3223             m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(genSSBOComputeSource()));
3224         m_testCtx.getLog() << *m_ssboAtomicProgram;
3225         if (!m_ssboAtomicProgram->isOk())
3226             throw tcu::TestError("could not build program");
3227     }
3228     {
3229         const tcu::ScopedLogSection section(m_testCtx.getLog(), "AtomicCounterProgram", "Atomic counter program");
3230 
3231         m_atomicCounterProgram = new glu::ShaderProgram(
3232             m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(genAtomicCounterComputeSource()));
3233         m_testCtx.getLog() << *m_atomicCounterProgram;
3234         if (!m_atomicCounterProgram->isOk())
3235             throw tcu::TestError("could not build program");
3236     }
3237 }
3238 
deinit(void)3239 void ConcurrentSSBOAtomicCounterMixedCase::deinit(void)
3240 {
3241     if (m_bufferID)
3242     {
3243         m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_bufferID);
3244         m_bufferID = 0;
3245     }
3246 
3247     delete m_ssboAtomicProgram;
3248     m_ssboAtomicProgram = DE_NULL;
3249 
3250     delete m_atomicCounterProgram;
3251     m_atomicCounterProgram = DE_NULL;
3252 }
3253 
iterate(void)3254 TestCase::IterateResult ConcurrentSSBOAtomicCounterMixedCase::iterate(void)
3255 {
3256     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
3257 
3258     m_testCtx.getLog() << tcu::TestLog::Message
3259                        << "Testing atomic counters and SSBO atomic operations with both backed by the same buffer."
3260                        << tcu::TestLog::EndMessage;
3261 
3262     // invoke programs N times
3263     {
3264         m_testCtx.getLog() << tcu::TestLog::Message << "Running SSBO atomic program and atomic counter program "
3265                            << m_numCalls << " times. (interleaved)\n"
3266                            << "Num groups = (" << m_workSize << ", 1, 1)\n"
3267                            << tcu::TestLog::EndMessage;
3268 
3269         gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_bufferID);
3270         gl.bindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, m_bufferID);
3271 
3272         for (int callNdx = 0; callNdx < m_numCalls; ++callNdx)
3273         {
3274             gl.useProgram(m_atomicCounterProgram->getProgram());
3275             gl.dispatchCompute(m_workSize, 1, 1);
3276 
3277             gl.useProgram(m_ssboAtomicProgram->getProgram());
3278             gl.dispatchCompute(m_workSize, 1, 1);
3279         }
3280 
3281         GLU_EXPECT_NO_ERROR(gl.getError(), "post dispatch");
3282     }
3283 
3284     // Verify result
3285     {
3286         uint32_t result;
3287 
3288         // XORs cancel out, only addition is left
3289         m_testCtx.getLog() << tcu::TestLog::Message << "Verifying work buffer, it should be " << m_numCalls * m_workSize
3290                            << tcu::TestLog::EndMessage;
3291 
3292         gl.bindBuffer(GL_ATOMIC_COUNTER_BUFFER, m_bufferID);
3293         result = readBufferUint32(gl, GL_ATOMIC_COUNTER_BUFFER);
3294 
3295         if ((int)result != m_numCalls * m_workSize)
3296         {
3297             m_testCtx.getLog() << tcu::TestLog::Message << "Buffer value error, expected value "
3298                                << (m_numCalls * m_workSize) << ", got " << result << "\n"
3299                                << tcu::TestLog::EndMessage;
3300 
3301             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Buffer contents invalid");
3302             return STOP;
3303         }
3304 
3305         m_testCtx.getLog() << tcu::TestLog::Message << "Buffer is valid." << tcu::TestLog::EndMessage;
3306     }
3307 
3308     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3309     return STOP;
3310 }
3311 
genSSBOComputeSource(void) const3312 std::string ConcurrentSSBOAtomicCounterMixedCase::genSSBOComputeSource(void) const
3313 {
3314     std::ostringstream buf;
3315 
3316     buf << "${GLSL_VERSION_DECL}\n"
3317         << "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
3318         << "layout (binding = 1, std430) volatile buffer WorkBuffer\n"
3319         << "{\n"
3320         << "    highp uint targetValue;\n"
3321         << "    highp uint unused;\n"
3322         << "} sb_work;\n"
3323         << "\n"
3324         << "void main ()\n"
3325         << "{\n"
3326         << "    // flip high bits\n"
3327         << "    highp uint mask = uint(1) << (24u + (gl_GlobalInvocationID.x % 8u));\n"
3328         << "    sb_work.unused = atomicXor(sb_work.targetValue, mask);\n"
3329         << "}";
3330 
3331     return specializeShader(m_context, buf.str().c_str());
3332 }
3333 
genAtomicCounterComputeSource(void) const3334 std::string ConcurrentSSBOAtomicCounterMixedCase::genAtomicCounterComputeSource(void) const
3335 {
3336     std::ostringstream buf;
3337 
3338     buf << "${GLSL_VERSION_DECL}\n"
3339         << "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
3340         << "\n"
3341         << "layout (binding = 0, offset = 0) uniform atomic_uint u_counter;\n"
3342         << "\n"
3343         << "void main ()\n"
3344         << "{\n"
3345         << "    atomicCounterIncrement(u_counter);\n"
3346         << "}";
3347 
3348     return specializeShader(m_context, buf.str().c_str());
3349 }
3350 
3351 } // namespace
3352 
SynchronizationTests(Context & context)3353 SynchronizationTests::SynchronizationTests(Context &context)
3354     : TestCaseGroup(context, "synchronization", "Synchronization tests")
3355 {
3356 }
3357 
~SynchronizationTests(void)3358 SynchronizationTests::~SynchronizationTests(void)
3359 {
3360 }
3361 
init(void)3362 void SynchronizationTests::init(void)
3363 {
3364     tcu::TestCaseGroup *const inInvocationGroup =
3365         new tcu::TestCaseGroup(m_testCtx, "in_invocation", "Test intra-invocation synchronization");
3366     tcu::TestCaseGroup *const interInvocationGroup =
3367         new tcu::TestCaseGroup(m_testCtx, "inter_invocation", "Test inter-invocation synchronization");
3368     tcu::TestCaseGroup *const interCallGroup =
3369         new tcu::TestCaseGroup(m_testCtx, "inter_call", "Test inter-call synchronization");
3370 
3371     addChild(inInvocationGroup);
3372     addChild(interInvocationGroup);
3373     addChild(interCallGroup);
3374 
3375     // .in_invocation & .inter_invocation
3376     {
3377         static const struct CaseConfig
3378         {
3379             const char *namePrefix;
3380             const InterInvocationTestCase::StorageType storage;
3381             const int flags;
3382         } configs[] = {
3383             {"image", InterInvocationTestCase::STORAGE_IMAGE, 0},
3384             {"image_atomic", InterInvocationTestCase::STORAGE_IMAGE, InterInvocationTestCase::FLAG_ATOMIC},
3385             {"ssbo", InterInvocationTestCase::STORAGE_BUFFER, 0},
3386             {"ssbo_atomic", InterInvocationTestCase::STORAGE_BUFFER, InterInvocationTestCase::FLAG_ATOMIC},
3387         };
3388 
3389         for (int groupNdx = 0; groupNdx < 2; ++groupNdx)
3390         {
3391             tcu::TestCaseGroup *const targetGroup = (groupNdx == 0) ? (inInvocationGroup) : (interInvocationGroup);
3392             const int extraFlags                  = (groupNdx == 0) ? (0) : (InterInvocationTestCase::FLAG_IN_GROUP);
3393 
3394             for (int configNdx = 0; configNdx < DE_LENGTH_OF_ARRAY(configs); ++configNdx)
3395             {
3396                 const char *const target =
3397                     (configs[configNdx].storage == InterInvocationTestCase::STORAGE_BUFFER) ? ("buffer") : ("image");
3398 
3399                 targetGroup->addChild(new InvocationWriteReadCase(
3400                     m_context, (std::string(configs[configNdx].namePrefix) + "_write_read").c_str(),
3401                     (std::string("Write to ") + target + " and read it").c_str(), configs[configNdx].storage,
3402                     configs[configNdx].flags | extraFlags));
3403 
3404                 targetGroup->addChild(new InvocationReadWriteCase(
3405                     m_context, (std::string(configs[configNdx].namePrefix) + "_read_write").c_str(),
3406                     (std::string("Read form ") + target + " and then write to it").c_str(), configs[configNdx].storage,
3407                     configs[configNdx].flags | extraFlags));
3408 
3409                 targetGroup->addChild(new InvocationOverWriteCase(
3410                     m_context, (std::string(configs[configNdx].namePrefix) + "_overwrite").c_str(),
3411                     (std::string("Write to ") + target + " twice and read it").c_str(), configs[configNdx].storage,
3412                     configs[configNdx].flags | extraFlags));
3413 
3414                 targetGroup->addChild(new InvocationAliasWriteCase(
3415                     m_context, (std::string(configs[configNdx].namePrefix) + "_alias_write").c_str(),
3416                     (std::string("Write to aliasing ") + target + " and read it").c_str(),
3417                     InvocationAliasWriteCase::TYPE_WRITE, configs[configNdx].storage,
3418                     configs[configNdx].flags | extraFlags));
3419 
3420                 targetGroup->addChild(new InvocationAliasWriteCase(
3421                     m_context, (std::string(configs[configNdx].namePrefix) + "_alias_overwrite").c_str(),
3422                     (std::string("Write to aliasing ") + target + "s and read it").c_str(),
3423                     InvocationAliasWriteCase::TYPE_OVERWRITE, configs[configNdx].storage,
3424                     configs[configNdx].flags | extraFlags));
3425             }
3426         }
3427     }
3428 
3429     // .inter_call
3430     {
3431         tcu::TestCaseGroup *const withBarrierGroup =
3432             new tcu::TestCaseGroup(m_testCtx, "with_memory_barrier", "Synchronize with memory barrier");
3433         tcu::TestCaseGroup *const withoutBarrierGroup =
3434             new tcu::TestCaseGroup(m_testCtx, "without_memory_barrier", "Synchronize without memory barrier");
3435 
3436         interCallGroup->addChild(withBarrierGroup);
3437         interCallGroup->addChild(withoutBarrierGroup);
3438 
3439         // .with_memory_barrier
3440         {
3441             static const struct CaseConfig
3442             {
3443                 const char *namePrefix;
3444                 const InterCallTestCase::StorageType storage;
3445                 const int flags;
3446             } configs[] = {
3447                 {"image", InterCallTestCase::STORAGE_IMAGE, 0},
3448                 {"image_atomic", InterCallTestCase::STORAGE_IMAGE,
3449                  InterCallTestCase::FLAG_USE_ATOMIC | InterCallTestCase::FLAG_USE_INT},
3450                 {"ssbo", InterCallTestCase::STORAGE_BUFFER, 0},
3451                 {"ssbo_atomic", InterCallTestCase::STORAGE_BUFFER,
3452                  InterCallTestCase::FLAG_USE_ATOMIC | InterCallTestCase::FLAG_USE_INT},
3453             };
3454 
3455             const int seed0 = 123;
3456             const int seed1 = 457;
3457 
3458             for (int configNdx = 0; configNdx < DE_LENGTH_OF_ARRAY(configs); ++configNdx)
3459             {
3460                 const char *const target =
3461                     (configs[configNdx].storage == InterCallTestCase::STORAGE_BUFFER) ? ("buffer") : ("image");
3462 
3463                 withBarrierGroup->addChild(new InterCallTestCase(
3464                     m_context, (std::string(configs[configNdx].namePrefix) + "_write_read").c_str(),
3465                     (std::string("Write to ") + target + " and read it").c_str(), configs[configNdx].storage,
3466                     configs[configNdx].flags,
3467                     InterCallOperations()
3468                         << op::WriteData::Generate(1, seed0) << op::Barrier() << op::ReadData::Generate(1, seed0)));
3469 
3470                 withBarrierGroup->addChild(new InterCallTestCase(
3471                     m_context, (std::string(configs[configNdx].namePrefix) + "_read_write").c_str(),
3472                     (std::string("Read from ") + target + " and then write to it").c_str(), configs[configNdx].storage,
3473                     configs[configNdx].flags,
3474                     InterCallOperations()
3475                         << op::ReadZeroData::Generate(1) << op::Barrier() << op::WriteData::Generate(1, seed0)));
3476 
3477                 withBarrierGroup->addChild(new InterCallTestCase(
3478                     m_context, (std::string(configs[configNdx].namePrefix) + "_overwrite").c_str(),
3479                     (std::string("Write to ") + target + " twice and read it").c_str(), configs[configNdx].storage,
3480                     configs[configNdx].flags,
3481                     InterCallOperations()
3482                         << op::WriteData::Generate(1, seed0) << op::Barrier() << op::WriteData::Generate(1, seed1)
3483                         << op::Barrier() << op::ReadData::Generate(1, seed1)));
3484 
3485                 withBarrierGroup->addChild(new InterCallTestCase(
3486                     m_context, (std::string(configs[configNdx].namePrefix) + "_multiple_write_read").c_str(),
3487                     (std::string("Write to multiple ") + target + "s and read them").c_str(),
3488                     configs[configNdx].storage, configs[configNdx].flags,
3489                     InterCallOperations() << op::WriteData::Generate(1, seed0) << op::WriteData::Generate(2, seed1)
3490                                           << op::Barrier() << op::ReadMultipleData::Generate(1, seed0, 2, seed1)));
3491 
3492                 withBarrierGroup->addChild(new InterCallTestCase(
3493                     m_context,
3494                     (std::string(configs[configNdx].namePrefix) + "_multiple_interleaved_write_read").c_str(),
3495                     (std::string("Write to same ") + target + " in multiple calls and read it").c_str(),
3496                     configs[configNdx].storage, configs[configNdx].flags,
3497                     InterCallOperations() << op::WriteDataInterleaved::Generate(1, seed0, true)
3498                                           << op::WriteDataInterleaved::Generate(1, seed1, false) << op::Barrier()
3499                                           << op::ReadDataInterleaved::Generate(1, seed0, seed1)));
3500 
3501                 withBarrierGroup->addChild(new InterCallTestCase(
3502                     m_context,
3503                     (std::string(configs[configNdx].namePrefix) + "_multiple_unrelated_write_read_ordered").c_str(),
3504                     (std::string("Two unrelated ") + target + " write-reads").c_str(), configs[configNdx].storage,
3505                     configs[configNdx].flags,
3506                     InterCallOperations()
3507                         << op::WriteData::Generate(1, seed0) << op::WriteData::Generate(2, seed1) << op::Barrier()
3508                         << op::ReadData::Generate(1, seed0) << op::ReadData::Generate(2, seed1)));
3509 
3510                 withBarrierGroup->addChild(new InterCallTestCase(
3511                     m_context,
3512                     (std::string(configs[configNdx].namePrefix) + "_multiple_unrelated_write_read_non_ordered").c_str(),
3513                     (std::string("Two unrelated ") + target + " write-reads").c_str(), configs[configNdx].storage,
3514                     configs[configNdx].flags,
3515                     InterCallOperations()
3516                         << op::WriteData::Generate(1, seed0) << op::WriteData::Generate(2, seed1) << op::Barrier()
3517                         << op::ReadData::Generate(2, seed1) << op::ReadData::Generate(1, seed0)));
3518             }
3519 
3520             // .without_memory_barrier
3521             {
3522                 struct InvocationConfig
3523                 {
3524                     const char *name;
3525                     int count;
3526                 };
3527 
3528                 static const InvocationConfig ssboInvocations[] = {
3529                     {"1k", 1024},
3530                     {"4k", 4096},
3531                     {"32k", 32768},
3532                 };
3533                 static const InvocationConfig imageInvocations[] = {
3534                     {"8x8", 8},
3535                     {"32x32", 32},
3536                     {"128x128", 128},
3537                 };
3538                 static const InvocationConfig counterInvocations[] = {
3539                     {"32", 32},
3540                     {"128", 128},
3541                     {"1k", 1024},
3542                 };
3543                 static const int callCounts[] = {2, 5, 100};
3544 
3545                 for (int invocationNdx = 0; invocationNdx < DE_LENGTH_OF_ARRAY(ssboInvocations); ++invocationNdx)
3546                     for (int callCountNdx = 0; callCountNdx < DE_LENGTH_OF_ARRAY(callCounts); ++callCountNdx)
3547                         withoutBarrierGroup->addChild(new SSBOConcurrentAtomicCase(
3548                             m_context,
3549                             (std::string("ssbo_atomic_dispatch_") + de::toString(callCounts[callCountNdx]) + "_calls_" +
3550                              ssboInvocations[invocationNdx].name + "_invocations")
3551                                 .c_str(),
3552                             "", callCounts[callCountNdx], ssboInvocations[invocationNdx].count));
3553 
3554                 for (int invocationNdx = 0; invocationNdx < DE_LENGTH_OF_ARRAY(imageInvocations); ++invocationNdx)
3555                     for (int callCountNdx = 0; callCountNdx < DE_LENGTH_OF_ARRAY(callCounts); ++callCountNdx)
3556                         withoutBarrierGroup->addChild(new ConcurrentImageAtomicCase(
3557                             m_context,
3558                             (std::string("image_atomic_dispatch_") + de::toString(callCounts[callCountNdx]) +
3559                              "_calls_" + imageInvocations[invocationNdx].name + "_invocations")
3560                                 .c_str(),
3561                             "", callCounts[callCountNdx], imageInvocations[invocationNdx].count));
3562 
3563                 for (int invocationNdx = 0; invocationNdx < DE_LENGTH_OF_ARRAY(counterInvocations); ++invocationNdx)
3564                     for (int callCountNdx = 0; callCountNdx < DE_LENGTH_OF_ARRAY(callCounts); ++callCountNdx)
3565                         withoutBarrierGroup->addChild(new ConcurrentAtomicCounterCase(
3566                             m_context,
3567                             (std::string("atomic_counter_dispatch_") + de::toString(callCounts[callCountNdx]) +
3568                              "_calls_" + counterInvocations[invocationNdx].name + "_invocations")
3569                                 .c_str(),
3570                             "", callCounts[callCountNdx], counterInvocations[invocationNdx].count));
3571 
3572                 for (int invocationNdx = 0; invocationNdx < DE_LENGTH_OF_ARRAY(counterInvocations); ++invocationNdx)
3573                     for (int callCountNdx = 0; callCountNdx < DE_LENGTH_OF_ARRAY(callCounts); ++callCountNdx)
3574                         withoutBarrierGroup->addChild(new ConcurrentSSBOAtomicCounterMixedCase(
3575                             m_context,
3576                             (std::string("ssbo_atomic_counter_mixed_dispatch_") +
3577                              de::toString(callCounts[callCountNdx]) + "_calls_" +
3578                              counterInvocations[invocationNdx].name + "_invocations")
3579                                 .c_str(),
3580                             "", callCounts[callCountNdx], counterInvocations[invocationNdx].count));
3581             }
3582         }
3583     }
3584 }
3585 
3586 } // namespace Functional
3587 } // namespace gles31
3588 } // namespace deqp
3589