es31fShaderAtomicOpTests.cpp revision 1552064677d053102339529167f27e75e6e1bc5c
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 deInt32*)((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 deInt32*)((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 deInt32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
305				const int	outputValue		= *(const deInt32*)((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		const int		numBits			= m_precision == PRECISION_HIGHP ? 32 :
609										  m_precision == PRECISION_MEDIUMP ? 16 : 8;
610		const deUint32	compareMask		= (m_type == TYPE_UINT || numBits == 32) ? ~0u : (1u<<numBits)-1u;
611
612		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
613		{
614			const int		groupOffset		= groupNdx*workGroupSize;
615			const deUint32	groupOutput		= *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
616			deUint32		expectedValue	= m_initialValue;
617
618			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
619			{
620				const deUint32 inputValue = *(const deUint32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
621				expectedValue &= inputValue;
622			}
623
624			if ((groupOutput & compareMask) != (expectedValue & compareMask))
625			{
626				m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected " << tcu::toHex(expectedValue) << ", got " << tcu::toHex(groupOutput) << TestLog::EndMessage;
627				return false;
628			}
629
630			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
631			{
632				const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
633
634				if ((compareMask & (outputValue & ~m_initialValue)) != 0)
635				{
636					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
637														   << ": found unexpected value " << tcu::toHex(outputValue)
638									   << TestLog::EndMessage;
639					return false;
640				}
641			}
642		}
643
644		return true;
645	}
646};
647
648class ShaderAtomicOrCase : public ShaderAtomicOpCase
649{
650public:
651	ShaderAtomicOrCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
652		: ShaderAtomicOpCase(context, name, "atomicOr", operandType, type, precision, UVec3(3,2,1))
653	{
654		m_initialValue = 1u; // Lowest bit set.
655	}
656
657protected:
658	void getInputs (int numValues, int stride, void* inputs) const
659	{
660		de::Random		rnd				(deStringHash(getName()));
661		const int		workGroupSize	= (int)product(m_workGroupSize);
662		const int		numWorkGroups	= numValues/workGroupSize;
663		const int		numBits			= m_precision == PRECISION_HIGHP ? 32 :
664										  m_precision == PRECISION_MEDIUMP ? 16 : 8;
665
666		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
667		{
668			const int groupOffset = groupNdx*workGroupSize;
669
670			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
671				*(deUint32*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = 1u<<rnd.getInt(0, numBits-1);
672		}
673	}
674
675	bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
676	{
677		const int		workGroupSize	= (int)product(m_workGroupSize);
678		const int		numWorkGroups	= numValues/workGroupSize;
679		const int		numBits			= m_precision == PRECISION_HIGHP ? 32 :
680										  m_precision == PRECISION_MEDIUMP ? 16 : 8;
681		const deUint32	compareMask		= (m_type == TYPE_UINT || numBits == 32) ? ~0u : (1u<<numBits)-1u;
682
683		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
684		{
685			const int		groupOffset		= groupNdx*workGroupSize;
686			const deUint32	groupOutput		= *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
687			deUint32		expectedValue	= m_initialValue;
688
689			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
690			{
691				const deUint32 inputValue = *(const deUint32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
692				expectedValue |= inputValue;
693			}
694
695			if ((groupOutput & compareMask) != (expectedValue & compareMask))
696			{
697				m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected " << tcu::toHex(expectedValue) << ", got " << tcu::toHex(groupOutput) << TestLog::EndMessage;
698				return false;
699			}
700
701			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
702			{
703				const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
704
705				if ((compareMask & (outputValue & m_initialValue)) == 0)
706				{
707					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
708														   << ": found unexpected value " << tcu::toHex(outputValue)
709									   << TestLog::EndMessage;
710					return false;
711				}
712			}
713		}
714
715		return true;
716	}
717};
718
719class ShaderAtomicXorCase : public ShaderAtomicOpCase
720{
721public:
722	ShaderAtomicXorCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
723		: ShaderAtomicOpCase(context, name, "atomicXor", operandType, type, precision, UVec3(3,2,1))
724	{
725		m_initialValue = 0;
726	}
727
728protected:
729	void getInputs (int numValues, int stride, void* inputs) const
730	{
731		de::Random		rnd				(deStringHash(getName()));
732		const int		workGroupSize	= (int)product(m_workGroupSize);
733		const int		numWorkGroups	= numValues/workGroupSize;
734
735		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
736		{
737			const int groupOffset = groupNdx*workGroupSize;
738
739			// First uses random bit-pattern.
740			*(deUint32*)((deUint8*)inputs + stride*(groupOffset)) = rnd.getUint32();
741
742			// Rest have either all or no bits set.
743			for (int localNdx = 1; localNdx < workGroupSize; localNdx++)
744				*(deUint32*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = rnd.getBool() ? ~0u : 0u;
745		}
746	}
747
748	bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
749	{
750		const int		workGroupSize	= (int)product(m_workGroupSize);
751		const int		numWorkGroups	= numValues/workGroupSize;
752		const int		numBits			= m_precision == PRECISION_HIGHP ? 32 :
753										  m_precision == PRECISION_MEDIUMP ? 16 : 8;
754		const deUint32	compareMask		= (m_type == TYPE_UINT || numBits == 32) ? ~0u : (1u<<numBits)-1u;
755
756		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
757		{
758			const int		groupOffset		= groupNdx*workGroupSize;
759			const deUint32	groupOutput		= *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
760			const deUint32	randomValue		= *(const deInt32*)((const deUint8*)inputs + inputStride*groupOffset);
761			const deUint32	expected0		= randomValue ^ 0u;
762			const deUint32	expected1		= randomValue ^ ~0u;
763			int				numXorZeros		= (m_initialValue == 0) ? 1 : 0;
764
765			for (int localNdx = 1; localNdx < workGroupSize; localNdx++)
766			{
767				const deUint32 inputValue = *(const deUint32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
768				if (inputValue == 0)
769					numXorZeros += 1;
770			}
771
772			const deUint32 expected = (numXorZeros%2 == 0) ? expected0 : expected1;
773
774			if ((groupOutput & compareMask) != (expected & compareMask))
775			{
776				m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected " << tcu::toHex(expected0)
777													   << " or " << tcu::toHex(expected1) << " (compare mask " << tcu::toHex(compareMask)
778													   << "), got " << tcu::toHex(groupOutput) << TestLog::EndMessage;
779				return false;
780			}
781
782			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
783			{
784				const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
785
786				if ((outputValue&compareMask) != 0 &&
787					(outputValue&compareMask) != compareMask &&
788					(outputValue&compareMask) != (expected0&compareMask) &&
789					(outputValue&compareMask) != (expected1&compareMask))
790				{
791					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
792														   << ": found unexpected value " << tcu::toHex(outputValue)
793									   << TestLog::EndMessage;
794					return false;
795				}
796			}
797		}
798
799		return true;
800	}
801};
802
803class ShaderAtomicExchangeCase : public ShaderAtomicOpCase
804{
805public:
806	ShaderAtomicExchangeCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
807		: ShaderAtomicOpCase(context, name, "atomicExchange", operandType, type, precision, UVec3(3,2,1))
808	{
809		m_initialValue = 0;
810	}
811
812protected:
813	void getInputs (int numValues, int stride, void* inputs) const
814	{
815		const int	workGroupSize	= (int)product(m_workGroupSize);
816		const int	numWorkGroups	= numValues/workGroupSize;
817
818		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
819		{
820			const int groupOffset = groupNdx*workGroupSize;
821
822			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
823				*(int*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = localNdx+1;
824		}
825	}
826
827	bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
828	{
829		const int	workGroupSize	= (int)product(m_workGroupSize);
830		const int	numWorkGroups	= numValues/workGroupSize;
831
832		DE_UNREF(inputStride && inputs);
833
834		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
835		{
836			const int	groupOffset		= groupNdx*workGroupSize;
837			const int	groupOutput		= *(const deInt32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
838			set<int>	usedValues;
839
840			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
841			{
842				const int outputValue = *(const deInt32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
843
844				if (!de::inRange(outputValue, 0, workGroupSize) || usedValues.find(outputValue) != usedValues.end())
845				{
846					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
847														   << ": found unexpected value " << outputValue
848									   << TestLog::EndMessage;
849					return false;
850				}
851				usedValues.insert(outputValue);
852			}
853
854			if (!de::inRange(groupOutput, 0, workGroupSize) || usedValues.find(groupOutput) != usedValues.end())
855			{
856				m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": unexpected final value" << groupOutput << TestLog::EndMessage;
857				return false;
858			}
859		}
860
861		return true;
862	}
863};
864
865class ShaderAtomicCompSwapCase : public TestCase
866{
867public:
868									ShaderAtomicCompSwapCase	(Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision);
869									~ShaderAtomicCompSwapCase	(void);
870
871	void							init						(void);
872	void							deinit						(void);
873	IterateResult					iterate						(void);
874
875protected:
876
877private:
878									ShaderAtomicCompSwapCase	(const ShaderAtomicCompSwapCase& other);
879	ShaderAtomicCompSwapCase&		operator=					(const ShaderAtomicCompSwapCase& other);
880
881	const AtomicOperandType			m_operandType;
882	const DataType					m_type;
883	const Precision					m_precision;
884
885	const UVec3						m_workGroupSize;
886	const UVec3						m_numWorkGroups;
887
888	ShaderProgram*					m_program;
889};
890
891ShaderAtomicCompSwapCase::ShaderAtomicCompSwapCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
892	: TestCase			(context, name, "atomicCompSwap() Test")
893	, m_operandType		(operandType)
894	, m_type			(type)
895	, m_precision		(precision)
896	, m_workGroupSize	(3,2,1)
897	, m_numWorkGroups	(4,4,4)
898	, m_program			(DE_NULL)
899{
900}
901
902ShaderAtomicCompSwapCase::~ShaderAtomicCompSwapCase (void)
903{
904	ShaderAtomicCompSwapCase::deinit();
905}
906
907void ShaderAtomicCompSwapCase::init (void)
908{
909	const bool			isSSBO		= m_operandType == ATOMIC_OPERAND_BUFFER_VARIABLE;
910	const char*			precName	= getPrecisionName(m_precision);
911	const char*			typeName	= getDataTypeName(m_type);
912	const deUint32		numValues	= product(m_workGroupSize)*product(m_numWorkGroups);
913	std::ostringstream	src;
914
915	src << "#version 310 es\n"
916		<< "layout(local_size_x = " << m_workGroupSize.x()
917		<< ", local_size_y = " << m_workGroupSize.y()
918		<< ", local_size_z = " << m_workGroupSize.z() << ") in;\n"
919		<< "layout(binding = 0) buffer InOut\n"
920		<< "{\n"
921		<< "	" << precName << " " << typeName << " compareValues[" << numValues << "];\n"
922		<< "	" << precName << " " << typeName << " exchangeValues[" << numValues << "];\n"
923		<< "	" << precName << " " << typeName << " outputValues[" << numValues << "];\n"
924		<< "	" << (isSSBO ? "coherent " : "") << precName << " " << typeName << " groupValues[" << product(m_numWorkGroups) << "];\n"
925		<< "} sb_inout;\n";
926
927	if (!isSSBO)
928		src << "shared " << precName << " " << typeName << " s_var;\n";
929
930	src << "\n"
931		<< "void main (void)\n"
932		<< "{\n"
933		<< "	uint localSize  = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z;\n"
934		<< "	uint globalNdx  = gl_NumWorkGroups.x*gl_NumWorkGroups.y*gl_WorkGroupID.z + gl_NumWorkGroups.x*gl_WorkGroupID.y + gl_WorkGroupID.x;\n"
935		<< "	uint globalOffs = localSize*globalNdx;\n"
936		<< "	uint offset     = globalOffs + gl_LocalInvocationIndex;\n"
937		<< "\n";
938
939	if (!isSSBO)
940	{
941		src << "	if (gl_LocalInvocationIndex == 0u)\n"
942			<< "		s_var = " << typeName << "(" << 0 << ");\n"
943			<< "\n";
944	}
945
946	src << "	" << precName << " " << typeName << " compare = sb_inout.compareValues[offset];\n"
947		<< "	" << precName << " " << typeName << " exchange = sb_inout.exchangeValues[offset];\n"
948		<< "	" << precName << " " << typeName << " result;\n"
949		<< "	bool swapDone = false;\n"
950		<< "\n"
951		<< "	for (uint ndx = 0u; ndx < localSize; ndx++)\n"
952		<< "	{\n"
953		<< "		barrier();\n"
954		<< "		if (!swapDone)\n"
955		<< "		{\n"
956		<< "			result = atomicCompSwap(" << (isSSBO ? "sb_inout.groupValues[globalNdx]" : "s_var") << ", compare, exchange);\n"
957		<< "			if (result == compare)\n"
958		<< "				swapDone = true;\n"
959		<< "		}\n"
960		<< "	}\n"
961		<< "\n"
962		<< "	sb_inout.outputValues[offset] = result;\n";
963
964	if (!isSSBO)
965	{
966		src << "	barrier();\n"
967			<< "	if (gl_LocalInvocationIndex == 0u)\n"
968			<< "		sb_inout.groupValues[globalNdx] = s_var;\n";
969	}
970
971	src << "}\n";
972
973	DE_ASSERT(!m_program);
974	m_program = new ShaderProgram(m_context.getRenderContext(), ProgramSources() << ComputeSource(src.str()));
975
976	m_testCtx.getLog() << *m_program;
977
978	if (!m_program->isOk())
979	{
980		delete m_program;
981		m_program = DE_NULL;
982		throw tcu::TestError("Compile failed");
983	}
984}
985
986void ShaderAtomicCompSwapCase::deinit (void)
987{
988	delete m_program;
989	m_program = DE_NULL;
990}
991
992ShaderAtomicOpCase::IterateResult ShaderAtomicCompSwapCase::iterate (void)
993{
994	const glw::Functions&		gl				= m_context.getRenderContext().getFunctions();
995	const deUint32				program			= m_program->getProgram();
996	const Buffer				inoutBuffer		(m_context.getRenderContext());
997	const deUint32				blockNdx		= gl.getProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, "InOut");
998	const InterfaceBlockInfo	blockInfo		= getProgramInterfaceBlockInfo(gl, program, GL_SHADER_STORAGE_BLOCK, blockNdx);
999	const deUint32				cmpVarNdx		= gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.compareValues[0]");
1000	const InterfaceVariableInfo	cmpVarInfo		= getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, cmpVarNdx);
1001	const deUint32				exhVarNdx		= gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.exchangeValues[0]");
1002	const InterfaceVariableInfo	exhVarInfo		= getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, exhVarNdx);
1003	const deUint32				outVarNdx		= gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.outputValues[0]");
1004	const InterfaceVariableInfo	outVarInfo		= getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, outVarNdx);
1005	const deUint32				groupVarNdx		= gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.groupValues[0]");
1006	const InterfaceVariableInfo	groupVarInfo	= getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, groupVarNdx);
1007	const deUint32				numValues		= product(m_workGroupSize)*product(m_numWorkGroups);
1008
1009	TCU_CHECK(cmpVarInfo.arraySize == numValues &&
1010			  exhVarInfo.arraySize == numValues &&
1011			  outVarInfo.arraySize == numValues &&
1012			  groupVarInfo.arraySize == product(m_numWorkGroups));
1013
1014	gl.useProgram(program);
1015
1016	// \todo [2013-09-05 pyry] Use randomized input values!
1017
1018	// Setup buffer.
1019	{
1020		const deUint32	workGroupSize	= product(m_workGroupSize);
1021		vector<deUint8>	bufData			(blockInfo.dataSize);
1022
1023		std::fill(bufData.begin(), bufData.end(), 0);
1024
1025		for (deUint32 ndx = 0; ndx < numValues; ndx++)
1026			*(deUint32*)(&bufData[0] + cmpVarInfo.offset + cmpVarInfo.arrayStride*ndx) = ndx%workGroupSize;
1027
1028		for (deUint32 ndx = 0; ndx < numValues; ndx++)
1029			*(deUint32*)(&bufData[0] + exhVarInfo.offset + exhVarInfo.arrayStride*ndx) = (ndx%workGroupSize)+1;
1030
1031		gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *inoutBuffer);
1032		gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockInfo.dataSize, &bufData[0], GL_STATIC_READ);
1033		gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *inoutBuffer);
1034		GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
1035	}
1036
1037	gl.dispatchCompute(m_numWorkGroups.x(), m_numWorkGroups.y(), m_numWorkGroups.z());
1038
1039	// Read back and compare
1040	{
1041		const void*		resPtr			= gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, blockInfo.dataSize, GL_MAP_READ_BIT);
1042		const int		numWorkGroups	= (int)product(m_numWorkGroups);
1043		const int		workGroupSize	= (int)product(m_workGroupSize);
1044		bool			isOk			= true;
1045
1046		GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
1047		TCU_CHECK(resPtr);
1048
1049		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
1050		{
1051			const int	groupOffset		= groupNdx*workGroupSize;
1052			const int	groupOutput		= *(const deInt32*)((const deUint8*)resPtr + groupVarInfo.offset + groupNdx*groupVarInfo.arrayStride);
1053
1054			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
1055			{
1056				const int	refValue		= localNdx;
1057				const int	outputValue		= *(const deInt32*)((const deUint8*)resPtr + outVarInfo.offset + outVarInfo.arrayStride*(groupOffset+localNdx));
1058
1059				if (outputValue != refValue)
1060				{
1061					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
1062														   << ": expected " << refValue << ", got " << outputValue
1063									   << TestLog::EndMessage;
1064					isOk = false;
1065					break;
1066				}
1067			}
1068
1069			if (groupOutput != workGroupSize)
1070			{
1071				m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected" << workGroupSize << ", got " << groupOutput << TestLog::EndMessage;
1072				isOk = false;
1073				break;
1074			}
1075		}
1076
1077		gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
1078		GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer()");
1079
1080		m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
1081								isOk ? "Pass"				: "Comparison failed");
1082	}
1083
1084	return STOP;
1085}
1086
1087ShaderAtomicOpTests::ShaderAtomicOpTests (Context& context, const char* name, AtomicOperandType operandType)
1088	: TestCaseGroup	(context, name, "Atomic Operation Tests")
1089	, m_operandType	(operandType)
1090{
1091}
1092
1093ShaderAtomicOpTests::~ShaderAtomicOpTests (void)
1094{
1095}
1096
1097template<typename T>
1098static tcu::TestCaseGroup* createAtomicOpGroup (Context& context, AtomicOperandType operandType, const char* groupName)
1099{
1100	tcu::TestCaseGroup *const group = new tcu::TestCaseGroup(context.getTestContext(), groupName, (string("Atomic ") + groupName).c_str());
1101	try
1102	{
1103		for (int precNdx = 0; precNdx < PRECISION_LAST; precNdx++)
1104		{
1105			for (int typeNdx = 0; typeNdx < 2; typeNdx++)
1106			{
1107				const Precision		precision		= Precision(precNdx);
1108				const DataType		type			= typeNdx > 0 ? TYPE_INT : TYPE_UINT;
1109				const string		caseName		= string(getPrecisionName(precision)) + "_" + getDataTypeName(type);
1110
1111				group->addChild(new T(context, caseName.c_str(), operandType, type, precision));
1112			}
1113		}
1114
1115		return group;
1116	}
1117	catch (...)
1118	{
1119		delete group;
1120		throw;
1121	}
1122}
1123
1124void ShaderAtomicOpTests::init (void)
1125{
1126	addChild(createAtomicOpGroup<ShaderAtomicAddCase>		(m_context, m_operandType, "add"));
1127	addChild(createAtomicOpGroup<ShaderAtomicMinCase>		(m_context, m_operandType, "min"));
1128	addChild(createAtomicOpGroup<ShaderAtomicMaxCase>		(m_context, m_operandType, "max"));
1129	addChild(createAtomicOpGroup<ShaderAtomicAndCase>		(m_context, m_operandType, "and"));
1130	addChild(createAtomicOpGroup<ShaderAtomicOrCase>		(m_context, m_operandType, "or"));
1131	addChild(createAtomicOpGroup<ShaderAtomicXorCase>		(m_context, m_operandType, "xor"));
1132	addChild(createAtomicOpGroup<ShaderAtomicExchangeCase>	(m_context, m_operandType, "exchange"));
1133	addChild(createAtomicOpGroup<ShaderAtomicCompSwapCase>	(m_context, m_operandType, "compswap"));
1134}
1135
1136} // Functional
1137} // gles31
1138} // deqp
1139