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 
    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 
    184 void ShaderAtomicOpCase::deinit (void)
    185 {
    186 	delete m_program;
    187 	m_program = DE_NULL;
    188 }
    189 
    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:
    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:
    265 	void getInputs (int numValues, int stride, void* inputs) const
    266 	{
    267 		de::Random	rnd			(deStringHash(getName()));
    268 		const int	maxVal		= m_precision == PRECISION_LOWP ? 2 : 32;
    269 		const int	minVal		= 1;
    270 
    271 		// \todo [2013-09-04 pyry] Negative values!
    272 
    273 		for (int valNdx = 0; valNdx < numValues; valNdx++)
    274 			*(int*)((deUint8*)inputs + stride*valNdx) = rnd.getInt(minVal, maxVal);
    275 	}
    276 
    277 	bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
    278 	{
    279 		const int	workGroupSize	= (int)product(m_workGroupSize);
    280 		const int	numWorkGroups	= numValues/workGroupSize;
    281 
    282 		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
    283 		{
    284 			const int	groupOffset		= groupNdx*workGroupSize;
    285 			const int	groupOutput		= *(const int*)((const deUint8*)groupOutputs + groupNdx*groupStride);
    286 			set<int>	outValues;
    287 			bool		maxFound		= false;
    288 			int			valueSum		= (int)m_initialValue;
    289 
    290 			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
    291 			{
    292 				const int inputValue = *(const int*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
    293 				valueSum += inputValue;
    294 			}
    295 
    296 			if (groupOutput != valueSum)
    297 			{
    298 				m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected sum " << valueSum << ", got " << groupOutput << TestLog::EndMessage;
    299 				return false;
    300 			}
    301 
    302 			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
    303 			{
    304 				const int	inputValue		= *(const int*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
    305 				const int	outputValue		= *(const int*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
    306 
    307 				if (!de::inRange(outputValue, (int)m_initialValue, valueSum-inputValue))
    308 				{
    309 					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
    310 														   << ": expected value in range [" << m_initialValue << ", " << (valueSum-inputValue)
    311 														   << "], got " << outputValue
    312 									   << TestLog::EndMessage;
    313 					return false;
    314 				}
    315 
    316 				if (outValues.find(outputValue) != outValues.end())
    317 				{
    318 					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
    319 														   << ": found duplicate value " << outputValue
    320 									   << TestLog::EndMessage;
    321 					return false;
    322 				}
    323 
    324 				outValues.insert(outputValue);
    325 				if (outputValue == valueSum-inputValue)
    326 					maxFound = true;
    327 			}
    328 
    329 			if (!maxFound)
    330 			{
    331 				m_testCtx.getLog() << TestLog::Message << "ERROR: could not find maximum expected value from group " << groupNdx << TestLog::EndMessage;
    332 				return false;
    333 			}
    334 
    335 			if (outValues.find((int)m_initialValue) == outValues.end())
    336 			{
    337 				m_testCtx.getLog() << TestLog::Message << "ERROR: could not find initial value from group " << groupNdx << TestLog::EndMessage;
    338 				return false;
    339 			}
    340 		}
    341 
    342 		return true;
    343 	}
    344 };
    345 
    346 
    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 
    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 
    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 
    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 
    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:
    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:
    419 	void getInputs (int numValues, int stride, void* inputs) const
    420 	{
    421 		de::Random	rnd			(deStringHash(getName()));
    422 		const bool	isSigned	= m_type == TYPE_INT;
    423 		const int	maxVal		= m_precision == PRECISION_LOWP ? 100 : 1000;
    424 		const int	minVal		= isSigned ? -maxVal : 0;
    425 
    426 		for (int valNdx = 0; valNdx < numValues; valNdx++)
    427 			*(int*)((deUint8*)inputs + stride*valNdx) = rnd.getInt(minVal, maxVal);
    428 	}
    429 
    430 	bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
    431 	{
    432 		const int	workGroupSize	= (int)product(m_workGroupSize);
    433 		const int	numWorkGroups	= numValues/workGroupSize;
    434 		bool		anyError		= false;
    435 
    436 		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
    437 		{
    438 			const int		groupOffset		= groupNdx*workGroupSize;
    439 			const deUint32	groupOutput		= *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
    440 			set<deInt32>	inValues;
    441 			set<deUint32>	outValues;
    442 			int				minValue		= (int)m_initialValue;
    443 
    444 			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
    445 			{
    446 				const deInt32 inputValue = *(const deInt32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
    447 				inValues.insert(inputValue);
    448 				minValue = de::min(inputValue, minValue);
    449 			}
    450 
    451 			if (!intEqualsAfterUintCast(minValue, groupOutput, m_precision))
    452 			{
    453 				m_testCtx.getLog()
    454 					<< TestLog::Message
    455 					<< "ERROR: at group " << groupNdx
    456 					<< ": expected minimum " << minValue << " (" << tcu::Format::Hex<8>((deUint32)minValue) << ")"
    457 					<< ", got " << groupOutput << " (" << tcu::Format::Hex<8>(groupOutput) << ")"
    458 					<< TestLog::EndMessage;
    459 				anyError = true;
    460 			}
    461 
    462 			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
    463 			{
    464 				const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
    465 
    466 				if (!containsAfterUintCast(inValues, outputValue, m_precision) &&
    467 					!intEqualsAfterUintCast((deInt32)m_initialValue, outputValue, m_precision))
    468 				{
    469 					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
    470 														   << ": found unexpected value " << outputValue
    471 														   << " (" << tcu::Format::Hex<8>(outputValue) << ")"
    472 									   << TestLog::EndMessage;
    473 					anyError = true;
    474 				}
    475 
    476 				outValues.insert(outputValue);
    477 			}
    478 
    479 			if (!containsAfterUintCast(outValues, (int)m_initialValue, m_precision))
    480 			{
    481 				m_testCtx.getLog() << TestLog::Message << "ERROR: could not find initial value from group " << groupNdx << TestLog::EndMessage;
    482 				anyError = true;
    483 			}
    484 		}
    485 
    486 		return !anyError;
    487 	}
    488 };
    489 
    490 class ShaderAtomicMaxCase : public ShaderAtomicOpCase
    491 {
    492 public:
    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:
    501 	void getInputs (int numValues, int stride, void* inputs) const
    502 	{
    503 		de::Random	rnd			(deStringHash(getName()));
    504 		const bool	isSigned	= m_type == TYPE_INT;
    505 		const int	maxVal		= m_precision == PRECISION_LOWP ? 100 : 1000;
    506 		const int	minVal		= isSigned ? -maxVal : 0;
    507 
    508 		for (int valNdx = 0; valNdx < numValues; valNdx++)
    509 			*(int*)((deUint8*)inputs + stride*valNdx) = rnd.getInt(minVal, maxVal);
    510 	}
    511 
    512 	bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
    513 	{
    514 		const int	workGroupSize	= (int)product(m_workGroupSize);
    515 		const int	numWorkGroups	= numValues/workGroupSize;
    516 		bool		anyError		= false;
    517 
    518 		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
    519 		{
    520 			const int		groupOffset		= groupNdx*workGroupSize;
    521 			const deUint32	groupOutput		= *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
    522 			set<int>		inValues;
    523 			set<deUint32>	outValues;
    524 			int				maxValue		= (int)m_initialValue;
    525 
    526 			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
    527 			{
    528 				const deInt32 inputValue = *(const deInt32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
    529 				inValues.insert(inputValue);
    530 				maxValue = de::max(maxValue, inputValue);
    531 			}
    532 
    533 			if (!intEqualsAfterUintCast(maxValue, groupOutput, m_precision))
    534 			{
    535 				m_testCtx.getLog()
    536 					<< TestLog::Message
    537 					<< "ERROR: at group " << groupNdx
    538 					<< ": expected maximum " << maxValue << " (" << tcu::Format::Hex<8>((deUint32)maxValue) << ")"
    539 					<< ", got " << groupOutput << " (" << tcu::Format::Hex<8>(groupOutput) << ")"
    540 					<< TestLog::EndMessage;
    541 				anyError = true;
    542 			}
    543 
    544 			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
    545 			{
    546 				const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
    547 
    548 				if (!containsAfterUintCast(inValues, outputValue, m_precision) &&
    549 					!intEqualsAfterUintCast((deInt32)m_initialValue, outputValue, m_precision))
    550 				{
    551 					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
    552 														   << ": found unexpected value " << outputValue
    553 														   << " (" << tcu::Format::Hex<8>(outputValue) << ")"
    554 									   << TestLog::EndMessage;
    555 					anyError = true;
    556 				}
    557 
    558 				outValues.insert(outputValue);
    559 			}
    560 
    561 			if (!containsAfterUintCast(outValues, (int)m_initialValue, m_precision))
    562 			{
    563 				m_testCtx.getLog() << TestLog::Message << "ERROR: could not find initial value from group " << groupNdx << TestLog::EndMessage;
    564 				anyError = true;
    565 			}
    566 		}
    567 
    568 		return !anyError;
    569 	}
    570 };
    571 
    572 class ShaderAtomicAndCase : public ShaderAtomicOpCase
    573 {
    574 public:
    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:
    585 	void getInputs (int numValues, int stride, void* inputs) const
    586 	{
    587 		de::Random		rnd				(deStringHash(getName()));
    588 		const int		workGroupSize	= (int)product(m_workGroupSize);
    589 		const int		numWorkGroups	= numValues/workGroupSize;
    590 		const int		numBits			= m_precision == PRECISION_HIGHP ? 32 :
    591 										  m_precision == PRECISION_MEDIUMP ? 16 : 8;
    592 		const deUint32	valueMask		= numBits == 32 ? ~0u : (1u<<numBits)-1u;
    593 
    594 		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
    595 		{
    596 			const int		groupOffset		= groupNdx*workGroupSize;
    597 			const deUint32	groupMask		= 1<<rnd.getInt(0, numBits-2); // One bit is always set.
    598 
    599 			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
    600 				*(deUint32*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = (rnd.getUint32() & valueMask) | groupMask;
    601 		}
    602 	}
    603 
    604 	bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
    605 	{
    606 		const int	workGroupSize	= (int)product(m_workGroupSize);
    607 		const int	numWorkGroups	= numValues/workGroupSize;
    608 
    609 		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
    610 		{
    611 			const int		groupOffset		= groupNdx*workGroupSize;
    612 			const deUint32	groupOutput		= *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
    613 			deUint32		expectedValue	= m_initialValue;
    614 
    615 			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
    616 			{
    617 				const deUint32 inputValue = *(const deUint32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
    618 				expectedValue &= inputValue;
    619 			}
    620 
    621 			if (expectedValue != groupOutput)
    622 			{
    623 				m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected " << tcu::toHex(expectedValue) << ", got " << tcu::toHex(groupOutput) << TestLog::EndMessage;
    624 				return false;
    625 			}
    626 
    627 			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
    628 			{
    629 				const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
    630 
    631 				if ((outputValue & ~m_initialValue) != 0)
    632 				{
    633 					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
    634 														   << ": found unexpected value " << tcu::toHex(outputValue)
    635 									   << TestLog::EndMessage;
    636 					return false;
    637 				}
    638 			}
    639 		}
    640 
    641 		return true;
    642 	}
    643 };
    644 
    645 class ShaderAtomicOrCase : public ShaderAtomicOpCase
    646 {
    647 public:
    648 	ShaderAtomicOrCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
    649 		: ShaderAtomicOpCase(context, name, "atomicOr", operandType, type, precision, UVec3(3,2,1))
    650 	{
    651 		m_initialValue = 1u; // Lowest bit set.
    652 	}
    653 
    654 protected:
    655 	void getInputs (int numValues, int stride, void* inputs) const
    656 	{
    657 		de::Random		rnd				(deStringHash(getName()));
    658 		const int		workGroupSize	= (int)product(m_workGroupSize);
    659 		const int		numWorkGroups	= numValues/workGroupSize;
    660 		const int		numBits			= m_precision == PRECISION_HIGHP ? 32 :
    661 										  m_precision == PRECISION_MEDIUMP ? 16 : 8;
    662 
    663 		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
    664 		{
    665 			const int groupOffset = groupNdx*workGroupSize;
    666 
    667 			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
    668 				*(deUint32*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = 1u<<rnd.getInt(0, numBits-1);
    669 		}
    670 	}
    671 
    672 	bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
    673 	{
    674 		const int	workGroupSize	= (int)product(m_workGroupSize);
    675 		const int	numWorkGroups	= numValues/workGroupSize;
    676 
    677 		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
    678 		{
    679 			const int		groupOffset		= groupNdx*workGroupSize;
    680 			const deUint32	groupOutput		= *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
    681 			deUint32		expectedValue	= m_initialValue;
    682 
    683 			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
    684 			{
    685 				const deUint32 inputValue = *(const deUint32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
    686 				expectedValue |= inputValue;
    687 			}
    688 
    689 			if (expectedValue != groupOutput)
    690 			{
    691 				m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected " << tcu::toHex(expectedValue) << ", got " << tcu::toHex(groupOutput) << TestLog::EndMessage;
    692 				return false;
    693 			}
    694 
    695 			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
    696 			{
    697 				const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
    698 
    699 				if ((outputValue & m_initialValue) == 0)
    700 				{
    701 					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
    702 														   << ": found unexpected value " << tcu::toHex(outputValue)
    703 									   << TestLog::EndMessage;
    704 					return false;
    705 				}
    706 			}
    707 		}
    708 
    709 		return true;
    710 	}
    711 };
    712 
    713 class ShaderAtomicXorCase : public ShaderAtomicOpCase
    714 {
    715 public:
    716 	ShaderAtomicXorCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
    717 		: ShaderAtomicOpCase(context, name, "atomicXor", operandType, type, precision, UVec3(3,2,1))
    718 	{
    719 		m_initialValue = 0;
    720 	}
    721 
    722 protected:
    723 	void getInputs (int numValues, int stride, void* inputs) const
    724 	{
    725 		de::Random		rnd				(deStringHash(getName()));
    726 		const int		workGroupSize	= (int)product(m_workGroupSize);
    727 		const int		numWorkGroups	= numValues/workGroupSize;
    728 
    729 		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
    730 		{
    731 			const int groupOffset = groupNdx*workGroupSize;
    732 
    733 			// First uses random bit-pattern.
    734 			*(deUint32*)((deUint8*)inputs + stride*(groupOffset)) = rnd.getUint32();
    735 
    736 			// Rest have either all or no bits set.
    737 			for (int localNdx = 1; localNdx < workGroupSize; localNdx++)
    738 				*(deUint32*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = rnd.getBool() ? ~0u : 0u;
    739 		}
    740 	}
    741 
    742 	bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
    743 	{
    744 		const int		workGroupSize	= (int)product(m_workGroupSize);
    745 		const int		numWorkGroups	= numValues/workGroupSize;
    746 		const int		numBits			= m_precision == PRECISION_HIGHP ? 32 :
    747 										  m_precision == PRECISION_MEDIUMP ? 16 : 8;
    748 		const deUint32	compareMask		= numBits == 32 ? ~0u : (1u<<numBits)-1u;
    749 
    750 		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
    751 		{
    752 			const int		groupOffset		= groupNdx*workGroupSize;
    753 			const deUint32	groupOutput		= *(const deUint32*)((const deUint8*)groupOutputs + groupNdx*groupStride);
    754 			const deUint32	randomValue		= *(const int*)((const deUint8*)inputs + inputStride*groupOffset);
    755 			const deUint32	expected0		= randomValue ^ 0u;
    756 			const deUint32	expected1		= randomValue ^ ~0u;
    757 			int				numXorZeros		= (m_initialValue == 0) ? 1 : 0;
    758 
    759 			for (int localNdx = 1; localNdx < workGroupSize; localNdx++)
    760 			{
    761 				const deUint32 inputValue = *(const deUint32*)((const deUint8*)inputs + inputStride*(groupOffset+localNdx));
    762 				if (inputValue == 0)
    763 					numXorZeros += 1;
    764 			}
    765 
    766 			const deUint32 expected = (numXorZeros%2 == 0) ? expected0 : expected1;
    767 
    768 			if ((groupOutput & compareMask) != (expected & compareMask))
    769 			{
    770 				m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected " << tcu::toHex(expected0)
    771 													   << " or " << tcu::toHex(expected1) << " (compare mask " << tcu::toHex(compareMask)
    772 													   << "), got " << tcu::toHex(groupOutput) << TestLog::EndMessage;
    773 				return false;
    774 			}
    775 
    776 			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
    777 			{
    778 				const deUint32 outputValue = *(const deUint32*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
    779 
    780 				if ((outputValue&compareMask) != 0 &&
    781 					(outputValue&compareMask) != compareMask &&
    782 					(outputValue&compareMask) != (expected0&compareMask) &&
    783 					(outputValue&compareMask) != (expected1&compareMask))
    784 				{
    785 					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
    786 														   << ": found unexpected value " << tcu::toHex(outputValue)
    787 									   << TestLog::EndMessage;
    788 					return false;
    789 				}
    790 			}
    791 		}
    792 
    793 		return true;
    794 	}
    795 };
    796 
    797 class ShaderAtomicExchangeCase : public ShaderAtomicOpCase
    798 {
    799 public:
    800 	ShaderAtomicExchangeCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
    801 		: ShaderAtomicOpCase(context, name, "atomicExchange", operandType, type, precision, UVec3(3,2,1))
    802 	{
    803 		m_initialValue = 0;
    804 	}
    805 
    806 protected:
    807 	void getInputs (int numValues, int stride, void* inputs) const
    808 	{
    809 		const int	workGroupSize	= (int)product(m_workGroupSize);
    810 		const int	numWorkGroups	= numValues/workGroupSize;
    811 
    812 		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
    813 		{
    814 			const int groupOffset = groupNdx*workGroupSize;
    815 
    816 			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
    817 				*(int*)((deUint8*)inputs + stride*(groupOffset+localNdx)) = localNdx+1;
    818 		}
    819 	}
    820 
    821 	bool verify (int numValues, int inputStride, const void* inputs, int outputStride, const void* outputs, int groupStride, const void* groupOutputs) const
    822 	{
    823 		const int	workGroupSize	= (int)product(m_workGroupSize);
    824 		const int	numWorkGroups	= numValues/workGroupSize;
    825 
    826 		DE_UNREF(inputStride && inputs);
    827 
    828 		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
    829 		{
    830 			const int	groupOffset		= groupNdx*workGroupSize;
    831 			const int	groupOutput		= *(const int*)((const deUint8*)groupOutputs + groupNdx*groupStride);
    832 			set<int>	usedValues;
    833 
    834 			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
    835 			{
    836 				const int outputValue = *(const int*)((const deUint8*)outputs + outputStride*(groupOffset+localNdx));
    837 
    838 				if (!de::inRange(outputValue, 0, workGroupSize) || usedValues.find(outputValue) != usedValues.end())
    839 				{
    840 					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
    841 														   << ": found unexpected value " << outputValue
    842 									   << TestLog::EndMessage;
    843 					return false;
    844 				}
    845 				usedValues.insert(outputValue);
    846 			}
    847 
    848 			if (!de::inRange(groupOutput, 0, workGroupSize) || usedValues.find(groupOutput) != usedValues.end())
    849 			{
    850 				m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": unexpected final value" << groupOutput << TestLog::EndMessage;
    851 				return false;
    852 			}
    853 		}
    854 
    855 		return true;
    856 	}
    857 };
    858 
    859 class ShaderAtomicCompSwapCase : public TestCase
    860 {
    861 public:
    862 									ShaderAtomicCompSwapCase	(Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision);
    863 									~ShaderAtomicCompSwapCase	(void);
    864 
    865 	void							init						(void);
    866 	void							deinit						(void);
    867 	IterateResult					iterate						(void);
    868 
    869 protected:
    870 
    871 private:
    872 									ShaderAtomicCompSwapCase	(const ShaderAtomicCompSwapCase& other);
    873 	ShaderAtomicCompSwapCase&		operator=					(const ShaderAtomicCompSwapCase& other);
    874 
    875 	const AtomicOperandType			m_operandType;
    876 	const DataType					m_type;
    877 	const Precision					m_precision;
    878 
    879 	const UVec3						m_workGroupSize;
    880 	const UVec3						m_numWorkGroups;
    881 
    882 	ShaderProgram*					m_program;
    883 };
    884 
    885 ShaderAtomicCompSwapCase::ShaderAtomicCompSwapCase (Context& context, const char* name, AtomicOperandType operandType, DataType type, Precision precision)
    886 	: TestCase			(context, name, "atomicCompSwap() Test")
    887 	, m_operandType		(operandType)
    888 	, m_type			(type)
    889 	, m_precision		(precision)
    890 	, m_workGroupSize	(3,2,1)
    891 	, m_numWorkGroups	(4,4,4)
    892 	, m_program			(DE_NULL)
    893 {
    894 }
    895 
    896 ShaderAtomicCompSwapCase::~ShaderAtomicCompSwapCase (void)
    897 {
    898 	ShaderAtomicCompSwapCase::deinit();
    899 }
    900 
    901 void ShaderAtomicCompSwapCase::init (void)
    902 {
    903 	const bool			isSSBO		= m_operandType == ATOMIC_OPERAND_BUFFER_VARIABLE;
    904 	const char*			precName	= getPrecisionName(m_precision);
    905 	const char*			typeName	= getDataTypeName(m_type);
    906 	const deUint32		numValues	= product(m_workGroupSize)*product(m_numWorkGroups);
    907 	std::ostringstream	src;
    908 
    909 	src << "#version 310 es\n"
    910 		<< "layout(local_size_x = " << m_workGroupSize.x()
    911 		<< ", local_size_y = " << m_workGroupSize.y()
    912 		<< ", local_size_z = " << m_workGroupSize.z() << ") in;\n"
    913 		<< "layout(binding = 0) buffer InOut\n"
    914 		<< "{\n"
    915 		<< "	" << precName << " " << typeName << " compareValues[" << numValues << "];\n"
    916 		<< "	" << precName << " " << typeName << " exchangeValues[" << numValues << "];\n"
    917 		<< "	" << precName << " " << typeName << " outputValues[" << numValues << "];\n"
    918 		<< "	" << (isSSBO ? "coherent " : "") << precName << " " << typeName << " groupValues[" << product(m_numWorkGroups) << "];\n"
    919 		<< "} sb_inout;\n";
    920 
    921 	if (!isSSBO)
    922 		src << "shared " << precName << " " << typeName << " s_var;\n";
    923 
    924 	src << "\n"
    925 		<< "void main (void)\n"
    926 		<< "{\n"
    927 		<< "	uint localSize  = gl_WorkGroupSize.x*gl_WorkGroupSize.y*gl_WorkGroupSize.z;\n"
    928 		<< "	uint globalNdx  = gl_NumWorkGroups.x*gl_NumWorkGroups.y*gl_WorkGroupID.z + gl_NumWorkGroups.x*gl_WorkGroupID.y + gl_WorkGroupID.x;\n"
    929 		<< "	uint globalOffs = localSize*globalNdx;\n"
    930 		<< "	uint offset     = globalOffs + gl_LocalInvocationIndex;\n"
    931 		<< "\n";
    932 
    933 	if (!isSSBO)
    934 	{
    935 		src << "	if (gl_LocalInvocationIndex == 0u)\n"
    936 			<< "		s_var = " << typeName << "(" << 0 << ");\n"
    937 			<< "\n";
    938 	}
    939 
    940 	src << "	" << precName << " " << typeName << " compare = sb_inout.compareValues[offset];\n"
    941 		<< "	" << precName << " " << typeName << " exchange = sb_inout.exchangeValues[offset];\n"
    942 		<< "	" << precName << " " << typeName << " result;\n"
    943 		<< "	bool swapDone = false;\n"
    944 		<< "\n"
    945 		<< "	for (uint ndx = 0u; ndx < localSize; ndx++)\n"
    946 		<< "	{\n"
    947 		<< "		barrier();\n"
    948 		<< "		if (!swapDone)\n"
    949 		<< "		{\n"
    950 		<< "			result = atomicCompSwap(" << (isSSBO ? "sb_inout.groupValues[globalNdx]" : "s_var") << ", compare, exchange);\n"
    951 		<< "			if (result == compare)\n"
    952 		<< "				swapDone = true;\n"
    953 		<< "		}\n"
    954 		<< "	}\n"
    955 		<< "\n"
    956 		<< "	sb_inout.outputValues[offset] = result;\n";
    957 
    958 	if (!isSSBO)
    959 	{
    960 		src << "	barrier();\n"
    961 			<< "	if (gl_LocalInvocationIndex == 0u)\n"
    962 			<< "		sb_inout.groupValues[globalNdx] = s_var;\n";
    963 	}
    964 
    965 	src << "}\n";
    966 
    967 	DE_ASSERT(!m_program);
    968 	m_program = new ShaderProgram(m_context.getRenderContext(), ProgramSources() << ComputeSource(src.str()));
    969 
    970 	m_testCtx.getLog() << *m_program;
    971 
    972 	if (!m_program->isOk())
    973 	{
    974 		delete m_program;
    975 		m_program = DE_NULL;
    976 		throw tcu::TestError("Compile failed");
    977 	}
    978 }
    979 
    980 void ShaderAtomicCompSwapCase::deinit (void)
    981 {
    982 	delete m_program;
    983 	m_program = DE_NULL;
    984 }
    985 
    986 ShaderAtomicOpCase::IterateResult ShaderAtomicCompSwapCase::iterate (void)
    987 {
    988 	const glw::Functions&		gl				= m_context.getRenderContext().getFunctions();
    989 	const deUint32				program			= m_program->getProgram();
    990 	const Buffer				inoutBuffer		(m_context.getRenderContext());
    991 	const deUint32				blockNdx		= gl.getProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, "InOut");
    992 	const InterfaceBlockInfo	blockInfo		= getProgramInterfaceBlockInfo(gl, program, GL_SHADER_STORAGE_BLOCK, blockNdx);
    993 	const deUint32				cmpVarNdx		= gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.compareValues[0]");
    994 	const InterfaceVariableInfo	cmpVarInfo		= getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, cmpVarNdx);
    995 	const deUint32				exhVarNdx		= gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.exchangeValues[0]");
    996 	const InterfaceVariableInfo	exhVarInfo		= getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, exhVarNdx);
    997 	const deUint32				outVarNdx		= gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.outputValues[0]");
    998 	const InterfaceVariableInfo	outVarInfo		= getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, outVarNdx);
    999 	const deUint32				groupVarNdx		= gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "InOut.groupValues[0]");
   1000 	const InterfaceVariableInfo	groupVarInfo	= getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, groupVarNdx);
   1001 	const deUint32				numValues		= product(m_workGroupSize)*product(m_numWorkGroups);
   1002 
   1003 	TCU_CHECK(cmpVarInfo.arraySize == numValues &&
   1004 			  exhVarInfo.arraySize == numValues &&
   1005 			  outVarInfo.arraySize == numValues &&
   1006 			  groupVarInfo.arraySize == product(m_numWorkGroups));
   1007 
   1008 	gl.useProgram(program);
   1009 
   1010 	// \todo [2013-09-05 pyry] Use randomized input values!
   1011 
   1012 	// Setup buffer.
   1013 	{
   1014 		const deUint32	workGroupSize	= product(m_workGroupSize);
   1015 		vector<deUint8>	bufData			(blockInfo.dataSize);
   1016 
   1017 		std::fill(bufData.begin(), bufData.end(), 0);
   1018 
   1019 		for (deUint32 ndx = 0; ndx < numValues; ndx++)
   1020 			*(deUint32*)(&bufData[0] + cmpVarInfo.offset + cmpVarInfo.arrayStride*ndx) = ndx%workGroupSize;
   1021 
   1022 		for (deUint32 ndx = 0; ndx < numValues; ndx++)
   1023 			*(deUint32*)(&bufData[0] + exhVarInfo.offset + exhVarInfo.arrayStride*ndx) = (ndx%workGroupSize)+1;
   1024 
   1025 		gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *inoutBuffer);
   1026 		gl.bufferData(GL_SHADER_STORAGE_BUFFER, blockInfo.dataSize, &bufData[0], GL_STATIC_READ);
   1027 		gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *inoutBuffer);
   1028 		GLU_EXPECT_NO_ERROR(gl.getError(), "Output buffer setup failed");
   1029 	}
   1030 
   1031 	gl.dispatchCompute(m_numWorkGroups.x(), m_numWorkGroups.y(), m_numWorkGroups.z());
   1032 
   1033 	// Read back and compare
   1034 	{
   1035 		const void*		resPtr			= gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, blockInfo.dataSize, GL_MAP_READ_BIT);
   1036 		const int		numWorkGroups	= (int)product(m_numWorkGroups);
   1037 		const int		workGroupSize	= (int)product(m_workGroupSize);
   1038 		bool			isOk			= true;
   1039 
   1040 		GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
   1041 		TCU_CHECK(resPtr);
   1042 
   1043 		for (int groupNdx = 0; groupNdx < numWorkGroups; groupNdx++)
   1044 		{
   1045 			const int	groupOffset		= groupNdx*workGroupSize;
   1046 			const int	groupOutput		= *(const int*)((const deUint8*)resPtr + groupVarInfo.offset + groupNdx*groupVarInfo.arrayStride);
   1047 
   1048 			for (int localNdx = 0; localNdx < workGroupSize; localNdx++)
   1049 			{
   1050 				const int	refValue		= localNdx;
   1051 				const int	outputValue		= *(const int*)((const deUint8*)resPtr + outVarInfo.offset + outVarInfo.arrayStride*(groupOffset+localNdx));
   1052 
   1053 				if (outputValue != refValue)
   1054 				{
   1055 					m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ", invocation " << localNdx
   1056 														   << ": expected " << refValue << ", got " << outputValue
   1057 									   << TestLog::EndMessage;
   1058 					isOk = false;
   1059 					break;
   1060 				}
   1061 			}
   1062 
   1063 			if (groupOutput != workGroupSize)
   1064 			{
   1065 				m_testCtx.getLog() << TestLog::Message << "ERROR: at group " << groupNdx << ": expected" << workGroupSize << ", got " << groupOutput << TestLog::EndMessage;
   1066 				isOk = false;
   1067 				break;
   1068 			}
   1069 		}
   1070 
   1071 		gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
   1072 		GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer()");
   1073 
   1074 		m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
   1075 								isOk ? "Pass"				: "Comparison failed");
   1076 	}
   1077 
   1078 	return STOP;
   1079 }
   1080 
   1081 ShaderAtomicOpTests::ShaderAtomicOpTests (Context& context, const char* name, AtomicOperandType operandType)
   1082 	: TestCaseGroup	(context, name, "Atomic Operation Tests")
   1083 	, m_operandType	(operandType)
   1084 {
   1085 }
   1086 
   1087 ShaderAtomicOpTests::~ShaderAtomicOpTests (void)
   1088 {
   1089 }
   1090 
   1091 template<typename T>
   1092 static tcu::TestCaseGroup* createAtomicOpGroup (Context& context, AtomicOperandType operandType, const char* groupName)
   1093 {
   1094 	tcu::TestCaseGroup *const group = new tcu::TestCaseGroup(context.getTestContext(), groupName, (string("Atomic ") + groupName).c_str());
   1095 	try
   1096 	{
   1097 		for (int precNdx = 0; precNdx < PRECISION_LAST; precNdx++)
   1098 		{
   1099 			for (int typeNdx = 0; typeNdx < 2; typeNdx++)
   1100 			{
   1101 				const Precision		precision		= Precision(precNdx);
   1102 				const DataType		type			= typeNdx > 0 ? TYPE_INT : TYPE_UINT;
   1103 				const string		caseName		= string(getPrecisionName(precision)) + "_" + getDataTypeName(type);
   1104 
   1105 				group->addChild(new T(context, caseName.c_str(), operandType, type, precision));
   1106 			}
   1107 		}
   1108 
   1109 		return group;
   1110 	}
   1111 	catch (...)
   1112 	{
   1113 		delete group;
   1114 		throw;
   1115 	}
   1116 }
   1117 
   1118 void ShaderAtomicOpTests::init (void)
   1119 {
   1120 	addChild(createAtomicOpGroup<ShaderAtomicAddCase>		(m_context, m_operandType, "add"));
   1121 	addChild(createAtomicOpGroup<ShaderAtomicMinCase>		(m_context, m_operandType, "min"));
   1122 	addChild(createAtomicOpGroup<ShaderAtomicMaxCase>		(m_context, m_operandType, "max"));
   1123 	addChild(createAtomicOpGroup<ShaderAtomicAndCase>		(m_context, m_operandType, "and"));
   1124 	addChild(createAtomicOpGroup<ShaderAtomicOrCase>		(m_context, m_operandType, "or"));
   1125 	addChild(createAtomicOpGroup<ShaderAtomicXorCase>		(m_context, m_operandType, "xor"));
   1126 	addChild(createAtomicOpGroup<ShaderAtomicExchangeCase>	(m_context, m_operandType, "exchange"));
   1127 	addChild(createAtomicOpGroup<ShaderAtomicCompSwapCase>	(m_context, m_operandType, "compswap"));
   1128 }
   1129 
   1130 } // Functional
   1131 } // gles31
   1132 } // deqp
   1133