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