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 Basic Compute Shader Tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es31fAtomicCounterTests.hpp"
25 
26 #include "gluShaderProgram.hpp"
27 #include "gluObjectWrapper.hpp"
28 #include "gluRenderContext.hpp"
29 
30 #include "glwFunctions.hpp"
31 #include "glwEnums.hpp"
32 
33 #include "tcuTestLog.hpp"
34 
35 #include "deStringUtil.hpp"
36 #include "deRandom.hpp"
37 #include "deMemory.h"
38 
39 #include <vector>
40 #include <string>
41 
42 using namespace glw;
43 using tcu::TestLog;
44 
45 using std::vector;
46 using std::string;
47 
48 namespace deqp
49 {
50 namespace gles31
51 {
52 namespace Functional
53 {
54 namespace
55 {
56 
57 class AtomicCounterTest : public TestCase
58 {
59 public:
60 	enum Operation
61 	{
62 		OPERATION_INC = (1<<0),
63 		OPERATION_DEC = (1<<1),
64 		OPERATION_GET = (1<<2)
65 	};
66 
67 	enum OffsetType
68 	{
69 		OFFSETTYPE_NONE = 0,
70 		OFFSETTYPE_BASIC,
71 		OFFSETTYPE_REVERSE,
72 		OFFSETTYPE_FIRST_AUTO,
73 		OFFSETTYPE_DEFAULT_AUTO,
74 		OFFSETTYPE_RESET_DEFAULT,
75 		OFFSETTYPE_INVALID,
76 		OFFSETTYPE_INVALID_OVERLAPPING,
77 		OFFSETTYPE_INVALID_DEFAULT
78 	};
79 
80 	enum BindingType
81 	{
82 		BINDINGTYPE_BASIC = 0,
83 		BINDINGTYPE_INVALID,
84 		BINDINGTYPE_INVALID_DEFAULT
85 	};
86 
87 	struct TestSpec
88 	{
TestSpecdeqp::gles31::Functional::__anona40c26350111::AtomicCounterTest::TestSpec89 		TestSpec (void)
90 			: atomicCounterCount	(0)
91 			, operations			((Operation)0)
92 			, callCount				(0)
93 			, useBranches			(false)
94 			, threadCount			(0)
95 			, offsetType			(OFFSETTYPE_NONE)
96 			, bindingType			(BINDINGTYPE_BASIC)
97 		{
98 		}
99 
100 		int			atomicCounterCount;
101 		Operation	operations;
102 		int			callCount;
103 		bool		useBranches;
104 		int			threadCount;
105 		OffsetType	offsetType;
106 		BindingType	bindingType;
107 	};
108 
109 						AtomicCounterTest		(Context& context, const char* name, const char* description, const TestSpec& spec);
110 						~AtomicCounterTest		(void);
111 
112 	void				init						(void);
113 	void				deinit						(void);
114 	IterateResult		iterate						(void);
115 
116 private:
117 	const TestSpec		m_spec;
118 
119 	bool				checkAndLogCounterValues	(TestLog& log, const vector<deUint32>& counters) const;
120 	bool				checkAndLogCallValues		(TestLog& log, const vector<deUint32>& increments, const vector<deUint32>& decrements, const vector<deUint32>& preGets, const vector<deUint32>& postGets, const vector<deUint32>& gets) const;
121 	void				splitBuffer					(const vector<deUint32>& buffer, vector<deUint32>& increments, vector<deUint32>& decrements, vector<deUint32>& preGets, vector<deUint32>& postGets, vector<deUint32>& gets) const;
getInitialValue(void) const122 	deUint32			getInitialValue				(void) const { return m_spec.callCount * m_spec.threadCount + 1; }
123 
124 	static string		generateShaderSource		(const TestSpec& spec);
125 	static void			getCountersValues			(vector<deUint32>& counterValues, const vector<deUint32>& values, int ndx, int counterCount);
126 	static bool			checkRange					(TestLog& log, const vector<deUint32>& values, const vector<deUint32>& min, const vector<deUint32>& max);
127 	static bool			checkUniquenessAndLinearity	(TestLog& log, const vector<deUint32>& values);
128 	static bool			checkPath					(const vector<deUint32>& increments, const vector<deUint32>& decrements, int initialValue, const TestSpec& spec);
129 
130 	int					getOperationCount			(void) const;
131 
132 	AtomicCounterTest&	operator=					(const AtomicCounterTest&);
133 						AtomicCounterTest			(const AtomicCounterTest&);
134 };
135 
getOperationCount(void) const136 int AtomicCounterTest::getOperationCount (void) const
137 {
138 	int count = 0;
139 
140 	if (m_spec.operations & OPERATION_INC)
141 		count++;
142 
143 	if (m_spec.operations & OPERATION_DEC)
144 		count++;
145 
146 	if (m_spec.operations == OPERATION_GET)
147 		count++;
148 	else if (m_spec.operations & OPERATION_GET)
149 		count += 2;
150 
151 	return count;
152 }
153 
AtomicCounterTest(Context & context,const char * name,const char * description,const TestSpec & spec)154 AtomicCounterTest::AtomicCounterTest (Context& context, const char* name, const char* description, const TestSpec& spec)
155 	: TestCase	(context, name, description)
156 	, m_spec	(spec)
157 {
158 }
159 
~AtomicCounterTest(void)160 AtomicCounterTest::~AtomicCounterTest (void)
161 {
162 }
163 
init(void)164 void AtomicCounterTest::init (void)
165 {
166 }
167 
deinit(void)168 void AtomicCounterTest::deinit (void)
169 {
170 }
171 
generateShaderSource(const TestSpec & spec)172 string AtomicCounterTest::generateShaderSource (const TestSpec& spec)
173 {
174 	std::ostringstream src;
175 
176 	src
177 		<<  "#version 310 es\n"
178 		<< "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n";
179 
180 	{
181 		bool wroteLayout = false;
182 
183 		switch (spec.bindingType)
184 		{
185 			case BINDINGTYPE_INVALID_DEFAULT:
186 				src << "layout(binding=10000";
187 				wroteLayout = true;
188 				break;
189 
190 			default:
191 				// Do nothing
192 				break;
193 		}
194 
195 		switch (spec.offsetType)
196 		{
197 			case OFFSETTYPE_DEFAULT_AUTO:
198 				if (!wroteLayout)
199 					src << "layout(binding=1, ";
200 				else
201 					src << ", ";
202 
203 				src << "offset=4";
204 				wroteLayout = true;
205 				break;
206 
207 			case OFFSETTYPE_RESET_DEFAULT:
208 				DE_ASSERT(spec.atomicCounterCount > 2);
209 
210 				if (!wroteLayout)
211 					src << "layout(binding=1, ";
212 				else
213 					src << ", ";
214 
215 				src << "offset=" << (4 * spec.atomicCounterCount/2);
216 				wroteLayout = true;
217 				break;
218 
219 			case OFFSETTYPE_INVALID_DEFAULT:
220 				if (!wroteLayout)
221 					src << "layout(binding=1, ";
222 				else
223 					src << ", ";
224 
225 				src << "offset=1";
226 				wroteLayout = true;
227 				break;
228 
229 			default:
230 				// Do nothing
231 				break;
232 		}
233 
234 		if (wroteLayout)
235 			src << ") uniform atomic_uint;\n";
236 	}
237 
238 	src
239 	<< "layout(binding = 1, std430) buffer Output {\n";
240 
241 	if ((spec.operations & OPERATION_GET) != 0 && spec.operations != OPERATION_GET)
242 		src << "	uint preGet[" << spec.threadCount * spec.atomicCounterCount * spec.callCount << "];\n";
243 
244 	if ((spec.operations & OPERATION_INC) != 0)
245 		src << "	uint increment[" << spec.threadCount * spec.atomicCounterCount * spec.callCount << "];\n";
246 
247 	if ((spec.operations & OPERATION_DEC) != 0)
248 		src << "	uint decrement[" << spec.threadCount * spec.atomicCounterCount * spec.callCount << "];\n";
249 
250 	if ((spec.operations & OPERATION_GET) != 0 && spec.operations != OPERATION_GET)
251 		src << "	uint postGet[" << spec.threadCount * spec.atomicCounterCount * spec.callCount << "];\n";
252 
253 	if (spec.operations == OPERATION_GET)
254 		src << "	uint get[" << spec.threadCount * spec.atomicCounterCount * spec.callCount << "];\n";
255 
256 	src << "} sb_in;\n\n";
257 
258 	for (int counterNdx = 0; counterNdx < spec.atomicCounterCount; counterNdx++)
259 	{
260 		bool layoutStarted = false;
261 
262 		if (spec.offsetType == OFFSETTYPE_RESET_DEFAULT && counterNdx == spec.atomicCounterCount/2)
263 			src << "layout(binding=1, offset=0) uniform atomic_uint;\n";
264 
265 		switch (spec.bindingType)
266 		{
267 			case BINDINGTYPE_BASIC:
268 				layoutStarted = true;
269 				src << "layout(binding=1";
270 				break;
271 
272 			case BINDINGTYPE_INVALID:
273 				layoutStarted = true;
274 				src << "layout(binding=10000";
275 				break;
276 
277 			case BINDINGTYPE_INVALID_DEFAULT:
278 				// Nothing
279 				break;
280 
281 			default:
282 				DE_ASSERT(false);
283 		}
284 
285 		switch (spec.offsetType)
286 		{
287 			case OFFSETTYPE_NONE:
288 				if (layoutStarted)
289 					src << ") ";
290 
291 				src << "uniform atomic_uint counter" << counterNdx << ";\n";
292 
293 				break;
294 
295 			case OFFSETTYPE_BASIC:
296 				if (!layoutStarted)
297 					src << "layout(";
298 				else
299 					src << ", ";
300 
301 				src << "offset=" << (counterNdx * 4) << ") uniform atomic_uint counter" << counterNdx << ";\n";
302 
303 				break;
304 
305 			case OFFSETTYPE_INVALID_DEFAULT:
306 				if (layoutStarted)
307 					src << ") ";
308 
309 				src << "uniform atomic_uint counter" << counterNdx << ";\n";
310 
311 				break;
312 
313 			case OFFSETTYPE_INVALID:
314 				if (!layoutStarted)
315 					src << "layout(";
316 				else
317 					src << ", ";
318 
319 				src << "offset=" << (1 + counterNdx * 2) << ") uniform atomic_uint counter" << counterNdx << ";\n";
320 
321 				break;
322 
323 			case OFFSETTYPE_INVALID_OVERLAPPING:
324 				if (!layoutStarted)
325 					src << "layout(";
326 				else
327 					src << ", ";
328 
329 				src << "offset=0) uniform atomic_uint counter" << counterNdx << ";\n";
330 
331 				break;
332 
333 			case OFFSETTYPE_REVERSE:
334 				if (!layoutStarted)
335 					src << "layout(";
336 				else
337 					src << ", ";
338 
339 				src << "offset=" << (spec.atomicCounterCount - counterNdx - 1) * 4 << ") uniform atomic_uint counter" << (spec.atomicCounterCount - counterNdx - 1) << ";\n";
340 
341 				break;
342 
343 			case OFFSETTYPE_FIRST_AUTO:
344 				DE_ASSERT(spec.atomicCounterCount > 2);
345 
346 				if (counterNdx + 1 == spec.atomicCounterCount)
347 				{
348 					if (!layoutStarted)
349 						src << "layout(";
350 					else
351 						src << ", ";
352 
353 					src << "offset=0) uniform atomic_uint counter0;\n";
354 				}
355 				else if (counterNdx == 0)
356 				{
357 					if (!layoutStarted)
358 						src << "layout(";
359 					else
360 						src << ", ";
361 
362 					src << "offset=4) uniform atomic_uint counter1;\n";
363 				}
364 				else
365 				{
366 					if (layoutStarted)
367 						src << ") ";
368 
369 					src << "uniform atomic_uint counter" << (counterNdx + 1) << ";\n";
370 				}
371 
372 				break;
373 
374 			case OFFSETTYPE_DEFAULT_AUTO:
375 				if (counterNdx + 1 == spec.atomicCounterCount)
376 				{
377 					if (!layoutStarted)
378 						src << "layout(";
379 					else
380 						src << ", ";
381 
382 					src << "offset=0) uniform atomic_uint counter0;\n";
383 				}
384 				else
385 				{
386 					if (layoutStarted)
387 						src << ") ";
388 
389 					src << "uniform atomic_uint counter" << (counterNdx + 1) << ";\n";
390 				}
391 
392 				break;
393 
394 			case OFFSETTYPE_RESET_DEFAULT:
395 				if (layoutStarted)
396 					src << ") ";
397 
398 				if (counterNdx < spec.atomicCounterCount/2)
399 					src << "uniform atomic_uint counter" << (counterNdx + spec.atomicCounterCount/2) << ";\n";
400 				else
401 					src << "uniform atomic_uint counter" << (counterNdx - spec.atomicCounterCount/2) << ";\n";
402 
403 				break;
404 
405 			default:
406 				DE_ASSERT(false);
407 		}
408 	}
409 
410 	src
411 	<< "\n"
412 	<< "void main (void)\n"
413 	<< "{\n";
414 
415 	if (spec.callCount > 1)
416 		src << "\tfor (uint i = 0u; i < " << spec.callCount << "u; i++)\n";
417 
418 	src
419 	<< "\t{\n"
420 	<< "\t\tuint id = (gl_GlobalInvocationID.x";
421 
422 	if (spec.callCount > 1)
423 		src << " * "<< spec.callCount << "u";
424 
425 	if (spec.callCount > 1)
426 		src << " + i)";
427 	else
428 		src << ")";
429 
430 	if  (spec.atomicCounterCount > 1)
431 		src << " * " << spec.atomicCounterCount << "u";
432 
433 	src << ";\n";
434 
435 	for (int counterNdx = 0; counterNdx < spec.atomicCounterCount; counterNdx++)
436 	{
437 		if ((spec.operations & OPERATION_GET) != 0 && spec.operations != OPERATION_GET)
438 			src << "\t\tsb_in.preGet[id + " << counterNdx << "u] = atomicCounter(counter" << counterNdx << ");\n";
439 
440 		if (spec.useBranches && ((spec.operations & (OPERATION_INC|OPERATION_DEC)) == (OPERATION_INC|OPERATION_DEC)))
441 		{
442 			src
443 			<< "\t\tif (((gl_GlobalInvocationID.x" << (spec.callCount > 1 ? " + i" : "") << ") % 2u) == 0u)\n"
444 			<< "\t\t{\n"
445 			<< "\t\t\tsb_in.increment[id + " << counterNdx << "u] = atomicCounterIncrement(counter" << counterNdx << ");\n"
446 			<< "\t\t\tsb_in.decrement[id + " << counterNdx << "u] = uint(-1);\n"
447 			<< "\t\t}\n"
448 			<< "\t\telse\n"
449 			<< "\t\t{\n"
450 			<< "\t\t\tsb_in.decrement[id + " << counterNdx << "u] = atomicCounterDecrement(counter" << counterNdx << ") + 1u;\n"
451 			<< "\t\t\tsb_in.increment[id + " << counterNdx << "u] = uint(-1);\n"
452 			<< "\t\t}\n";
453 		}
454 		else
455 		{
456 			if ((spec.operations & OPERATION_INC) != 0)
457 			{
458 				if (spec.useBranches)
459 				{
460 					src
461 					<< "\t\tif (((gl_GlobalInvocationID.x" << (spec.callCount > 1 ? " + i" : "") << ") % 2u) == 0u)\n"
462 					<< "\t\t{\n"
463 					<< "\t\t\tsb_in.increment[id + " << counterNdx << "u] = atomicCounterIncrement(counter" << counterNdx << ");\n"
464 					<< "\t\t}\n"
465 					<< "\t\telse\n"
466 					<< "\t\t{\n"
467 					<< "\t\t\tsb_in.increment[id + " << counterNdx << "u] = uint(-1);\n"
468 					<< "\t\t}\n";
469 
470 				}
471 				else
472 					src << "\t\tsb_in.increment[id + " << counterNdx << "u] = atomicCounterIncrement(counter" << counterNdx << ");\n";
473 			}
474 
475 			if ((spec.operations & OPERATION_DEC) != 0)
476 			{
477 				if (spec.useBranches)
478 				{
479 					src
480 					<< "\t\tif (((gl_GlobalInvocationID.x" << (spec.callCount > 1 ? " + i" : "") << ") % 2u) == 0u)\n"
481 					<< "\t\t{\n"
482 					<< "\t\t\tsb_in.decrement[id + " << counterNdx << "u] = atomicCounterDecrement(counter" << counterNdx << ") + 1u;\n"
483 					<< "\t\t}\n"
484 					<< "\t\telse\n"
485 					<< "\t\t{\n"
486 					<< "\t\t\tsb_in.decrement[id + " << counterNdx << "u] = uint(-1);\n"
487 					<< "\t\t}\n";
488 
489 				}
490 				else
491 					src << "\t\tsb_in.decrement[id + " << counterNdx << "u] = atomicCounterDecrement(counter" << counterNdx << ") + 1u;\n";
492 			}
493 		}
494 
495 		if ((spec.operations & OPERATION_GET) != 0 && spec.operations != OPERATION_GET)
496 			src << "\t\tsb_in.postGet[id + " << counterNdx << "u] = atomicCounter(counter" << counterNdx << ");\n";
497 
498 		if ((spec.operations == OPERATION_GET) != 0)
499 		{
500 			if (spec.useBranches)
501 			{
502 				src
503 				<< "\t\tif (((gl_GlobalInvocationID.x" << (spec.callCount > 1 ? " + i" : "") << ") % 2u) == 0u)\n"
504 				<< "\t\t{\n"
505 				<< "\t\t\tsb_in.get[id + " << counterNdx << "u] = atomicCounter(counter" << counterNdx << ");\n"
506 				<< "\t\t}\n"
507 				<< "\t\telse\n"
508 				<< "\t\t{\n"
509 				<< "\t\t\tsb_in.get[id + " << counterNdx << "u] = uint(-1);\n"
510 				<< "\t\t}\n";
511 			}
512 			else
513 				src << "\t\tsb_in.get[id + " << counterNdx << "u] = atomicCounter(counter" << counterNdx << ");\n";
514 		}
515 	}
516 
517 	src
518 	<< "\t}\n"
519 	<< "}\n";
520 
521 	return src.str();
522 }
523 
checkAndLogCounterValues(TestLog & log,const vector<deUint32> & counters) const524 bool AtomicCounterTest::checkAndLogCounterValues (TestLog& log, const vector<deUint32>& counters) const
525 {
526 	tcu::ScopedLogSection	counterSection	(log, "Counter info", "Show initial value, current value and expected value of each counter.");
527 	bool					isOk			= true;
528 
529 	// Check that atomic counters have sensible results
530 	for (int counterNdx = 0; counterNdx < (int)counters.size(); counterNdx++)
531 	{
532 		const deUint32	value			= counters[counterNdx];
533 		const deUint32	initialValue	= getInitialValue();
534 		deUint32		expectedValue	= (deUint32)-1;
535 
536 		if ((m_spec.operations & OPERATION_INC) != 0 && (m_spec.operations & OPERATION_DEC) == 0)
537 			expectedValue = initialValue + (m_spec.useBranches ? m_spec.threadCount*m_spec.callCount - m_spec.threadCount*m_spec.callCount/2 : m_spec.threadCount*m_spec.callCount);
538 
539 		if ((m_spec.operations & OPERATION_INC) == 0 && (m_spec.operations & OPERATION_DEC) != 0)
540 			expectedValue = initialValue - (m_spec.useBranches ? m_spec.threadCount*m_spec.callCount - m_spec.threadCount*m_spec.callCount/2 : m_spec.threadCount*m_spec.callCount);
541 
542 		if ((m_spec.operations & OPERATION_INC) != 0 && (m_spec.operations & OPERATION_DEC) != 0)
543 			expectedValue = initialValue + (m_spec.useBranches ? m_spec.threadCount*m_spec.callCount - m_spec.threadCount*m_spec.callCount/2 : 0) - (m_spec.useBranches ? m_spec.threadCount*m_spec.callCount/2 : 0);
544 
545 		if ((m_spec.operations & OPERATION_INC) == 0 && (m_spec.operations & OPERATION_DEC) == 0)
546 			expectedValue = initialValue;
547 
548 		log << TestLog::Message << "atomic_uint counter" << counterNdx << " initial value: " << initialValue << ", value: " << value << ", expected: " << expectedValue << (value == expectedValue ? "" : ", failed!") << TestLog::EndMessage;
549 
550 		if (value != expectedValue)
551 			isOk = false;
552 	}
553 
554 	return isOk;
555 }
556 
splitBuffer(const vector<deUint32> & buffer,vector<deUint32> & increments,vector<deUint32> & decrements,vector<deUint32> & preGets,vector<deUint32> & postGets,vector<deUint32> & gets) const557 void AtomicCounterTest::splitBuffer (const vector<deUint32>& buffer, vector<deUint32>& increments, vector<deUint32>& decrements, vector<deUint32>& preGets, vector<deUint32>& postGets, vector<deUint32>& gets) const
558 {
559 	const int bufferValueCount	= m_spec.callCount * m_spec.threadCount * m_spec.atomicCounterCount;
560 
561 	int firstPreGet				= -1;
562 	int firstPostGet			= -1;
563 	int	firstGet				= -1;
564 	int firstInc				= -1;
565 	int firstDec				= -1;
566 
567 	increments.clear();
568 	decrements.clear();
569 	preGets.clear();
570 	postGets.clear();
571 	gets.clear();
572 
573 	if (m_spec.operations == OPERATION_GET)
574 		firstGet = 0;
575 	else if (m_spec.operations == OPERATION_INC)
576 		firstInc = 0;
577 	else if (m_spec.operations == OPERATION_DEC)
578 		firstDec = 0;
579 	else if (m_spec.operations == (OPERATION_GET|OPERATION_INC))
580 	{
581 		firstPreGet		= 0;
582 		firstInc		= bufferValueCount;
583 		firstPostGet	= bufferValueCount * 2;
584 	}
585 	else if (m_spec.operations == (OPERATION_GET|OPERATION_DEC))
586 	{
587 		firstPreGet		= 0;
588 		firstDec		= bufferValueCount;
589 		firstPostGet	= bufferValueCount * 2;
590 	}
591 	else if (m_spec.operations == (OPERATION_GET|OPERATION_DEC|OPERATION_INC))
592 	{
593 		firstPreGet		= 0;
594 		firstInc		= bufferValueCount;
595 		firstDec		= bufferValueCount * 2;
596 		firstPostGet	= bufferValueCount * 3;
597 	}
598 	else if (m_spec.operations == (OPERATION_DEC|OPERATION_INC))
599 	{
600 		firstInc		= 0;
601 		firstDec		= bufferValueCount;
602 	}
603 	else
604 		DE_ASSERT(false);
605 
606 	for (int threadNdx = 0; threadNdx < m_spec.threadCount; threadNdx++)
607 	{
608 		for (int callNdx = 0; callNdx < m_spec.callCount; callNdx++)
609 		{
610 			for (int counterNdx = 0; counterNdx < m_spec.atomicCounterCount; counterNdx++)
611 			{
612 				const int id = ((threadNdx * m_spec.callCount) + callNdx) * m_spec.atomicCounterCount + counterNdx;
613 
614 				if (firstInc != -1)
615 					increments.push_back(buffer[firstInc + id]);
616 
617 				if (firstDec != -1)
618 					decrements.push_back(buffer[firstDec + id]);
619 
620 				if (firstPreGet != -1)
621 					preGets.push_back(buffer[firstPreGet + id]);
622 
623 				if (firstPostGet != -1)
624 					postGets.push_back(buffer[firstPostGet + id]);
625 
626 				if (firstGet != -1)
627 					gets.push_back(buffer[firstGet + id]);
628 			}
629 		}
630 	}
631 }
632 
getCountersValues(vector<deUint32> & counterValues,const vector<deUint32> & values,int ndx,int counterCount)633 void AtomicCounterTest::getCountersValues (vector<deUint32>& counterValues, const vector<deUint32>& values, int ndx, int counterCount)
634 {
635 	counterValues.resize(values.size()/counterCount, 0);
636 
637 	DE_ASSERT(values.size() % counterCount == 0);
638 
639 	for (int valueNdx = 0; valueNdx < (int)counterValues.size(); valueNdx++)
640 		counterValues[valueNdx] = values[valueNdx * counterCount + ndx];
641 }
642 
checkRange(TestLog & log,const vector<deUint32> & values,const vector<deUint32> & min,const vector<deUint32> & max)643 bool AtomicCounterTest::checkRange (TestLog& log, const vector<deUint32>& values, const vector<deUint32>& min, const vector<deUint32>& max)
644 {
645 	int failedCount = 0;
646 
647 	DE_ASSERT(values.size() == min.size());
648 	DE_ASSERT(values.size() == max.size());
649 
650 	for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++)
651 	{
652 		if (values[valueNdx] != (deUint32)-1)
653 		{
654 			if (!deInRange32(values[valueNdx], min[valueNdx], max[valueNdx]))
655 			{
656 				if (failedCount < 20)
657 					log << TestLog::Message << "Value " << values[valueNdx] << " not in range [" << min[valueNdx] << ", " << max[valueNdx] << "]." << TestLog::EndMessage;
658 				failedCount++;
659 			}
660 		}
661 	}
662 
663 	if (failedCount > 20)
664 		log << TestLog::Message << "Number of values not in range: " << failedCount << ", displaying first 20 values." << TestLog::EndMessage;
665 
666 	return failedCount == 0;
667 }
668 
checkUniquenessAndLinearity(TestLog & log,const vector<deUint32> & values)669 bool AtomicCounterTest::checkUniquenessAndLinearity (TestLog& log, const vector<deUint32>& values)
670 {
671 	vector<deUint32>	counts;
672 	int					failedCount	= 0;
673 	deUint32			minValue	= (deUint32)-1;
674 	deUint32			maxValue	= 0;
675 
676 	DE_ASSERT(!values.empty());
677 
678 	for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++)
679 	{
680 		if (values[valueNdx] != (deUint32)-1)
681 		{
682 			minValue = std::min(minValue, values[valueNdx]);
683 			maxValue = std::max(maxValue, values[valueNdx]);
684 		}
685 	}
686 
687 	counts.resize(maxValue - minValue + 1, 0);
688 
689 	for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++)
690 	{
691 		if (values[valueNdx] != (deUint32)-1)
692 			counts[values[valueNdx] - minValue]++;
693 	}
694 
695 	for (int countNdx = 0; countNdx < (int)counts.size(); countNdx++)
696 	{
697 		if (counts[countNdx] != 1)
698 		{
699 			if (failedCount < 20)
700 				log << TestLog::Message << "Value " << (minValue + countNdx) << " is not unique. Returned " << counts[countNdx] << " times." << TestLog::EndMessage;
701 
702 			failedCount++;
703 		}
704 	}
705 
706 	if (failedCount > 20)
707 		log << TestLog::Message << "Number of values not unique: " << failedCount << ", displaying first 20 values." << TestLog::EndMessage;
708 
709 	return failedCount == 0;
710 }
711 
checkPath(const vector<deUint32> & increments,const vector<deUint32> & decrements,int initialValue,const TestSpec & spec)712 bool AtomicCounterTest::checkPath (const vector<deUint32>& increments, const vector<deUint32>& decrements, int initialValue, const TestSpec& spec)
713 {
714 	const deUint32		lastValue	= initialValue + (spec.useBranches ? spec.threadCount*spec.callCount - spec.threadCount*spec.callCount/2 : 0) - (spec.useBranches ? spec.threadCount*spec.callCount/2 : 0);
715 	bool				isOk		= true;
716 
717 	vector<deUint32>	incrementCounts;
718 	vector<deUint32>	decrementCounts;
719 
720 	deUint32			minValue = 0xFFFFFFFFu;
721 	deUint32			maxValue = 0;
722 
723 	for (int valueNdx = 0; valueNdx < (int)increments.size(); valueNdx++)
724 	{
725 		if (increments[valueNdx] != (deUint32)-1)
726 		{
727 			minValue = std::min(minValue, increments[valueNdx]);
728 			maxValue = std::max(maxValue, increments[valueNdx]);
729 		}
730 	}
731 
732 	for (int valueNdx = 0; valueNdx < (int)decrements.size(); valueNdx++)
733 	{
734 		if (decrements[valueNdx] != (deUint32)-1)
735 		{
736 			minValue = std::min(minValue, decrements[valueNdx]);
737 			maxValue = std::max(maxValue, decrements[valueNdx]);
738 		}
739 	}
740 
741 	minValue = std::min(minValue, (deUint32)initialValue);
742 	maxValue = std::max(maxValue, (deUint32)initialValue);
743 
744 	incrementCounts.resize(maxValue - minValue + 1, 0);
745 	decrementCounts.resize(maxValue - minValue + 1, 0);
746 
747 	for (int valueNdx = 0; valueNdx < (int)increments.size(); valueNdx++)
748 	{
749 		if (increments[valueNdx] != (deUint32)-1)
750 			incrementCounts[increments[valueNdx] - minValue]++;
751 	}
752 
753 	for (int valueNdx = 0; valueNdx < (int)decrements.size(); valueNdx++)
754 	{
755 		if (decrements[valueNdx] != (deUint32)-1)
756 			decrementCounts[decrements[valueNdx] - minValue]++;
757 	}
758 
759 	int pos = initialValue - minValue;
760 
761 	while (incrementCounts[pos] + decrementCounts[pos] != 0)
762 	{
763 		if (incrementCounts[pos] > 0 && pos >= (int)(lastValue - minValue))
764 		{
765 			// If can increment and incrementation would move us away from result value, increment
766 			incrementCounts[pos]--;
767 			pos++;
768 		}
769 		else if (decrementCounts[pos] > 0)
770 		{
771 			// If can, decrement
772 			decrementCounts[pos]--;
773 			pos--;
774 		}
775 		else if (incrementCounts[pos] > 0)
776 		{
777 			// If increment moves closer to result value and can't decrement, increment
778 			incrementCounts[pos]--;
779 			pos++;
780 		}
781 		else
782 			DE_ASSERT(false);
783 
784 		if (pos < 0 || pos >= (int)incrementCounts.size())
785 			break;
786 	}
787 
788 	if (minValue + pos != lastValue)
789 		isOk = false;
790 
791 	for (int valueNdx = 0; valueNdx < (int)incrementCounts.size(); valueNdx++)
792 	{
793 		if (incrementCounts[valueNdx] != 0)
794 			isOk = false;
795 	}
796 
797 	for (int valueNdx = 0; valueNdx < (int)decrementCounts.size(); valueNdx++)
798 	{
799 		if (decrementCounts[valueNdx] != 0)
800 			isOk = false;
801 	}
802 
803 	return isOk;
804 }
805 
checkAndLogCallValues(TestLog & log,const vector<deUint32> & increments,const vector<deUint32> & decrements,const vector<deUint32> & preGets,const vector<deUint32> & postGets,const vector<deUint32> & gets) const806 bool AtomicCounterTest::checkAndLogCallValues (TestLog& log, const vector<deUint32>& increments, const vector<deUint32>& decrements, const vector<deUint32>& preGets, const vector<deUint32>& postGets, const vector<deUint32>& gets) const
807 {
808 	bool isOk = true;
809 
810 	for (int counterNdx = 0; counterNdx < m_spec.atomicCounterCount; counterNdx++)
811 	{
812 		vector<deUint32> counterIncrements;
813 		vector<deUint32> counterDecrements;
814 		vector<deUint32> counterPreGets;
815 		vector<deUint32> counterPostGets;
816 		vector<deUint32> counterGets;
817 
818 		getCountersValues(counterIncrements,	increments,	counterNdx, m_spec.atomicCounterCount);
819 		getCountersValues(counterDecrements,	decrements,	counterNdx, m_spec.atomicCounterCount);
820 		getCountersValues(counterPreGets,		preGets,	counterNdx, m_spec.atomicCounterCount);
821 		getCountersValues(counterPostGets,		postGets,	counterNdx, m_spec.atomicCounterCount);
822 		getCountersValues(counterGets,			gets,		counterNdx, m_spec.atomicCounterCount);
823 
824 		if (m_spec.operations == OPERATION_GET)
825 		{
826 			tcu::ScopedLogSection valueCheck(log, ("counter" + de::toString(counterNdx) + " value check").c_str(), ("Check that counter" + de::toString(counterNdx) + " values haven't changed.").c_str());
827 			int changedValues = 0;
828 
829 			for (int valueNdx = 0; valueNdx < (int)gets.size(); valueNdx++)
830 			{
831 				if ((!m_spec.useBranches || gets[valueNdx] != (deUint32)-1) && gets[valueNdx] != getInitialValue())
832 				{
833 					if (changedValues < 20)
834 						log << TestLog::Message << "atomicCounter(counter" << counterNdx << ") returned " << gets[valueNdx] << " expected " << getInitialValue() << TestLog::EndMessage;
835 					isOk = false;
836 					changedValues++;
837 				}
838 			}
839 
840 			if (changedValues == 0)
841 				log << TestLog::Message << "All values returned by atomicCounter(counter" << counterNdx << ") match initial value " << getInitialValue() <<  "." << TestLog::EndMessage;
842 			else if (changedValues > 20)
843 				log << TestLog::Message << "Total number of invalid values returned by atomicCounter(counter" << counterNdx << ") " << changedValues << " displaying first 20 values." <<  TestLog::EndMessage;
844 		}
845 		else if ((m_spec.operations & (OPERATION_INC|OPERATION_DEC)) == (OPERATION_INC|OPERATION_DEC))
846 		{
847 			tcu::ScopedLogSection valueCheck(log, ("counter" + de::toString(counterNdx) + " path check").c_str(), ("Check that there is order in which counter" + de::toString(counterNdx) + " increments and decrements could have happened.").c_str());
848 			if (!checkPath(counterIncrements, counterDecrements, getInitialValue(), m_spec))
849 			{
850 				isOk = false;
851 				log << TestLog::Message << "No possible order of calls to atomicCounterIncrement(counter" << counterNdx << ") and atomicCounterDecrement(counter" << counterNdx << ") found." << TestLog::EndMessage;
852 			}
853 			else
854 				log << TestLog::Message << "Found possible order of calls to atomicCounterIncrement(counter" << counterNdx << ") and atomicCounterDecrement(counter" << counterNdx << ")." << TestLog::EndMessage;
855 		}
856 		else if ((m_spec.operations & OPERATION_INC) != 0)
857 		{
858 			{
859 				tcu::ScopedLogSection uniquenesCheck(log, ("counter" + de::toString(counterNdx) + " check uniqueness and linearity").c_str(), ("Check that counter" + de::toString(counterNdx) + " returned only unique and linear values.").c_str());
860 
861 				if (!checkUniquenessAndLinearity(log, counterIncrements))
862 				{
863 					isOk = false;
864 					log << TestLog::Message << "atomicCounterIncrement(counter" << counterNdx << ") returned non unique values." << TestLog::EndMessage;
865 				}
866 				else
867 					log << TestLog::Message << "atomicCounterIncrement(counter" << counterNdx << ") returned only unique values." << TestLog::EndMessage;
868 			}
869 
870 			if (isOk && ((m_spec.operations & OPERATION_GET) != 0))
871 			{
872 				tcu::ScopedLogSection uniquenesCheck(log, ("counter" + de::toString(counterNdx) + " check range").c_str(), ("Check that counter" + de::toString(counterNdx) + " returned only values values between previous and next atomicCounter(counter" + de::toString(counterNdx) + ").").c_str());
873 
874 				if (!checkRange(log, counterIncrements, counterPreGets, counterPostGets))
875 				{
876 					isOk = false;
877 					log << TestLog::Message << "atomicCounterIncrement(counter" << counterNdx << ") returned value that is not between previous and next call to atomicCounter(counter" << counterNdx << ")." << TestLog::EndMessage;
878 				}
879 				else
880 					log << TestLog::Message << "atomicCounterIncrement(counter" << counterNdx << ") returned only values between previous and next call to atomicCounter(counter" << counterNdx << ")." << TestLog::EndMessage;
881 			}
882 		}
883 		else if ((m_spec.operations & OPERATION_DEC) != 0)
884 		{
885 			{
886 				tcu::ScopedLogSection uniquenesCheck(log, ("counter" + de::toString(counterNdx) + " check uniqueness and linearity").c_str(), ("Check that counter" + de::toString(counterNdx) + " returned only unique and linear values.").c_str());
887 
888 				if (!checkUniquenessAndLinearity(log, counterDecrements))
889 				{
890 					isOk = false;
891 					log << TestLog::Message << "atomicCounterDecrement(counter" << counterNdx << ") returned non unique values." << TestLog::EndMessage;
892 				}
893 				else
894 					log << TestLog::Message << "atomicCounterDecrement(counter" << counterNdx << ") returned only unique values." << TestLog::EndMessage;
895 			}
896 
897 			if (isOk && ((m_spec.operations & OPERATION_GET) != 0))
898 			{
899 				tcu::ScopedLogSection uniquenesCheck(log, ("counter" + de::toString(counterNdx) + " check range").c_str(), ("Check that counter" + de::toString(counterNdx) + " returned only values values between previous and next atomicCounter(counter" + de::toString(counterNdx) + ".").c_str());
900 
901 				if (!checkRange(log, counterDecrements, counterPostGets, counterPreGets))
902 				{
903 					isOk = false;
904 					log << TestLog::Message << "atomicCounterDecrement(counter" << counterNdx << ") returned value that is not between previous and next call to atomicCounter(counter" << counterNdx << ")." << TestLog::EndMessage;
905 				}
906 				else
907 					log << TestLog::Message << "atomicCounterDecrement(counter" << counterNdx << ") returned only values between previous and next call to atomicCounter(counter" << counterNdx << ")." << TestLog::EndMessage;
908 			}
909 		}
910 	}
911 
912 	return isOk;
913 }
914 
iterate(void)915 TestCase::IterateResult AtomicCounterTest::iterate (void)
916 {
917 	const glw::Functions&		gl					= m_context.getRenderContext().getFunctions();
918 	TestLog&					log					= m_testCtx.getLog();
919 	const glu::Buffer			counterBuffer		(m_context.getRenderContext());
920 	const glu::Buffer			outputBuffer		(m_context.getRenderContext());
921 	const glu::ShaderProgram	program				(m_context.getRenderContext(), glu::ProgramSources() << glu::ShaderSource(glu::SHADERTYPE_COMPUTE, generateShaderSource(m_spec)));
922 
923 	const deInt32				counterBufferSize	= m_spec.atomicCounterCount * 4;
924 	const deInt32				ssoSize				= m_spec.atomicCounterCount * m_spec.callCount * m_spec.threadCount * 4 * getOperationCount();
925 
926 	log << program;
927 
928 	if (m_spec.offsetType == OFFSETTYPE_INVALID || m_spec.offsetType == OFFSETTYPE_INVALID_DEFAULT || m_spec.bindingType == BINDINGTYPE_INVALID || m_spec.bindingType == BINDINGTYPE_INVALID_DEFAULT || m_spec.offsetType == OFFSETTYPE_INVALID_OVERLAPPING)
929 	{
930 		if (program.isOk())
931 		{
932 			log << TestLog::Message << "Expected program to fail, but compilation passed." << TestLog::EndMessage;
933 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile succeeded");
934 			return STOP;
935 		}
936 		else
937 		{
938 			log << TestLog::Message << "Compilation failed as expected." << TestLog::EndMessage;
939 			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Compile failed");
940 			return STOP;
941 		}
942 	}
943 	else if (!program.isOk())
944 	{
945 		log << TestLog::Message << "Compile failed." << TestLog::EndMessage;
946 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile failed");
947 		return STOP;
948 	}
949 
950 	gl.useProgram(program.getProgram());
951 	GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram()");
952 
953 	// Create output buffer
954 	gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
955 	gl.bufferData(GL_SHADER_STORAGE_BUFFER, ssoSize, NULL, GL_STATIC_DRAW);
956 	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to create output buffer");
957 
958 	// Create atomic counter buffer
959 	{
960 		vector<deUint32> data(m_spec.atomicCounterCount, getInitialValue());
961 		gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *counterBuffer);
962 		gl.bufferData(GL_SHADER_STORAGE_BUFFER, counterBufferSize, &(data[0]), GL_STATIC_DRAW);
963 		GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to create buffer for atomic counters");
964 	}
965 
966 	// Bind output buffer
967 	gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, *outputBuffer);
968 	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to setup output buffer");
969 
970 	// Bind atomic counter buffer
971 	gl.bindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 1, *counterBuffer);
972 	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to setup atomic counter buffer");
973 
974 	// Dispath compute
975 	gl.dispatchCompute(m_spec.threadCount, 1, 1);
976 	GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute()");
977 
978 	gl.finish();
979 	GLU_EXPECT_NO_ERROR(gl.getError(), "glFinish()");
980 
981 	vector<deUint32> output(ssoSize/4, 0);
982 	vector<deUint32> counters(m_spec.atomicCounterCount, 0);
983 
984 	// Read back output buffer
985 	{
986 		gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
987 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()");
988 
989 		void* ptr = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, (GLsizeiptr)(output.size() * sizeof(deUint32)), GL_MAP_READ_BIT);
990 		GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
991 
992 		deMemcpy(&(output[0]), ptr, (int)output.size() * sizeof(deUint32));
993 
994 		if (!gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER))
995 		{
996 			GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer()");
997 			TCU_CHECK_MSG(false, "Mapped buffer corrupted");
998 		}
999 
1000 		gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
1001 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()");
1002 	}
1003 
1004 	// Read back counter buffer
1005 	{
1006 		gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *counterBuffer);
1007 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()");
1008 
1009 		void* ptr = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, (GLsizeiptr)(counters.size() * sizeof(deUint32)), GL_MAP_READ_BIT);
1010 		GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
1011 
1012 		deMemcpy(&(counters[0]), ptr, (int)counters.size() * sizeof(deUint32));
1013 
1014 		if (!gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER))
1015 		{
1016 			GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer()");
1017 			TCU_CHECK_MSG(false, "Mapped buffer corrupted");
1018 		}
1019 
1020 		gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
1021 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()");
1022 	}
1023 
1024 	bool isOk = true;
1025 
1026 	if (!checkAndLogCounterValues(log, counters))
1027 		isOk = false;
1028 
1029 	{
1030 		vector<deUint32> increments;
1031 		vector<deUint32> decrements;
1032 		vector<deUint32> preGets;
1033 		vector<deUint32> postGets;
1034 		vector<deUint32> gets;
1035 
1036 		splitBuffer(output, increments, decrements, preGets, postGets, gets);
1037 
1038 		if (!checkAndLogCallValues(log, increments, decrements, preGets, postGets, gets))
1039 			isOk = false;
1040 	}
1041 
1042 	if (isOk)
1043 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1044 	else
1045 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
1046 
1047 	return STOP;
1048 }
1049 
specToTestName(const AtomicCounterTest::TestSpec & spec)1050 string specToTestName (const AtomicCounterTest::TestSpec& spec)
1051 {
1052 	std::ostringstream stream;
1053 
1054 	stream << spec.atomicCounterCount	<< (spec.atomicCounterCount == 1 ? "_counter" : "_counters");
1055 	stream << "_" << spec.callCount		<< (spec.callCount == 1 ? "_call" : "_calls");
1056 	stream << "_" << spec.threadCount	<< (spec.threadCount == 1 ? "_thread" : "_threads");
1057 
1058 	return stream.str();
1059 }
1060 
specToTestDescription(const AtomicCounterTest::TestSpec & spec)1061 string specToTestDescription (const AtomicCounterTest::TestSpec& spec)
1062 {
1063 	std::ostringstream	stream;
1064 	bool				firstOperation = 0;
1065 
1066 	stream
1067 	<< "Test ";
1068 
1069 	if ((spec.operations & AtomicCounterTest::OPERATION_GET) != 0)
1070 	{
1071 		stream << "atomicCounter()";
1072 		firstOperation = false;
1073 	}
1074 
1075 	if ((spec.operations & AtomicCounterTest::OPERATION_INC) != 0)
1076 	{
1077 		if (!firstOperation)
1078 			stream << ", ";
1079 
1080 		stream << " atomicCounterIncrement()";
1081 		firstOperation = false;
1082 	}
1083 
1084 	if ((spec.operations & AtomicCounterTest::OPERATION_DEC) != 0)
1085 	{
1086 		if (!firstOperation)
1087 			stream << ", ";
1088 
1089 		stream << " atomicCounterDecrement()";
1090 		firstOperation = false;
1091 	}
1092 
1093 	stream << " calls with ";
1094 
1095 	if (spec.useBranches)
1096 		stream << " branches, ";
1097 
1098 	stream << spec.atomicCounterCount << " atomic counters, " << spec.callCount << " calls and " << spec.threadCount << " threads.";
1099 
1100 	return stream.str();
1101 }
1102 
operationToName(const AtomicCounterTest::Operation & operations,bool useBranch)1103 string operationToName (const AtomicCounterTest::Operation& operations, bool useBranch)
1104 {
1105 	std::ostringstream	stream;
1106 	bool				first = true;
1107 
1108 	if ((operations & AtomicCounterTest::OPERATION_GET) != 0)
1109 	{
1110 		stream << "get";
1111 		first = false;
1112 	}
1113 
1114 	if ((operations & AtomicCounterTest::OPERATION_INC) != 0)
1115 	{
1116 		if (!first)
1117 			stream << "_";
1118 
1119 		stream << "inc";
1120 		first = false;
1121 	}
1122 
1123 	if ((operations & AtomicCounterTest::OPERATION_DEC) != 0)
1124 	{
1125 		if (!first)
1126 			stream << "_";
1127 
1128 		stream << "dec";
1129 		first = false;
1130 	}
1131 
1132 	if (useBranch)
1133 		stream << "_branch";
1134 
1135 	return stream.str();
1136 }
1137 
operationToDescription(const AtomicCounterTest::Operation & operations,bool useBranch)1138 string operationToDescription (const AtomicCounterTest::Operation& operations, bool useBranch)
1139 {
1140 	std::ostringstream	stream;
1141 	bool				firstOperation = 0;
1142 
1143 	stream
1144 	<< "Test ";
1145 
1146 	if ((operations & AtomicCounterTest::OPERATION_GET) != 0)
1147 	{
1148 		stream << "atomicCounter()";
1149 		firstOperation = false;
1150 	}
1151 
1152 	if ((operations & AtomicCounterTest::OPERATION_INC) != 0)
1153 	{
1154 		if (!firstOperation)
1155 			stream << ", ";
1156 
1157 		stream << " atomicCounterIncrement()";
1158 		firstOperation = false;
1159 	}
1160 
1161 	if ((operations & AtomicCounterTest::OPERATION_DEC) != 0)
1162 	{
1163 		if (!firstOperation)
1164 			stream << ", ";
1165 
1166 		stream << " atomicCounterDecrement()";
1167 		firstOperation = false;
1168 	}
1169 
1170 
1171 	if (useBranch)
1172 		stream << " calls with branches.";
1173 	else
1174 		stream << ".";
1175 
1176 	return stream.str();
1177 }
1178 
layoutTypesToName(const AtomicCounterTest::BindingType & bindingType,const AtomicCounterTest::OffsetType & offsetType)1179 string layoutTypesToName (const AtomicCounterTest::BindingType& bindingType, const AtomicCounterTest::OffsetType& offsetType)
1180 {
1181 	std::ostringstream	stream;
1182 
1183 	switch (bindingType)
1184 	{
1185 		case AtomicCounterTest::BINDINGTYPE_BASIC:
1186 			// Nothing
1187 			break;
1188 
1189 		case AtomicCounterTest::BINDINGTYPE_INVALID:
1190 			stream << "invalid_binding";
1191 			break;
1192 
1193 		default:
1194 			DE_ASSERT(false);
1195 	}
1196 
1197 	if (bindingType != AtomicCounterTest::BINDINGTYPE_BASIC && offsetType != AtomicCounterTest::OFFSETTYPE_NONE)
1198 		stream << "_";
1199 
1200 	switch (offsetType)
1201 	{
1202 		case AtomicCounterTest::OFFSETTYPE_BASIC:
1203 			stream << "basic_offset";
1204 			break;
1205 
1206 		case AtomicCounterTest::OFFSETTYPE_REVERSE:
1207 			stream << "reverse_offset";
1208 			break;
1209 
1210 		case AtomicCounterTest::OFFSETTYPE_INVALID:
1211 			stream << "invalid_offset";
1212 			break;
1213 
1214 		case AtomicCounterTest::OFFSETTYPE_FIRST_AUTO:
1215 			stream << "first_offset_set";
1216 			break;
1217 
1218 		case AtomicCounterTest::OFFSETTYPE_DEFAULT_AUTO:
1219 			stream << "default_offset_set";
1220 			break;
1221 
1222 		case AtomicCounterTest::OFFSETTYPE_RESET_DEFAULT:
1223 			stream << "reset_default_offset";
1224 			break;
1225 
1226 		case AtomicCounterTest::OFFSETTYPE_NONE:
1227 			// Do nothing
1228 			break;
1229 
1230 		default:
1231 			DE_ASSERT(false);
1232 	}
1233 
1234 	return stream.str();
1235 }
1236 
layoutTypesToDesc(const AtomicCounterTest::BindingType & bindingType,const AtomicCounterTest::OffsetType & offsetType)1237 string layoutTypesToDesc (const AtomicCounterTest::BindingType& bindingType, const AtomicCounterTest::OffsetType& offsetType)
1238 {
1239 	std::ostringstream	stream;
1240 
1241 	switch (bindingType)
1242 	{
1243 		case AtomicCounterTest::BINDINGTYPE_BASIC:
1244 			stream << "Test using atomic counters with explicit layout bindings and";
1245 			break;
1246 
1247 		case AtomicCounterTest::BINDINGTYPE_INVALID:
1248 			stream << "Test using atomic counters with invalid explicit layout bindings and";
1249 			break;
1250 
1251 		case AtomicCounterTest::BINDINGTYPE_INVALID_DEFAULT:
1252 			stream << "Test using atomic counters with invalid default layout binding and";
1253 			break;
1254 
1255 		default:
1256 			DE_ASSERT(false);
1257 	}
1258 
1259 	switch (offsetType)
1260 	{
1261 		case AtomicCounterTest::OFFSETTYPE_NONE:
1262 			stream << " no explicit offsets.";
1263 			break;
1264 
1265 		case AtomicCounterTest::OFFSETTYPE_BASIC:
1266 			stream << "explicit continuos offsets.";
1267 			break;
1268 
1269 		case AtomicCounterTest::OFFSETTYPE_REVERSE:
1270 			stream << "reversed explicit offsets.";
1271 			break;
1272 
1273 		case AtomicCounterTest::OFFSETTYPE_INVALID:
1274 			stream << "invalid explicit offsets.";
1275 			break;
1276 
1277 		case AtomicCounterTest::OFFSETTYPE_FIRST_AUTO:
1278 			stream << "only first counter with explicit offset.";
1279 			break;
1280 
1281 		case AtomicCounterTest::OFFSETTYPE_DEFAULT_AUTO:
1282 			stream << "default offset.";
1283 			break;
1284 
1285 		case AtomicCounterTest::OFFSETTYPE_RESET_DEFAULT:
1286 			stream << "default offset specified twice.";
1287 			break;
1288 
1289 		default:
1290 			DE_ASSERT(false);
1291 	}
1292 
1293 	return stream.str();
1294 }
1295 
1296 } // Anonymous
1297 
AtomicCounterTests(Context & context)1298 AtomicCounterTests::AtomicCounterTests (Context& context)
1299 	: TestCaseGroup(context, "atomic_counter", "Atomic counter tests")
1300 {
1301 	// Runtime use tests
1302 	{
1303 		const int counterCounts[] =
1304 		{
1305 			1, 4, 8
1306 		};
1307 
1308 		const int callCounts[] =
1309 		{
1310 			1, 5, 100
1311 		};
1312 
1313 		const int threadCounts[] =
1314 		{
1315 			1, 10, 5000
1316 		};
1317 
1318 		const AtomicCounterTest::Operation operations[] =
1319 		{
1320 			AtomicCounterTest::OPERATION_GET,
1321 			AtomicCounterTest::OPERATION_INC,
1322 			AtomicCounterTest::OPERATION_DEC,
1323 
1324 			(AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_INC|AtomicCounterTest::OPERATION_GET),
1325 			(AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_DEC|AtomicCounterTest::OPERATION_GET),
1326 
1327 			(AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_INC|AtomicCounterTest::OPERATION_DEC),
1328 			(AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_INC|AtomicCounterTest::OPERATION_DEC|AtomicCounterTest::OPERATION_GET)
1329 		};
1330 
1331 		for (int operationNdx = 0; operationNdx < DE_LENGTH_OF_ARRAY(operations); operationNdx++)
1332 		{
1333 			const AtomicCounterTest::Operation operation = operations[operationNdx];
1334 
1335 			for (int branch = 0; branch < 2; branch++)
1336 			{
1337 				const bool useBranch = (branch == 1);
1338 
1339 				TestCaseGroup* operationGroup = new TestCaseGroup(m_context, operationToName(operation, useBranch).c_str(), operationToDescription(operation, useBranch).c_str());
1340 
1341 				for (int counterCountNdx = 0; counterCountNdx < DE_LENGTH_OF_ARRAY(counterCounts); counterCountNdx++)
1342 				{
1343 					const int counterCount = counterCounts[counterCountNdx];
1344 
1345 					for (int callCountNdx = 0; callCountNdx < DE_LENGTH_OF_ARRAY(callCounts); callCountNdx++)
1346 					{
1347 						const int callCount = callCounts[callCountNdx];
1348 
1349 						for (int threadCountNdx = 0; threadCountNdx < DE_LENGTH_OF_ARRAY(threadCounts); threadCountNdx++)
1350 						{
1351 							const int threadCount = threadCounts[threadCountNdx];
1352 
1353 							if (threadCount * callCount * counterCount > 10000)
1354 								continue;
1355 
1356 							if (useBranch && threadCount * callCount == 1)
1357 								continue;
1358 
1359 							AtomicCounterTest::TestSpec spec;
1360 
1361 							spec.atomicCounterCount = counterCount;
1362 							spec.operations			= operation;
1363 							spec.callCount			= callCount;
1364 							spec.useBranches		= useBranch;
1365 							spec.threadCount		= threadCount;
1366 							spec.bindingType		= AtomicCounterTest::BINDINGTYPE_BASIC;
1367 							spec.offsetType			= AtomicCounterTest::OFFSETTYPE_NONE;
1368 
1369 							operationGroup->addChild(new AtomicCounterTest(m_context, specToTestName(spec).c_str(), specToTestDescription(spec).c_str(), spec));
1370 						}
1371 					}
1372 				}
1373 
1374 				addChild(operationGroup);
1375 			}
1376 		}
1377 	}
1378 
1379 	{
1380 		TestCaseGroup* layoutGroup = new TestCaseGroup(m_context, "layout", "Layout qualifier tests.");
1381 
1382 		const int counterCounts[]	= { 1, 8 };
1383 		const int callCounts[]		= { 1, 5 };
1384 		const int threadCounts[]	= { 1, 1000 };
1385 
1386 		const AtomicCounterTest::Operation operations[] =
1387 		{
1388 			(AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_INC|AtomicCounterTest::OPERATION_GET),
1389 			(AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_DEC|AtomicCounterTest::OPERATION_GET),
1390 			(AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_INC|AtomicCounterTest::OPERATION_DEC)
1391 		};
1392 
1393 		const AtomicCounterTest::OffsetType offsetTypes[] =
1394 		{
1395 			AtomicCounterTest::OFFSETTYPE_REVERSE,
1396 			AtomicCounterTest::OFFSETTYPE_FIRST_AUTO,
1397 			AtomicCounterTest::OFFSETTYPE_DEFAULT_AUTO,
1398 			AtomicCounterTest::OFFSETTYPE_RESET_DEFAULT
1399 		};
1400 
1401 		for (int offsetTypeNdx = 0; offsetTypeNdx < DE_LENGTH_OF_ARRAY(offsetTypes); offsetTypeNdx++)
1402 		{
1403 			const AtomicCounterTest::OffsetType offsetType = offsetTypes[offsetTypeNdx];
1404 
1405 			TestCaseGroup* layoutQualifierGroup = new TestCaseGroup(m_context, layoutTypesToName(AtomicCounterTest::BINDINGTYPE_BASIC, offsetType).c_str(), layoutTypesToDesc(AtomicCounterTest::BINDINGTYPE_BASIC, offsetType).c_str());
1406 
1407 			for (int operationNdx = 0; operationNdx < DE_LENGTH_OF_ARRAY(operations); operationNdx++)
1408 			{
1409 				const AtomicCounterTest::Operation operation = operations[operationNdx];
1410 
1411 				TestCaseGroup* operationGroup = new TestCaseGroup(m_context, operationToName(operation, false).c_str(), operationToDescription(operation, false).c_str());
1412 
1413 				for (int counterCountNdx = 0; counterCountNdx < DE_LENGTH_OF_ARRAY(counterCounts); counterCountNdx++)
1414 				{
1415 					const int counterCount = counterCounts[counterCountNdx];
1416 
1417 					if (offsetType == AtomicCounterTest::OFFSETTYPE_FIRST_AUTO && counterCount < 3)
1418 						continue;
1419 
1420 					if (offsetType == AtomicCounterTest::OFFSETTYPE_DEFAULT_AUTO && counterCount < 2)
1421 						continue;
1422 
1423 					if (offsetType == AtomicCounterTest::OFFSETTYPE_RESET_DEFAULT && counterCount < 2)
1424 						continue;
1425 
1426 					if (offsetType == AtomicCounterTest::OFFSETTYPE_REVERSE && counterCount < 2)
1427 						continue;
1428 
1429 					for (int callCountNdx = 0; callCountNdx < DE_LENGTH_OF_ARRAY(callCounts); callCountNdx++)
1430 					{
1431 						const int callCount = callCounts[callCountNdx];
1432 
1433 						for (int threadCountNdx = 0; threadCountNdx < DE_LENGTH_OF_ARRAY(threadCounts); threadCountNdx++)
1434 						{
1435 							const int threadCount = threadCounts[threadCountNdx];
1436 
1437 							AtomicCounterTest::TestSpec spec;
1438 
1439 							spec.atomicCounterCount = counterCount;
1440 							spec.operations			= operation;
1441 							spec.callCount			= callCount;
1442 							spec.useBranches		= false;
1443 							spec.threadCount		= threadCount;
1444 							spec.bindingType		= AtomicCounterTest::BINDINGTYPE_BASIC;
1445 							spec.offsetType			= offsetType;
1446 
1447 							operationGroup->addChild(new AtomicCounterTest(m_context, specToTestName(spec).c_str(), specToTestDescription(spec).c_str(), spec));
1448 						}
1449 					}
1450 				}
1451 				layoutQualifierGroup->addChild(operationGroup);
1452 			}
1453 			layoutGroup->addChild(layoutQualifierGroup);
1454 		}
1455 
1456 		{
1457 			TestCaseGroup* invalidGroup = new TestCaseGroup(m_context, "invalid", "Test invalid layouts");
1458 
1459 			{
1460 				AtomicCounterTest::TestSpec spec;
1461 
1462 				spec.atomicCounterCount = 1;
1463 				spec.operations			= AtomicCounterTest::OPERATION_INC;
1464 				spec.callCount			= 1;
1465 				spec.useBranches		= false;
1466 				spec.threadCount		= 1;
1467 				spec.bindingType		= AtomicCounterTest::BINDINGTYPE_INVALID;
1468 				spec.offsetType			= AtomicCounterTest::OFFSETTYPE_NONE;
1469 
1470 				invalidGroup->addChild(new AtomicCounterTest(m_context, "invalid_binding", "Test layout qualifiers with invalid binding.", spec));
1471 			}
1472 
1473 			{
1474 				AtomicCounterTest::TestSpec spec;
1475 
1476 				spec.atomicCounterCount = 1;
1477 				spec.operations			= AtomicCounterTest::OPERATION_INC;
1478 				spec.callCount			= 1;
1479 				spec.useBranches		= false;
1480 				spec.threadCount		= 1;
1481 				spec.bindingType		= AtomicCounterTest::BINDINGTYPE_INVALID_DEFAULT;
1482 				spec.offsetType			= AtomicCounterTest::OFFSETTYPE_NONE;
1483 
1484 				invalidGroup->addChild(new AtomicCounterTest(m_context, "invalid_default_binding", "Test layout qualifiers with invalid default binding.", spec));
1485 			}
1486 
1487 			{
1488 				AtomicCounterTest::TestSpec spec;
1489 
1490 				spec.atomicCounterCount = 1;
1491 				spec.operations			= AtomicCounterTest::OPERATION_INC;
1492 				spec.callCount			= 1;
1493 				spec.useBranches		= false;
1494 				spec.threadCount		= 1;
1495 				spec.bindingType		= AtomicCounterTest::BINDINGTYPE_BASIC;
1496 				spec.offsetType			= AtomicCounterTest::OFFSETTYPE_INVALID;
1497 
1498 				invalidGroup->addChild(new AtomicCounterTest(m_context, "invalid_offset_align", "Test layout qualifiers with invalid alignment offset.", spec));
1499 			}
1500 
1501 			{
1502 				AtomicCounterTest::TestSpec spec;
1503 
1504 				spec.atomicCounterCount = 2;
1505 				spec.operations			= AtomicCounterTest::OPERATION_INC;
1506 				spec.callCount			= 1;
1507 				spec.useBranches		= false;
1508 				spec.threadCount		= 1;
1509 				spec.bindingType		= AtomicCounterTest::BINDINGTYPE_BASIC;
1510 				spec.offsetType			= AtomicCounterTest::OFFSETTYPE_INVALID_OVERLAPPING;
1511 
1512 				invalidGroup->addChild(new AtomicCounterTest(m_context, "invalid_offset_overlap", "Test layout qualifiers with invalid overlapping offset.", spec));
1513 			}
1514 
1515 			{
1516 				AtomicCounterTest::TestSpec spec;
1517 
1518 				spec.atomicCounterCount = 1;
1519 				spec.operations			= AtomicCounterTest::OPERATION_INC;
1520 				spec.callCount			= 1;
1521 				spec.useBranches		= false;
1522 				spec.threadCount		= 1;
1523 				spec.bindingType		= AtomicCounterTest::BINDINGTYPE_BASIC;
1524 				spec.offsetType			= AtomicCounterTest::OFFSETTYPE_INVALID_DEFAULT;
1525 
1526 				invalidGroup->addChild(new AtomicCounterTest(m_context, "invalid_default_offset", "Test layout qualifiers with invalid default offset.", spec));
1527 			}
1528 
1529 			layoutGroup->addChild(invalidGroup);
1530 		}
1531 
1532 		addChild(layoutGroup);
1533 	}
1534 }
1535 
~AtomicCounterTests(void)1536 AtomicCounterTests::~AtomicCounterTests (void)
1537 {
1538 }
1539 
init(void)1540 void AtomicCounterTests::init (void)
1541 {
1542 }
1543 
1544 } // Functional
1545 } // gles31
1546 } // deqp
1547