• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.1 Module
3  * -------------------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief 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 
42 namespace deqp
43 {
44 namespace gles31
45 {
46 namespace Functional
47 {
48 
49 using std::string;
50 using std::vector;
51 using tcu::TestLog;
52 using tcu::UVec3;
53 using std::set;
54 using namespace glu;
55 
56 template<typename T, int Size>
product(const tcu::Vector<T,Size> & v)57 static 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 
65 class ShaderAtomicOpCase : public TestCase
66 {
67 public:
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 
75 protected:
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 
89 private:
90 							ShaderAtomicOpCase	(const ShaderAtomicOpCase& other);
91 	ShaderAtomicOpCase&		operator=			(const ShaderAtomicOpCase& other);
92 
93 	ShaderProgram*			m_program;
94 };
95 
ShaderAtomicOpCase(Context & context,const char * name,const char * funcName,AtomicOperandType operandType,DataType type,Precision precision,const UVec3 & workGroupSize)96 ShaderAtomicOpCase::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 
~ShaderAtomicOpCase(void)109 ShaderAtomicOpCase::~ShaderAtomicOpCase (void)
110 {
111 	ShaderAtomicOpCase::deinit();
112 }
113 
init(void)114 void 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 
deinit(void)184 void ShaderAtomicOpCase::deinit (void)
185 {
186 	delete m_program;
187 	m_program = DE_NULL;
188 }
189 
iterate(void)190 ShaderAtomicOpCase::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 
255 class ShaderAtomicAddCase : public ShaderAtomicOpCase
256 {
257 public:
ShaderAtomicAddCase(Context & context,const char * name,AtomicOperandType operandType,DataType type,Precision precision)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 
264 protected:
getInputs(int numValues,int stride,void * inputs) const265 	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 
verify(int numValues,int inputStride,const void * inputs,int outputStride,const void * outputs,int groupStride,const void * groupOutputs) const277 	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 
getPrecisionNumIntegerBits(glu::Precision precision)347 static 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 
getPrecisionMask(int numPreciseBits)360 static 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 
intEqualsAfterUintCast(deInt32 value,deUint32 casted,glu::Precision precision)366 static 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 
containsAfterUintCast(const std::set<deInt32> & haystack,deUint32 needle,glu::Precision precision)393 static 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 
containsAfterUintCast(const std::set<deUint32> & haystack,deInt32 needle,glu::Precision precision)401 static 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 
409 class ShaderAtomicMinCase : public ShaderAtomicOpCase
410 {
411 public:
ShaderAtomicMinCase(Context & context,const char * name,AtomicOperandType operandType,DataType type,Precision precision)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 
418 protected:
getInputs(int numValues,int stride,void * inputs) const419 	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 
verify(int numValues,int inputStride,const void * inputs,int outputStride,const void * outputs,int groupStride,const void * groupOutputs) const430 	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 
490 class ShaderAtomicMaxCase : public ShaderAtomicOpCase
491 {
492 public:
ShaderAtomicMaxCase(Context & context,const char * name,AtomicOperandType operandType,DataType type,Precision precision)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 
500 protected:
getInputs(int numValues,int stride,void * inputs) const501 	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 
verify(int numValues,int inputStride,const void * inputs,int outputStride,const void * outputs,int groupStride,const void * groupOutputs) const512 	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 
572 class ShaderAtomicAndCase : public ShaderAtomicOpCase
573 {
574 public:
ShaderAtomicAndCase(Context & context,const char * name,AtomicOperandType operandType,DataType type,Precision precision)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 
584 protected:
getInputs(int numValues,int stride,void * inputs) const585 	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 
verify(int numValues,int inputStride,const void * inputs,int outputStride,const void * outputs,int groupStride,const void * groupOutputs) const604 	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 
648 class ShaderAtomicOrCase : public ShaderAtomicOpCase
649 {
650 public:
ShaderAtomicOrCase(Context & context,const char * name,AtomicOperandType operandType,DataType type,Precision precision)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 
657 protected:
getInputs(int numValues,int stride,void * inputs) const658 	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 
verify(int numValues,int inputStride,const void * inputs,int outputStride,const void * outputs,int groupStride,const void * groupOutputs) const675 	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 
719 class ShaderAtomicXorCase : public ShaderAtomicOpCase
720 {
721 public:
ShaderAtomicXorCase(Context & context,const char * name,AtomicOperandType operandType,DataType type,Precision precision)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 
728 protected:
getInputs(int numValues,int stride,void * inputs) const729 	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 
verify(int numValues,int inputStride,const void * inputs,int outputStride,const void * outputs,int groupStride,const void * groupOutputs) const748 	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		= 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 
803 class ShaderAtomicExchangeCase : public ShaderAtomicOpCase
804 {
805 public:
ShaderAtomicExchangeCase(Context & context,const char * name,AtomicOperandType operandType,DataType type,Precision precision)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 
812 protected:
getInputs(int numValues,int stride,void * inputs) const813 	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 
verify(int numValues,int inputStride,const void * inputs,int outputStride,const void * outputs,int groupStride,const void * groupOutputs) const827 	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 
865 class ShaderAtomicCompSwapCase : public TestCase
866 {
867 public:
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 
875 protected:
876 
877 private:
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 
ShaderAtomicCompSwapCase(Context & context,const char * name,AtomicOperandType operandType,DataType type,Precision precision)891 ShaderAtomicCompSwapCase::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 
~ShaderAtomicCompSwapCase(void)902 ShaderAtomicCompSwapCase::~ShaderAtomicCompSwapCase (void)
903 {
904 	ShaderAtomicCompSwapCase::deinit();
905 }
906 
init(void)907 void 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 
deinit(void)986 void ShaderAtomicCompSwapCase::deinit (void)
987 {
988 	delete m_program;
989 	m_program = DE_NULL;
990 }
991 
iterate(void)992 ShaderAtomicOpCase::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 
ShaderAtomicOpTests(Context & context,const char * name,AtomicOperandType operandType)1087 ShaderAtomicOpTests::ShaderAtomicOpTests (Context& context, const char* name, AtomicOperandType operandType)
1088 	: TestCaseGroup	(context, name, "Atomic Operation Tests")
1089 	, m_operandType	(operandType)
1090 {
1091 }
1092 
~ShaderAtomicOpTests(void)1093 ShaderAtomicOpTests::~ShaderAtomicOpTests (void)
1094 {
1095 }
1096 
1097 template<typename T>
createAtomicOpGroup(Context & context,AtomicOperandType operandType,const char * groupName)1098 static 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 
init(void)1124 void 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