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
42using namespace glw;
43using tcu::TestLog;
44
45using std::vector;
46using std::string;
47
48namespace deqp
49{
50namespace gles31
51{
52namespace Functional
53{
54namespace
55{
56
57class AtomicCounterTest : public TestCase
58{
59public:
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	{
89		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
116private:
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;
122	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
136int 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
154AtomicCounterTest::AtomicCounterTest (Context& context, const char* name, const char* description, const TestSpec& spec)
155	: TestCase	(context, name, description)
156	, m_spec	(spec)
157{
158}
159
160AtomicCounterTest::~AtomicCounterTest (void)
161{
162}
163
164void AtomicCounterTest::init (void)
165{
166}
167
168void AtomicCounterTest::deinit (void)
169{
170}
171
172string 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
524bool 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
557void 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
633void 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
643bool 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
669bool 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
712bool 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
806bool 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
915TestCase::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
1050string 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
1061string 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
1103string 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
1138string 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
1179string 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
1237string 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
1298AtomicCounterTests::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
1536AtomicCounterTests::~AtomicCounterTests (void)
1537{
1538}
1539
1540void AtomicCounterTests::init (void)
1541{
1542}
1543
1544} // Functional
1545} // gles31
1546} // deqp
1547