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 Shader atomic operation tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es31fShaderAtomicOpTests.hpp"
25#include "gluShaderProgram.hpp"
26#include "gluShaderUtil.hpp"
27#include "gluRenderContext.hpp"
28#include "gluObjectWrapper.hpp"
29#include "gluProgramInterfaceQuery.hpp"
30#include "tcuVector.hpp"
31#include "tcuTestLog.hpp"
32#include "tcuVectorUtil.hpp"
33#include "tcuFormatUtil.hpp"
34#include "deStringUtil.hpp"
35#include "deRandom.hpp"
36#include "glwFunctions.hpp"
37#include "glwEnums.hpp"
38
39#include <algorithm>
40#include <set>
41
42namespace deqp
43{
44namespace gles31
45{
46namespace Functional
47{
48
49using std::string;
50using std::vector;
51using tcu::TestLog;
52using tcu::UVec3;
53using std::set;
54using namespace glu;
55
56template<typename T, int Size>
57static inline T product (const tcu::Vector<T, Size>& v)
58{
59	T res = v[0];
60	for (int ndx = 1; ndx < Size; ndx++)
61		res *= v[ndx];
62	return res;
63}
64
65class ShaderAtomicOpCase : public TestCase
66{
67public:
68							ShaderAtomicOpCase	(Context& context, const char* name, const char* funcName, AtomicOperandType operandType, DataType type, Precision precision, const UVec3& workGroupSize);
69							~ShaderAtomicOpCase	(void);
70
71	void					init				(void);
72	void					deinit				(void);
73	IterateResult			iterate				(void);
74
75protected:
76	virtual void			getInputs			(int numValues, int stride, void* inputs) const = 0;
77	virtual bool			verify				(int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const = 0;
78
79	const string			m_funcName;
80	const AtomicOperandType	m_operandType;
81	const DataType			m_type;
82	const Precision			m_precision;
83
84	const UVec3				m_workGroupSize;
85	const UVec3				m_numWorkGroups;
86
87	deUint32				m_initialValue;
88
89private:
90							ShaderAtomicOpCase	(const ShaderAtomicOpCase& other);
91	ShaderAtomicOpCase&		operator=			(const ShaderAtomicOpCase& other);
92
93	ShaderProgram*			m_program;
94};
95
96ShaderAtomicOpCase::ShaderAtomicOpCase (Context& context, const char* name, const char* funcName, AtomicOperandType operandType, DataType type, Precision precision, const UVec3& workGroupSize)
97	: TestCase			(context, name, funcName)
98	, m_funcName		(funcName)
99	, m_operandType		(operandType)
100	, m_type			(type)
101	, m_precision		(precision)
102	, m_workGroupSize	(workGroupSize)
103	, m_numWorkGroups	(4,4,4)
104	, m_initialValue	(0)
105	, m_program			(DE_NULL)
106{
107}
108
109ShaderAtomicOpCase::~ShaderAtomicOpCase (void)
110{
111	ShaderAtomicOpCase::deinit();
112}
113
114void ShaderAtomicOpCase::init (void)
115{
116	const bool			isSSBO		= m_operandType == ATOMIC_OPERAND_BUFFER_VARIABLE;
117	const char*			precName	= getPrecisionName(m_precision);
118	const char*			typeName	= getDataTypeName(m_type);
119
120	const DataType		outType		= isSSBO ? m_type : glu::TYPE_UINT;
121	const char*			outTypeName	= getDataTypeName(outType);
122
123	const deUint32		numValues	= product(m_workGroupSize)*product(m_numWorkGroups);
124	std::ostringstream	src;
125
126	src << "#version 310 es\n"
127		<< "layout(local_size_x = " << m_workGroupSize.x()
128		<< ", local_size_y = " << m_workGroupSize.y()
129		<< ", local_size_z = " << m_workGroupSize.z() << ") in;\n"
130		<< "layout(binding = 0) buffer InOut\n"
131		<< "{\n"
132		<< "	" << precName << " " << typeName << " inputValues[" << numValues << "];\n"
133		<< "	" << precName << " " << outTypeName << " outputValues[" << numValues << "];\n"
134		<< "	" << (isSSBO ? "coherent " : "") << precName << " " << outTypeName << " groupValues[" << product(m_numWorkGroups) << "];\n"
135		<< "} sb_inout;\n";
136
137	if (!isSSBO)
138		src << "shared " << precName << " " << typeName << " s_var;\n";
139
140	src << "\n"
141		<< "void main (void)\n"
142		<< "{\n"
143		<< "	uint localSize  = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z;\n"
144		<< "	uint globalNdx  = gl_NumWorkGroups.x*gl_NumWorkGroups.y*gl_WorkGroupID.z + gl_NumWorkGroups.x*gl_WorkGroupID.y + gl_WorkGroupID.x;\n"
145		<< "	uint globalOffs = localSize*globalNdx;\n"
146		<< "	uint offset     = globalOffs + gl_LocalInvocationIndex;\n"
147		<< "\n";
148
149	if (isSSBO)
150	{
151		DE_ASSERT(outType == m_type);
152		src << "	sb_inout.outputValues[offset] = " << m_funcName << "(sb_inout.groupValues[globalNdx], sb_inout.inputValues[offset]);\n";
153	}
154	else
155	{
156		const string		castBeg	= outType != m_type ? (string(outTypeName) + "(") : string("");
157		const char* const	castEnd	= outType != m_type ? ")" : "";
158
159		src << "	if (gl_LocalInvocationIndex == 0u)\n"
160			<< "		s_var = " << typeName << "(" << tcu::toHex(m_initialValue) << "u);\n"
161			<< "	barrier();\n"
162			<< "	" << precName << " " << typeName << " res = " << m_funcName << "(s_var, sb_inout.inputValues[offset]);\n"
163			<< "	sb_inout.outputValues[offset] = " << castBeg << "res" << castEnd << ";\n"
164			<< "	barrier();\n"
165			<< "	if (gl_LocalInvocationIndex == 0u)\n"
166			<< "		sb_inout.groupValues[globalNdx] = " << castBeg << "s_var" << castEnd << ";\n";
167	}
168
169	src << "}\n";
170
171	DE_ASSERT(!m_program);
172	m_program = new ShaderProgram(m_context.getRenderContext(), ProgramSources() << ComputeSource(src.str()));
173
174	m_testCtx.getLog() << *m_program;
175
176	if (!m_program->isOk())
177	{
178		delete m_program;
179		m_program = DE_NULL;
180		throw tcu::TestError("Compile failed");
181	}
182}
183
184void ShaderAtomicOpCase::deinit (void)
185{
186	delete m_program;
187	m_program = DE_NULL;
188}
189
190ShaderAtomicOpCase::IterateResult ShaderAtomicOpCase::iterate (void)
191{
192	const glw::Functions&		gl				= m_context.getRenderContext().getFunctions();
193	const deUint32				program			= m_program->getProgram();
194	const Buffer				inoutBuffer		(m_context.getRenderContext());
195	const deUint32				blockNdx		= gl.getProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, "InOut");
196	const InterfaceBlockInfo	blockInfo		= getProgramInterfaceBlockInfo(gl, program, GL_SHADER_STORAGE_BLOCK, blockNdx);
197	const deUint32				inVarNdx		= gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.inputValues[0]");
198	const InterfaceVariableInfo	inVarInfo		= getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, inVarNdx);
199	const deUint32				outVarNdx		= gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.outputValues[0]");
200	const InterfaceVariableInfo	outVarInfo		= getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, outVarNdx);
201	const deUint32				groupVarNdx		= gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.groupValues[0]");
202	const InterfaceVariableInfo	groupVarInfo	= getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, groupVarNdx);
203	const deUint32				numValues		= product(m_workGroupSize)*product(m_numWorkGroups);
204
205	TCU_CHECK(inVarInfo.arraySize == numValues &&
206			  outVarInfo.arraySize == numValues &&
207			  groupVarInfo.arraySize == product(m_numWorkGroups));
208
209	gl.useProgram(program);
210
211	// Setup buffer.
212	{
213		vector<deUint8> bufData(blockInfo.dataSize);
214		std::fill(bufData.begin(), bufData.end(), 0);
215
216		getInputs((int)numValues, (int)inVarInfo.arrayStride, &bufData[0] + inVarInfo.offset);
217
218		if (m_operandType == ATOMIC_OPERAND_BUFFER_VARIABLE)
219		{
220			for (deUint32 valNdx = 0; valNdx < product(m_numWorkGroups); valNdx++)
221				*(deUint32*)(&bufData[0] + groupVarInfo.offset + groupVarInfo.arrayStride*valNdx) = m_initialValue;
222		}
223
224		gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *inoutBuffer);
225		gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockInfo.dataSize, &bufData[0], GL_STATIC_READ);
226		gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *inoutBuffer);
227		GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
228	}
229
230	gl.dispatchCompute(m_numWorkGroups.x(), m_numWorkGroups.y(), m_numWorkGroups.z());
231
232	// Read back and compare
233	{
234		const void*		resPtr		= gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, blockInfo.dataSize, GL_MAP_READ_BIT);
235		bool			isOk		= true;
236
237		GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
238		TCU_CHECK(resPtr);
239
240		isOk = verify((int)numValues,
241					  (int)inVarInfo.arrayStride, (const deUint8*)resPtr + inVarInfo.offset,
242					  (int)outVarInfo.arrayStride, (const deUint8*)resPtr + outVarInfo.offset,
243					  (int)groupVarInfo.arrayStride, (const deUint8*)resPtr + groupVarInfo.offset);
244
245		gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
246		GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer()");
247
248		m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
249								isOk ? "Pass"				: "Comparison failed");
250	}
251
252	return STOP;
253}
254
255class ShaderAtomicAddCase : public ShaderAtomicOpCase
256{
257public:
258	ShaderAtomicAddCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
259		: ShaderAtomicOpCase(context, name, "atomicAdd", operandType, type, precision, UVec3(3,2,1))
260	{
261		m_initialValue = 1;
262	}
263
264protected:
265	void getInputs (int numValues, int stride, void* inputs) const
266	{
267		de::Random	rnd			(deStringHash(getName()));
268		const int	maxVal		= m_precision == PRECISION_LOWP ? 2 : 32;
269		const int	minVal		= 1;
270
271		// \todo [2013-09-04 pyry] Negative values!
272
273		for (int valNdx = 0; valNdx < numValues; valNdx++)
274			*(int*)((deUint8*)inputs + stride*valNdx) = rnd.getInt(minVal, maxVal);
275	}
276
277	bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
278	{
279		const int	workGroupSize	= (int)product(m_workGroupSize);
280		const int	numWorkGroups	= numValues/workGroupSize;
281
282		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
283		{
284			const int	groupOffset		= groupNdx*workGroupSize;
285			const int	groupOutput		= *(const int*)((const deUint8*)groupOutputs + groupNdx*groupStride);
286			set<int>	outValues;
287			bool		maxFound		= false;
288			int			valueSum		= (int)m_initialValue;
289
290			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
291			{
292				const int inputValue = *(const int*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
293				valueSum += inputValue;
294			}
295
296			if (groupOutput != valueSum)
297			{
298				m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected sum " << valueSum << ", got " << groupOutput << TestLog::EndMessage;
299				return false;
300			}
301
302			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
303			{
304				const int	inputValue		= *(const int*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
305				const int	outputValue		= *(const int*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
306
307				if (!de::inRange(outputValue, (int)m_initialValue, valueSum-inputValue))
308				{
309					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
310														   << ": expected value in range [" << m_initialValue << ", " << (valueSum-inputValue)
311														   << "], got " << outputValue
312									   << TestLog::EndMessage;
313					return false;
314				}
315
316				if (outValues.find(outputValue) != outValues.end())
317				{
318					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
319														   << ": found duplicate value " << outputValue
320									   << TestLog::EndMessage;
321					return false;
322				}
323
324				outValues.insert(outputValue);
325				if (outputValue == valueSum-inputValue)
326					maxFound = true;
327			}
328
329			if (!maxFound)
330			{
331				m_testCtx.getLog() << TestLog::Message << "ERROR: could not find maximum expected value from group " << groupNdx << TestLog::EndMessage;
332				return false;
333			}
334
335			if (outValues.find((int)m_initialValue) == outValues.end())
336			{
337				m_testCtx.getLog() << TestLog::Message << "ERROR: could not find initial value from group " << groupNdx << TestLog::EndMessage;
338				return false;
339			}
340		}
341
342		return true;
343	}
344};
345
346
347static int getPrecisionNumIntegerBits (glu::Precision precision)
348{
349	switch (precision)
350	{
351		case glu::PRECISION_HIGHP:		return 32;
352		case glu::PRECISION_MEDIUMP:	return 16;
353		case glu::PRECISION_LOWP:		return 9;
354		default:
355			DE_ASSERT(false);
356			return 0;
357	}
358}
359
360static deUint32 getPrecisionMask (int numPreciseBits)
361{
362	// \note: bit shift with larger or equal than var length is undefined, use 64 bit ints
363	return (deUint32)((((deUint64)1u) << numPreciseBits) - 1) ;
364}
365
366static bool intEqualsAfterUintCast (deInt32 value, deUint32 casted, glu::Precision precision)
367{
368	// Bit format of 'casted' = [ uint -> highp uint promotion bits (0) ] [ sign extend bits (s) ] [ value bits ]
369	//                                                                                             |--min len---|
370	//                                                                    |---------------signed length---------|
371	//                          |-------------------------------- highp uint length ----------------------------|
372
373	const deUint32	reference		= (deUint32)value;
374	const int		signBitOn		= value < 0;
375	const int		numPreciseBits	= getPrecisionNumIntegerBits(precision);
376	const deUint32	preciseMask		= getPrecisionMask(numPreciseBits);
377
378	// Lowest N bits must match, N = minimum precision
379	if ((reference & preciseMask) != (casted & preciseMask))
380		return false;
381
382	// Other lowest bits must match the sign and the remaining (topmost) if any must be 0
383	for (int signedIntegerLength = numPreciseBits; signedIntegerLength <= 32; ++signedIntegerLength)
384	{
385		const deUint32 signBits = (signBitOn) ? (getPrecisionMask(signedIntegerLength)) : (0u);
386
387		if ((signBits & ~preciseMask) == (casted & ~preciseMask))
388			return true;
389	}
390	return false;
391}
392
393static bool containsAfterUintCast (const std::set<deInt32>& haystack, deUint32 needle, glu::Precision precision)
394{
395	for (std::set<deInt32>::const_iterator it = haystack.begin(); it != haystack.end(); ++it)
396		if (intEqualsAfterUintCast(*it, needle, precision))
397			return true;
398	return false;
399}
400
401static bool containsAfterUintCast (const std::set<deUint32>& haystack, deInt32 needle, glu::Precision precision)
402{
403	for (std::set<deUint32>::const_iterator it = haystack.begin(); it != haystack.end(); ++it)
404		if (intEqualsAfterUintCast(needle, *it, precision))
405			return true;
406	return false;
407}
408
409class ShaderAtomicMinCase : public ShaderAtomicOpCase
410{
411public:
412	ShaderAtomicMinCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
413		: ShaderAtomicOpCase(context, name, "atomicMin", operandType, type, precision, UVec3(3,2,1))
414	{
415		m_initialValue = m_precision == PRECISION_LOWP ? 100 : 1000;
416	}
417
418protected:
419	void getInputs (int numValues, int stride, void* inputs) const
420	{
421		de::Random	rnd			(deStringHash(getName()));
422		const bool	isSigned	= m_type == TYPE_INT;
423		const int	maxVal		= m_precision == PRECISION_LOWP ? 100 : 1000;
424		const int	minVal		= isSigned ? -maxVal : 0;
425
426		for (int valNdx = 0; valNdx < numValues; valNdx++)
427			*(int*)((deUint8*)inputs + stride*valNdx) = rnd.getInt(minVal, maxVal);
428	}
429
430	bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
431	{
432		const int	workGroupSize	= (int)product(m_workGroupSize);
433		const int	numWorkGroups	= numValues/workGroupSize;
434		bool		anyError		= false;
435
436		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
437		{
438			const int		groupOffset		= groupNdx*workGroupSize;
439			const deUint32	groupOutput		= *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
440			set<deInt32>	inValues;
441			set<deUint32>	outValues;
442			int				minValue		= (int)m_initialValue;
443
444			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
445			{
446				const deInt32 inputValue = *(const deInt32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
447				inValues.insert(inputValue);
448				minValue = de::min(inputValue, minValue);
449			}
450
451			if (!intEqualsAfterUintCast(minValue, groupOutput, m_precision))
452			{
453				m_testCtx.getLog()
454					<< TestLog::Message
455					<< "ERROR: at group " << groupNdx
456					<< ": expected minimum " << minValue << " (" << tcu::Format::Hex<8>((deUint32)minValue) << ")"
457					<< ", got " << groupOutput << " (" << tcu::Format::Hex<8>(groupOutput) << ")"
458					<< TestLog::EndMessage;
459				anyError = true;
460			}
461
462			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
463			{
464				const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
465
466				if (!containsAfterUintCast(inValues, outputValue, m_precision) &&
467					!intEqualsAfterUintCast((deInt32)m_initialValue, outputValue, m_precision))
468				{
469					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
470														   << ": found unexpected value " << outputValue
471														   << " (" << tcu::Format::Hex<8>(outputValue) << ")"
472									   << TestLog::EndMessage;
473					anyError = true;
474				}
475
476				outValues.insert(outputValue);
477			}
478
479			if (!containsAfterUintCast(outValues, (int)m_initialValue, m_precision))
480			{
481				m_testCtx.getLog() << TestLog::Message << "ERROR: could not find initial value from group " << groupNdx << TestLog::EndMessage;
482				anyError = true;
483			}
484		}
485
486		return !anyError;
487	}
488};
489
490class ShaderAtomicMaxCase : public ShaderAtomicOpCase
491{
492public:
493	ShaderAtomicMaxCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
494		: ShaderAtomicOpCase(context, name, "atomicMax", operandType, type, precision, UVec3(3,2,1))
495	{
496		const bool isSigned = m_type == TYPE_INT;
497		m_initialValue = isSigned ? (m_precision == PRECISION_LOWP ? -100 : -1000) : 0;
498	}
499
500protected:
501	void getInputs (int numValues, int stride, void* inputs) const
502	{
503		de::Random	rnd			(deStringHash(getName()));
504		const bool	isSigned	= m_type == TYPE_INT;
505		const int	maxVal		= m_precision == PRECISION_LOWP ? 100 : 1000;
506		const int	minVal		= isSigned ? -maxVal : 0;
507
508		for (int valNdx = 0; valNdx < numValues; valNdx++)
509			*(int*)((deUint8*)inputs + stride*valNdx) = rnd.getInt(minVal, maxVal);
510	}
511
512	bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
513	{
514		const int	workGroupSize	= (int)product(m_workGroupSize);
515		const int	numWorkGroups	= numValues/workGroupSize;
516		bool		anyError		= false;
517
518		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
519		{
520			const int		groupOffset		= groupNdx*workGroupSize;
521			const deUint32	groupOutput		= *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
522			set<int>		inValues;
523			set<deUint32>	outValues;
524			int				maxValue		= (int)m_initialValue;
525
526			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
527			{
528				const deInt32 inputValue = *(const deInt32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
529				inValues.insert(inputValue);
530				maxValue = de::max(maxValue, inputValue);
531			}
532
533			if (!intEqualsAfterUintCast(maxValue, groupOutput, m_precision))
534			{
535				m_testCtx.getLog()
536					<< TestLog::Message
537					<< "ERROR: at group " << groupNdx
538					<< ": expected maximum " << maxValue << " (" << tcu::Format::Hex<8>((deUint32)maxValue) << ")"
539					<< ", got " << groupOutput << " (" << tcu::Format::Hex<8>(groupOutput) << ")"
540					<< TestLog::EndMessage;
541				anyError = true;
542			}
543
544			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
545			{
546				const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
547
548				if (!containsAfterUintCast(inValues, outputValue, m_precision) &&
549					!intEqualsAfterUintCast((deInt32)m_initialValue, outputValue, m_precision))
550				{
551					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
552														   << ": found unexpected value " << outputValue
553														   << " (" << tcu::Format::Hex<8>(outputValue) << ")"
554									   << TestLog::EndMessage;
555					anyError = true;
556				}
557
558				outValues.insert(outputValue);
559			}
560
561			if (!containsAfterUintCast(outValues, (int)m_initialValue, m_precision))
562			{
563				m_testCtx.getLog() << TestLog::Message << "ERROR: could not find initial value from group " << groupNdx << TestLog::EndMessage;
564				anyError = true;
565			}
566		}
567
568		return !anyError;
569	}
570};
571
572class ShaderAtomicAndCase : public ShaderAtomicOpCase
573{
574public:
575	ShaderAtomicAndCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
576		: ShaderAtomicOpCase(context, name, "atomicAnd", operandType, type, precision, UVec3(3,2,1))
577	{
578		const int		numBits		= m_precision == PRECISION_HIGHP ? 32 :
579									  m_precision == PRECISION_MEDIUMP ? 16 : 8;
580		const deUint32	valueMask	= numBits == 32 ? ~0u : (1u<<numBits)-1u;
581		m_initialValue = ~((1u<<(numBits-1u)) | 1u) & valueMask; // All bits except lowest and highest set.
582	}
583
584protected:
585	void getInputs (int numValues, int stride, void* inputs) const
586	{
587		de::Random		rnd				(deStringHash(getName()));
588		const int		workGroupSize	= (int)product(m_workGroupSize);
589		const int		numWorkGroups	= numValues/workGroupSize;
590		const int		numBits			= m_precision == PRECISION_HIGHP ? 32 :
591										  m_precision == PRECISION_MEDIUMP ? 16 : 8;
592		const deUint32	valueMask		= numBits == 32 ? ~0u : (1u<<numBits)-1u;
593
594		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
595		{
596			const int		groupOffset		= groupNdx*workGroupSize;
597			const deUint32	groupMask		= 1<<rnd.getInt(0, numBits-2); // One bit is always set.
598
599			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
600				*(deUint32*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = (rnd.getUint32() & valueMask) | groupMask;
601		}
602	}
603
604	bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
605	{
606		const int	workGroupSize	= (int)product(m_workGroupSize);
607		const int	numWorkGroups	= numValues/workGroupSize;
608
609		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
610		{
611			const int		groupOffset		= groupNdx*workGroupSize;
612			const deUint32	groupOutput		= *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
613			deUint32		expectedValue	= m_initialValue;
614
615			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
616			{
617				const deUint32 inputValue = *(const deUint32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
618				expectedValue &= inputValue;
619			}
620
621			if (expectedValue != groupOutput)
622			{
623				m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected " << tcu::toHex(expectedValue) << ", got " << tcu::toHex(groupOutput) << TestLog::EndMessage;
624				return false;
625			}
626
627			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
628			{
629				const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
630
631				if ((outputValue & ~m_initialValue) != 0)
632				{
633					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
634														   << ": found unexpected value " << tcu::toHex(outputValue)
635									   << TestLog::EndMessage;
636					return false;
637				}
638			}
639		}
640
641		return true;
642	}
643};
644
645class ShaderAtomicOrCase : public ShaderAtomicOpCase
646{
647public:
648	ShaderAtomicOrCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
649		: ShaderAtomicOpCase(context, name, "atomicOr", operandType, type, precision, UVec3(3,2,1))
650	{
651		m_initialValue = 1u; // Lowest bit set.
652	}
653
654protected:
655	void getInputs (int numValues, int stride, void* inputs) const
656	{
657		de::Random		rnd				(deStringHash(getName()));
658		const int		workGroupSize	= (int)product(m_workGroupSize);
659		const int		numWorkGroups	= numValues/workGroupSize;
660		const int		numBits			= m_precision == PRECISION_HIGHP ? 32 :
661										  m_precision == PRECISION_MEDIUMP ? 16 : 8;
662
663		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
664		{
665			const int groupOffset = groupNdx*workGroupSize;
666
667			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
668				*(deUint32*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = 1u<<rnd.getInt(0, numBits-1);
669		}
670	}
671
672	bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
673	{
674		const int	workGroupSize	= (int)product(m_workGroupSize);
675		const int	numWorkGroups	= numValues/workGroupSize;
676
677		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
678		{
679			const int		groupOffset		= groupNdx*workGroupSize;
680			const deUint32	groupOutput		= *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
681			deUint32		expectedValue	= m_initialValue;
682
683			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
684			{
685				const deUint32 inputValue = *(const deUint32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
686				expectedValue |= inputValue;
687			}
688
689			if (expectedValue != groupOutput)
690			{
691				m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected " << tcu::toHex(expectedValue) << ", got " << tcu::toHex(groupOutput) << TestLog::EndMessage;
692				return false;
693			}
694
695			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
696			{
697				const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
698
699				if ((outputValue & m_initialValue) == 0)
700				{
701					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
702														   << ": found unexpected value " << tcu::toHex(outputValue)
703									   << TestLog::EndMessage;
704					return false;
705				}
706			}
707		}
708
709		return true;
710	}
711};
712
713class ShaderAtomicXorCase : public ShaderAtomicOpCase
714{
715public:
716	ShaderAtomicXorCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
717		: ShaderAtomicOpCase(context, name, "atomicXor", operandType, type, precision, UVec3(3,2,1))
718	{
719		m_initialValue = 0;
720	}
721
722protected:
723	void getInputs (int numValues, int stride, void* inputs) const
724	{
725		de::Random		rnd				(deStringHash(getName()));
726		const int		workGroupSize	= (int)product(m_workGroupSize);
727		const int		numWorkGroups	= numValues/workGroupSize;
728
729		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
730		{
731			const int groupOffset = groupNdx*workGroupSize;
732
733			// First uses random bit-pattern.
734			*(deUint32*)((deUint8*)inputs + stride*(groupOffset)) = rnd.getUint32();
735
736			// Rest have either all or no bits set.
737			for (int localNdx = 1; localNdx < workGroupSize; localNdx++)
738				*(deUint32*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = rnd.getBool() ? ~0u : 0u;
739		}
740	}
741
742	bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
743	{
744		const int		workGroupSize	= (int)product(m_workGroupSize);
745		const int		numWorkGroups	= numValues/workGroupSize;
746		const int		numBits			= m_precision == PRECISION_HIGHP ? 32 :
747										  m_precision == PRECISION_MEDIUMP ? 16 : 8;
748		const deUint32	compareMask		= numBits == 32 ? ~0u : (1u<<numBits)-1u;
749
750		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
751		{
752			const int		groupOffset		= groupNdx*workGroupSize;
753			const deUint32	groupOutput		= *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
754			const deUint32	randomValue		= *(const int*)((const deUint8*)inputs + inputStride*groupOffset);
755			const deUint32	expected0		= randomValue ^ 0u;
756			const deUint32	expected1		= randomValue ^ ~0u;
757			int				numXorZeros		= (m_initialValue == 0) ? 1 : 0;
758
759			for (int localNdx = 1; localNdx < workGroupSize; localNdx++)
760			{
761				const deUint32 inputValue = *(const deUint32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
762				if (inputValue == 0)
763					numXorZeros += 1;
764			}
765
766			const deUint32 expected = (numXorZeros%2 == 0) ? expected0 : expected1;
767
768			if ((groupOutput & compareMask) != (expected & compareMask))
769			{
770				m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected " << tcu::toHex(expected0)
771													   << " or " << tcu::toHex(expected1) << " (compare mask " << tcu::toHex(compareMask)
772													   << "), got " << tcu::toHex(groupOutput) << TestLog::EndMessage;
773				return false;
774			}
775
776			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
777			{
778				const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
779
780				if ((outputValue&compareMask) != 0 &&
781					(outputValue&compareMask) != compareMask &&
782					(outputValue&compareMask) != (expected0&compareMask) &&
783					(outputValue&compareMask) != (expected1&compareMask))
784				{
785					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
786														   << ": found unexpected value " << tcu::toHex(outputValue)
787									   << TestLog::EndMessage;
788					return false;
789				}
790			}
791		}
792
793		return true;
794	}
795};
796
797class ShaderAtomicExchangeCase : public ShaderAtomicOpCase
798{
799public:
800	ShaderAtomicExchangeCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
801		: ShaderAtomicOpCase(context, name, "atomicExchange", operandType, type, precision, UVec3(3,2,1))
802	{
803		m_initialValue = 0;
804	}
805
806protected:
807	void getInputs (int numValues, int stride, void* inputs) const
808	{
809		const int	workGroupSize	= (int)product(m_workGroupSize);
810		const int	numWorkGroups	= numValues/workGroupSize;
811
812		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
813		{
814			const int groupOffset = groupNdx*workGroupSize;
815
816			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
817				*(int*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = localNdx+1;
818		}
819	}
820
821	bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
822	{
823		const int	workGroupSize	= (int)product(m_workGroupSize);
824		const int	numWorkGroups	= numValues/workGroupSize;
825
826		DE_UNREF(inputStride && inputs);
827
828		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
829		{
830			const int	groupOffset		= groupNdx*workGroupSize;
831			const int	groupOutput		= *(const int*)((const deUint8*)groupOutputs + groupNdx*groupStride);
832			set<int>	usedValues;
833
834			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
835			{
836				const int outputValue = *(const int*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
837
838				if (!de::inRange(outputValue, 0, workGroupSize) || usedValues.find(outputValue) != usedValues.end())
839				{
840					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
841														   << ": found unexpected value " << outputValue
842									   << TestLog::EndMessage;
843					return false;
844				}
845				usedValues.insert(outputValue);
846			}
847
848			if (!de::inRange(groupOutput, 0, workGroupSize) || usedValues.find(groupOutput) != usedValues.end())
849			{
850				m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": unexpected final value" << groupOutput << TestLog::EndMessage;
851				return false;
852			}
853		}
854
855		return true;
856	}
857};
858
859class ShaderAtomicCompSwapCase : public TestCase
860{
861public:
862									ShaderAtomicCompSwapCase	(Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision);
863									~ShaderAtomicCompSwapCase	(void);
864
865	void							init						(void);
866	void							deinit						(void);
867	IterateResult					iterate						(void);
868
869protected:
870
871private:
872									ShaderAtomicCompSwapCase	(const ShaderAtomicCompSwapCase& other);
873	ShaderAtomicCompSwapCase&		operator=					(const ShaderAtomicCompSwapCase& other);
874
875	const AtomicOperandType			m_operandType;
876	const DataType					m_type;
877	const Precision					m_precision;
878
879	const UVec3						m_workGroupSize;
880	const UVec3						m_numWorkGroups;
881
882	ShaderProgram*					m_program;
883};
884
885ShaderAtomicCompSwapCase::ShaderAtomicCompSwapCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
886	: TestCase			(context, name, "atomicCompSwap() Test")
887	, m_operandType		(operandType)
888	, m_type			(type)
889	, m_precision		(precision)
890	, m_workGroupSize	(3,2,1)
891	, m_numWorkGroups	(4,4,4)
892	, m_program			(DE_NULL)
893{
894}
895
896ShaderAtomicCompSwapCase::~ShaderAtomicCompSwapCase (void)
897{
898	ShaderAtomicCompSwapCase::deinit();
899}
900
901void ShaderAtomicCompSwapCase::init (void)
902{
903	const bool			isSSBO		= m_operandType == ATOMIC_OPERAND_BUFFER_VARIABLE;
904	const char*			precName	= getPrecisionName(m_precision);
905	const char*			typeName	= getDataTypeName(m_type);
906	const deUint32		numValues	= product(m_workGroupSize)*product(m_numWorkGroups);
907	std::ostringstream	src;
908
909	src << "#version 310 es\n"
910		<< "layout(local_size_x = " << m_workGroupSize.x()
911		<< ", local_size_y = " << m_workGroupSize.y()
912		<< ", local_size_z = " << m_workGroupSize.z() << ") in;\n"
913		<< "layout(binding = 0) buffer InOut\n"
914		<< "{\n"
915		<< "	" << precName << " " << typeName << " compareValues[" << numValues << "];\n"
916		<< "	" << precName << " " << typeName << " exchangeValues[" << numValues << "];\n"
917		<< "	" << precName << " " << typeName << " outputValues[" << numValues << "];\n"
918		<< "	" << (isSSBO ? "coherent " : "") << precName << " " << typeName << " groupValues[" << product(m_numWorkGroups) << "];\n"
919		<< "} sb_inout;\n";
920
921	if (!isSSBO)
922		src << "shared " << precName << " " << typeName << " s_var;\n";
923
924	src << "\n"
925		<< "void main (void)\n"
926		<< "{\n"
927		<< "	uint localSize  = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z;\n"
928		<< "	uint globalNdx  = gl_NumWorkGroups.x*gl_NumWorkGroups.y*gl_WorkGroupID.z + gl_NumWorkGroups.x*gl_WorkGroupID.y + gl_WorkGroupID.x;\n"
929		<< "	uint globalOffs = localSize*globalNdx;\n"
930		<< "	uint offset     = globalOffs + gl_LocalInvocationIndex;\n"
931		<< "\n";
932
933	if (!isSSBO)
934	{
935		src << "	if (gl_LocalInvocationIndex == 0u)\n"
936			<< "		s_var = " << typeName << "(" << 0 << ");\n"
937			<< "\n";
938	}
939
940	src << "	" << precName << " " << typeName << " compare = sb_inout.compareValues[offset];\n"
941		<< "	" << precName << " " << typeName << " exchange = sb_inout.exchangeValues[offset];\n"
942		<< "	" << precName << " " << typeName << " result;\n"
943		<< "	bool swapDone = false;\n"
944		<< "\n"
945		<< "	for (uint ndx = 0u; ndx < localSize; ndx++)\n"
946		<< "	{\n"
947		<< "		barrier();\n"
948		<< "		if (!swapDone)\n"
949		<< "		{\n"
950		<< "			result = atomicCompSwap(" << (isSSBO ? "sb_inout.groupValues[globalNdx]" : "s_var") << ", compare, exchange);\n"
951		<< "			if (result == compare)\n"
952		<< "				swapDone = true;\n"
953		<< "		}\n"
954		<< "	}\n"
955		<< "\n"
956		<< "	sb_inout.outputValues[offset] = result;\n";
957
958	if (!isSSBO)
959	{
960		src << "	barrier();\n"
961			<< "	if (gl_LocalInvocationIndex == 0u)\n"
962			<< "		sb_inout.groupValues[globalNdx] = s_var;\n";
963	}
964
965	src << "}\n";
966
967	DE_ASSERT(!m_program);
968	m_program = new ShaderProgram(m_context.getRenderContext(), ProgramSources() << ComputeSource(src.str()));
969
970	m_testCtx.getLog() << *m_program;
971
972	if (!m_program->isOk())
973	{
974		delete m_program;
975		m_program = DE_NULL;
976		throw tcu::TestError("Compile failed");
977	}
978}
979
980void ShaderAtomicCompSwapCase::deinit (void)
981{
982	delete m_program;
983	m_program = DE_NULL;
984}
985
986ShaderAtomicOpCase::IterateResult ShaderAtomicCompSwapCase::iterate (void)
987{
988	const glw::Functions&		gl				= m_context.getRenderContext().getFunctions();
989	const deUint32				program			= m_program->getProgram();
990	const Buffer				inoutBuffer		(m_context.getRenderContext());
991	const deUint32				blockNdx		= gl.getProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, "InOut");
992	const InterfaceBlockInfo	blockInfo		= getProgramInterfaceBlockInfo(gl, program, GL_SHADER_STORAGE_BLOCK, blockNdx);
993	const deUint32				cmpVarNdx		= gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.compareValues[0]");
994	const InterfaceVariableInfo	cmpVarInfo		= getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, cmpVarNdx);
995	const deUint32				exhVarNdx		= gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.exchangeValues[0]");
996	const InterfaceVariableInfo	exhVarInfo		= getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, exhVarNdx);
997	const deUint32				outVarNdx		= gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.outputValues[0]");
998	const InterfaceVariableInfo	outVarInfo		= getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, outVarNdx);
999	const deUint32				groupVarNdx		= gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.groupValues[0]");
1000	const InterfaceVariableInfo	groupVarInfo	= getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, groupVarNdx);
1001	const deUint32				numValues		= product(m_workGroupSize)*product(m_numWorkGroups);
1002
1003	TCU_CHECK(cmpVarInfo.arraySize == numValues &&
1004			  exhVarInfo.arraySize == numValues &&
1005			  outVarInfo.arraySize == numValues &&
1006			  groupVarInfo.arraySize == product(m_numWorkGroups));
1007
1008	gl.useProgram(program);
1009
1010	// \todo [2013-09-05 pyry] Use randomized input values!
1011
1012	// Setup buffer.
1013	{
1014		const deUint32	workGroupSize	= product(m_workGroupSize);
1015		vector<deUint8>	bufData			(blockInfo.dataSize);
1016
1017		std::fill(bufData.begin(), bufData.end(), 0);
1018
1019		for (deUint32 ndx = 0; ndx < numValues; ndx++)
1020			*(deUint32*)(&bufData[0] + cmpVarInfo.offset + cmpVarInfo.arrayStride*ndx) = ndx%workGroupSize;
1021
1022		for (deUint32 ndx = 0; ndx < numValues; ndx++)
1023			*(deUint32*)(&bufData[0] + exhVarInfo.offset + exhVarInfo.arrayStride*ndx) = (ndx%workGroupSize)+1;
1024
1025		gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *inoutBuffer);
1026		gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockInfo.dataSize, &bufData[0], GL_STATIC_READ);
1027		gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *inoutBuffer);
1028		GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
1029	}
1030
1031	gl.dispatchCompute(m_numWorkGroups.x(), m_numWorkGroups.y(), m_numWorkGroups.z());
1032
1033	// Read back and compare
1034	{
1035		const void*		resPtr			= gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, blockInfo.dataSize, GL_MAP_READ_BIT);
1036		const int		numWorkGroups	= (int)product(m_numWorkGroups);
1037		const int		workGroupSize	= (int)product(m_workGroupSize);
1038		bool			isOk			= true;
1039
1040		GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
1041		TCU_CHECK(resPtr);
1042
1043		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
1044		{
1045			const int	groupOffset		= groupNdx*workGroupSize;
1046			const int	groupOutput		= *(const int*)((const deUint8*)resPtr + groupVarInfo.offset + groupNdx*groupVarInfo.arrayStride);
1047
1048			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
1049			{
1050				const int	refValue		= localNdx;
1051				const int	outputValue		= *(const int*)((const deUint8*)resPtr + outVarInfo.offset + outVarInfo.arrayStride*(groupOffset+localNdx));
1052
1053				if (outputValue != refValue)
1054				{
1055					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
1056														   << ": expected " << refValue << ", got " << outputValue
1057									   << TestLog::EndMessage;
1058					isOk = false;
1059					break;
1060				}
1061			}
1062
1063			if (groupOutput != workGroupSize)
1064			{
1065				m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected" << workGroupSize << ", got " << groupOutput << TestLog::EndMessage;
1066				isOk = false;
1067				break;
1068			}
1069		}
1070
1071		gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
1072		GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer()");
1073
1074		m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
1075								isOk ? "Pass"				: "Comparison failed");
1076	}
1077
1078	return STOP;
1079}
1080
1081ShaderAtomicOpTests::ShaderAtomicOpTests (Context& context, const char* name, AtomicOperandType operandType)
1082	: TestCaseGroup	(context, name, "Atomic Operation Tests")
1083	, m_operandType	(operandType)
1084{
1085}
1086
1087ShaderAtomicOpTests::~ShaderAtomicOpTests (void)
1088{
1089}
1090
1091template<typename T>
1092static tcu::TestCaseGroup* createAtomicOpGroup (Context& context, AtomicOperandType operandType, const char* groupName)
1093{
1094	tcu::TestCaseGroup *const group = new tcu::TestCaseGroup(context.getTestContext(), groupName, (string("Atomic ") + groupName).c_str());
1095	try
1096	{
1097		for (int precNdx = 0; precNdx < PRECISION_LAST; precNdx++)
1098		{
1099			for (int typeNdx = 0; typeNdx < 2; typeNdx++)
1100			{
1101				const Precision		precision		= Precision(precNdx);
1102				const DataType		type			= typeNdx > 0 ? TYPE_INT : TYPE_UINT;
1103				const string		caseName		= string(getPrecisionName(precision)) + "_" + getDataTypeName(type);
1104
1105				group->addChild(new T(context, caseName.c_str(), operandType, type, precision));
1106			}
1107		}
1108
1109		return group;
1110	}
1111	catch (...)
1112	{
1113		delete group;
1114		throw;
1115	}
1116}
1117
1118void ShaderAtomicOpTests::init (void)
1119{
1120	addChild(createAtomicOpGroup<ShaderAtomicAddCase>		(m_context, m_operandType, "add"));
1121	addChild(createAtomicOpGroup<ShaderAtomicMinCase>		(m_context, m_operandType, "min"));
1122	addChild(createAtomicOpGroup<ShaderAtomicMaxCase>		(m_context, m_operandType, "max"));
1123	addChild(createAtomicOpGroup<ShaderAtomicAndCase>		(m_context, m_operandType, "and"));
1124	addChild(createAtomicOpGroup<ShaderAtomicOrCase>		(m_context, m_operandType, "or"));
1125	addChild(createAtomicOpGroup<ShaderAtomicXorCase>		(m_context, m_operandType, "xor"));
1126	addChild(createAtomicOpGroup<ShaderAtomicExchangeCase>	(m_context, m_operandType, "exchange"));
1127	addChild(createAtomicOpGroup<ShaderAtomicCompSwapCase>	(m_context, m_operandType, "compswap"));
1128}
1129
1130} // Functional
1131} // gles31
1132} // deqp
1133