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