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 Opaque type (sampler, buffer, atomic counter, ...) indexing tests.
22 *
23 * \todo [2014-03-05 pyry] Extend with following:
24 *  + sampler: different filtering modes, multiple sizes, incomplete textures
25 *  + SSBO: write, atomic op, unsized array .length()
26 *//*--------------------------------------------------------------------*/
27
28#include "es31fOpaqueTypeIndexingTests.hpp"
29#include "tcuTexture.hpp"
30#include "tcuTestLog.hpp"
31#include "tcuFormatUtil.hpp"
32#include "tcuVectorUtil.hpp"
33#include "gluShaderUtil.hpp"
34#include "gluShaderProgram.hpp"
35#include "gluObjectWrapper.hpp"
36#include "gluTextureUtil.hpp"
37#include "gluRenderContext.hpp"
38#include "gluProgramInterfaceQuery.hpp"
39#include "gluContextInfo.hpp"
40#include "glsShaderExecUtil.hpp"
41#include "glwFunctions.hpp"
42#include "glwEnums.hpp"
43#include "deUniquePtr.hpp"
44#include "deStringUtil.hpp"
45#include "deRandom.hpp"
46
47#include <sstream>
48
49namespace deqp
50{
51namespace gles31
52{
53namespace Functional
54{
55
56namespace
57{
58
59using namespace gls::ShaderExecUtil;
60using namespace glu;
61using std::string;
62using std::vector;
63using tcu::TextureFormat;
64using tcu::TestLog;
65
66typedef de::UniquePtr<ShaderExecutor> ShaderExecutorPtr;
67
68enum IndexExprType
69{
70	INDEX_EXPR_TYPE_CONST_LITERAL	= 0,
71	INDEX_EXPR_TYPE_CONST_EXPRESSION,
72	INDEX_EXPR_TYPE_UNIFORM,
73	INDEX_EXPR_TYPE_DYNAMIC_UNIFORM,
74
75	INDEX_EXPR_TYPE_LAST
76};
77
78enum TextureType
79{
80	TEXTURE_TYPE_1D = 0,
81	TEXTURE_TYPE_2D,
82	TEXTURE_TYPE_CUBE,
83	TEXTURE_TYPE_2D_ARRAY,
84	TEXTURE_TYPE_3D,
85
86	TEXTURE_TYPE_LAST
87};
88
89static void declareUniformIndexVars (std::ostream& str, const char* varPrefix, int numVars)
90{
91	for (int varNdx = 0; varNdx < numVars; varNdx++)
92		str << "uniform highp int " << varPrefix << varNdx << ";\n";
93}
94
95static void uploadUniformIndices (const glw::Functions& gl, deUint32 program, const char* varPrefix, int numIndices, const int* indices)
96{
97	for (int varNdx = 0; varNdx < numIndices; varNdx++)
98	{
99		const string	varName		= varPrefix + de::toString(varNdx);
100		const int		loc			= gl.getUniformLocation(program, varName.c_str());
101		TCU_CHECK_MSG(loc >= 0, ("No location assigned for uniform '" + varName + "'").c_str());
102
103		gl.uniform1i(loc, indices[varNdx]);
104	}
105}
106
107template<typename T>
108static T maxElement (const std::vector<T>& elements)
109{
110	T maxElem = elements[0];
111
112	for (size_t ndx = 1; ndx < elements.size(); ndx++)
113		maxElem = de::max(maxElem, elements[ndx]);
114
115	return maxElem;
116}
117
118static TextureType getTextureType (glu::DataType samplerType)
119{
120	switch (samplerType)
121	{
122		case glu::TYPE_SAMPLER_1D:
123		case glu::TYPE_INT_SAMPLER_1D:
124		case glu::TYPE_UINT_SAMPLER_1D:
125		case glu::TYPE_SAMPLER_1D_SHADOW:
126			return TEXTURE_TYPE_1D;
127
128		case glu::TYPE_SAMPLER_2D:
129		case glu::TYPE_INT_SAMPLER_2D:
130		case glu::TYPE_UINT_SAMPLER_2D:
131		case glu::TYPE_SAMPLER_2D_SHADOW:
132			return TEXTURE_TYPE_2D;
133
134		case glu::TYPE_SAMPLER_CUBE:
135		case glu::TYPE_INT_SAMPLER_CUBE:
136		case glu::TYPE_UINT_SAMPLER_CUBE:
137		case glu::TYPE_SAMPLER_CUBE_SHADOW:
138			return TEXTURE_TYPE_CUBE;
139
140		case glu::TYPE_SAMPLER_2D_ARRAY:
141		case glu::TYPE_INT_SAMPLER_2D_ARRAY:
142		case glu::TYPE_UINT_SAMPLER_2D_ARRAY:
143		case glu::TYPE_SAMPLER_2D_ARRAY_SHADOW:
144			return TEXTURE_TYPE_2D_ARRAY;
145
146		case glu::TYPE_SAMPLER_3D:
147		case glu::TYPE_INT_SAMPLER_3D:
148		case glu::TYPE_UINT_SAMPLER_3D:
149			return TEXTURE_TYPE_3D;
150
151		default:
152			throw tcu::InternalError("Invalid sampler type");
153	}
154}
155
156static bool isShadowSampler (glu::DataType samplerType)
157{
158	return samplerType == glu::TYPE_SAMPLER_1D_SHADOW		||
159		   samplerType == glu::TYPE_SAMPLER_2D_SHADOW		||
160		   samplerType == glu::TYPE_SAMPLER_2D_ARRAY_SHADOW	||
161		   samplerType == glu::TYPE_SAMPLER_CUBE_SHADOW;
162}
163
164static glu::DataType getSamplerOutputType (glu::DataType samplerType)
165{
166	switch (samplerType)
167	{
168		case glu::TYPE_SAMPLER_1D:
169		case glu::TYPE_SAMPLER_2D:
170		case glu::TYPE_SAMPLER_CUBE:
171		case glu::TYPE_SAMPLER_2D_ARRAY:
172		case glu::TYPE_SAMPLER_3D:
173			return glu::TYPE_FLOAT_VEC4;
174
175		case glu::TYPE_SAMPLER_1D_SHADOW:
176		case glu::TYPE_SAMPLER_2D_SHADOW:
177		case glu::TYPE_SAMPLER_CUBE_SHADOW:
178		case glu::TYPE_SAMPLER_2D_ARRAY_SHADOW:
179			return glu::TYPE_FLOAT;
180
181		case glu::TYPE_INT_SAMPLER_1D:
182		case glu::TYPE_INT_SAMPLER_2D:
183		case glu::TYPE_INT_SAMPLER_CUBE:
184		case glu::TYPE_INT_SAMPLER_2D_ARRAY:
185		case glu::TYPE_INT_SAMPLER_3D:
186			return glu::TYPE_INT_VEC4;
187
188		case glu::TYPE_UINT_SAMPLER_1D:
189		case glu::TYPE_UINT_SAMPLER_2D:
190		case glu::TYPE_UINT_SAMPLER_CUBE:
191		case glu::TYPE_UINT_SAMPLER_2D_ARRAY:
192		case glu::TYPE_UINT_SAMPLER_3D:
193			return glu::TYPE_UINT_VEC4;
194
195		default:
196			throw tcu::InternalError("Invalid sampler type");
197	}
198}
199
200static tcu::TextureFormat getSamplerTextureFormat (glu::DataType samplerType)
201{
202	const glu::DataType		outType			= getSamplerOutputType(samplerType);
203	const glu::DataType		outScalarType	= glu::getDataTypeScalarType(outType);
204
205	switch (outScalarType)
206	{
207		case glu::TYPE_FLOAT:
208			if (isShadowSampler(samplerType))
209				return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT16);
210			else
211				return tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8);
212
213		case glu::TYPE_INT:		return tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SIGNED_INT8);
214		case glu::TYPE_UINT:	return tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT8);
215
216		default:
217			throw tcu::InternalError("Invalid sampler type");
218	}
219}
220
221static glu::DataType getSamplerCoordType (glu::DataType samplerType)
222{
223	const TextureType	texType		= getTextureType(samplerType);
224	int					numCoords	= 0;
225
226	switch (texType)
227	{
228		case TEXTURE_TYPE_1D:		numCoords = 1;	break;
229		case TEXTURE_TYPE_2D:		numCoords = 2;	break;
230		case TEXTURE_TYPE_2D_ARRAY:	numCoords = 3;	break;
231		case TEXTURE_TYPE_CUBE:		numCoords = 3;	break;
232		case TEXTURE_TYPE_3D:		numCoords = 3;	break;
233		default:
234			DE_ASSERT(false);
235	}
236
237	if (isShadowSampler(samplerType))
238		numCoords += 1;
239
240	DE_ASSERT(de::inRange(numCoords, 1, 4));
241
242	return numCoords == 1 ? glu::TYPE_FLOAT : glu::getDataTypeFloatVec(numCoords);
243}
244
245static deUint32 getGLTextureTarget (TextureType texType)
246{
247	switch (texType)
248	{
249		case TEXTURE_TYPE_1D:		return GL_TEXTURE_1D;
250		case TEXTURE_TYPE_2D:		return GL_TEXTURE_2D;
251		case TEXTURE_TYPE_2D_ARRAY:	return GL_TEXTURE_2D_ARRAY;
252		case TEXTURE_TYPE_CUBE:		return GL_TEXTURE_CUBE_MAP;
253		case TEXTURE_TYPE_3D:		return GL_TEXTURE_3D;
254		default:
255			DE_ASSERT(false);
256			return 0;
257	}
258}
259
260static void setupTexture (const glw::Functions&	gl,
261						  deUint32				texture,
262						  glu::DataType			samplerType,
263						  tcu::TextureFormat	texFormat,
264						  const void*			color)
265{
266	const TextureType			texType		= getTextureType(samplerType);
267	const deUint32				texTarget	= getGLTextureTarget(texType);
268	const deUint32				intFormat	= glu::getInternalFormat(texFormat);
269	const glu::TransferFormat	transferFmt	= glu::getTransferFormat(texFormat);
270
271	// \todo [2014-03-04 pyry] Use larger than 1x1 textures?
272
273	gl.bindTexture(texTarget, texture);
274
275	switch (texType)
276	{
277		case TEXTURE_TYPE_1D:
278			gl.texStorage1D(texTarget, 1, intFormat, 1);
279			gl.texSubImage1D(texTarget, 0, 0, 1, transferFmt.format, transferFmt.dataType, color);
280			break;
281
282		case TEXTURE_TYPE_2D:
283			gl.texStorage2D(texTarget, 1, intFormat, 1, 1);
284			gl.texSubImage2D(texTarget, 0, 0, 0, 1, 1, transferFmt.format, transferFmt.dataType, color);
285			break;
286
287		case TEXTURE_TYPE_2D_ARRAY:
288		case TEXTURE_TYPE_3D:
289			gl.texStorage3D(texTarget, 1, intFormat, 1, 1, 1);
290			gl.texSubImage3D(texTarget, 0, 0, 0, 0, 1, 1, 1, transferFmt.format, transferFmt.dataType, color);
291			break;
292
293		case TEXTURE_TYPE_CUBE:
294			gl.texStorage2D(texTarget, 1, intFormat, 1, 1);
295			for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
296				gl.texSubImage2D(glu::getGLCubeFace((tcu::CubeFace)face), 0, 0, 0, 1, 1, transferFmt.format, transferFmt.dataType, color);
297			break;
298
299		default:
300			DE_ASSERT(false);
301	}
302
303	gl.texParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
304	gl.texParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
305
306	if (isShadowSampler(samplerType))
307		gl.texParameteri(texTarget, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
308
309	GLU_EXPECT_NO_ERROR(gl.getError(), "Texture setup failed");
310}
311
312class SamplerIndexingCase : public TestCase
313{
314public:
315							SamplerIndexingCase			(Context& context, const char* name, const char* description, glu::ShaderType shaderType, glu::DataType samplerType, IndexExprType indexExprType);
316							~SamplerIndexingCase		(void);
317
318	void					init						(void);
319	IterateResult			iterate						(void);
320
321private:
322							SamplerIndexingCase			(const SamplerIndexingCase&);
323	SamplerIndexingCase&	operator=					(const SamplerIndexingCase&);
324
325	void					getShaderSpec				(ShaderSpec* spec, int numSamplers, int numLookups, const int* lookupIndices) const;
326
327	const glu::ShaderType	m_shaderType;
328	const glu::DataType		m_samplerType;
329	const IndexExprType		m_indexExprType;
330};
331
332SamplerIndexingCase::SamplerIndexingCase (Context& context, const char* name, const char* description, glu::ShaderType shaderType, glu::DataType samplerType, IndexExprType indexExprType)
333	: TestCase			(context, name, description)
334	, m_shaderType		(shaderType)
335	, m_samplerType		(samplerType)
336	, m_indexExprType	(indexExprType)
337{
338}
339
340SamplerIndexingCase::~SamplerIndexingCase (void)
341{
342}
343
344void SamplerIndexingCase::init (void)
345{
346	const char* extName = "GL_EXT_gpu_shader5";
347
348	if (m_indexExprType != INDEX_EXPR_TYPE_CONST_LITERAL &&
349		!m_context.getContextInfo().isExtensionSupported(extName))
350		throw tcu::NotSupportedError(string(extName) + " extension is required for dynamic indexing of sampler arrays");
351}
352
353void SamplerIndexingCase::getShaderSpec (ShaderSpec* spec, int numSamplers, int numLookups, const int* lookupIndices) const
354{
355	const char*			samplersName	= "sampler";
356	const char*			coordsName		= "coords";
357	const char*			indicesPrefix	= "index";
358	const char*			resultPrefix	= "result";
359	const DataType		coordType		= getSamplerCoordType(m_samplerType);
360	const DataType		outType			= getSamplerOutputType(m_samplerType);
361	std::ostringstream	global, code;
362
363	spec->inputs.push_back(Symbol(coordsName, VarType(coordType, PRECISION_HIGHP)));
364
365	if (m_indexExprType != INDEX_EXPR_TYPE_CONST_LITERAL)
366		global << "#extension GL_EXT_gpu_shader5 : require\n";
367
368	if (m_indexExprType == INDEX_EXPR_TYPE_CONST_EXPRESSION)
369		global << "const highp int indexBase = 1;\n";
370
371	global <<
372		"uniform highp " << getDataTypeName(m_samplerType) << " " << samplersName << "[" << numSamplers << "];\n";
373
374	if (m_indexExprType == INDEX_EXPR_TYPE_DYNAMIC_UNIFORM)
375	{
376		for (int lookupNdx = 0; lookupNdx < numLookups; lookupNdx++)
377		{
378			const string varName = indicesPrefix + de::toString(lookupNdx);
379			spec->inputs.push_back(Symbol(varName, VarType(TYPE_INT, PRECISION_HIGHP)));
380		}
381	}
382	else if (m_indexExprType == INDEX_EXPR_TYPE_UNIFORM)
383		declareUniformIndexVars(global, indicesPrefix, numLookups);
384
385	for (int lookupNdx = 0; lookupNdx < numLookups; lookupNdx++)
386	{
387		const string varName = resultPrefix + de::toString(lookupNdx);
388		spec->outputs.push_back(Symbol(varName, VarType(outType, PRECISION_HIGHP)));
389	}
390
391	for (int lookupNdx = 0; lookupNdx < numLookups; lookupNdx++)
392	{
393		code << resultPrefix << "" << lookupNdx << " = texture(" << samplersName << "[";
394
395		if (m_indexExprType == INDEX_EXPR_TYPE_CONST_LITERAL)
396			code << lookupIndices[lookupNdx];
397		else if (m_indexExprType == INDEX_EXPR_TYPE_CONST_EXPRESSION)
398			code << "indexBase + " << (lookupIndices[lookupNdx]-1);
399		else
400			code << indicesPrefix << lookupNdx;
401
402		code << "], " << coordsName << ");\n";
403	}
404
405	spec->version				= GLSL_VERSION_310_ES;
406	spec->globalDeclarations	= global.str();
407	spec->source				= code.str();
408}
409
410static void fillTextureData (const tcu::PixelBufferAccess& access, de::Random& rnd)
411{
412	DE_ASSERT(access.getHeight() == 1 && access.getDepth() == 1);
413
414	if (access.getFormat().order == TextureFormat::D)
415	{
416		// \note Texture uses odd values, lookup even values to avoid precision issues.
417		const float values[] = { 0.1f, 0.3f, 0.5f, 0.7f, 0.9f };
418
419		for (int ndx = 0; ndx < access.getWidth(); ndx++)
420			access.setPixDepth(rnd.choose<float>(DE_ARRAY_BEGIN(values), DE_ARRAY_END(values)), ndx, 0);
421	}
422	else
423	{
424		TCU_CHECK_INTERNAL(access.getFormat().order == TextureFormat::RGBA && access.getFormat().getPixelSize() == 4);
425
426		for (int ndx = 0; ndx < access.getWidth(); ndx++)
427			*((deUint32*)access.getDataPtr() + ndx) = rnd.getUint32();
428	}
429}
430
431SamplerIndexingCase::IterateResult SamplerIndexingCase::iterate (void)
432{
433	const int						numInvocations		= 64;
434	const int						numSamplers			= 8;
435	const int						numLookups			= 4;
436	const DataType					coordType			= getSamplerCoordType(m_samplerType);
437	const DataType					outputType			= getSamplerOutputType(m_samplerType);
438	const TextureFormat				texFormat			= getSamplerTextureFormat(m_samplerType);
439	const int						outLookupStride		= numInvocations*getDataTypeScalarSize(outputType);
440	vector<int>						lookupIndices		(numLookups);
441	vector<float>					coords;
442	vector<deUint32>				outData;
443	vector<deUint8>					texData				(numSamplers * texFormat.getPixelSize());
444	const tcu::PixelBufferAccess	refTexAccess		(texFormat, numSamplers, 1, 1, &texData[0]);
445	ShaderSpec						shaderSpec;
446	de::Random						rnd					(deInt32Hash(m_samplerType) ^ deInt32Hash(m_shaderType) ^ deInt32Hash(m_indexExprType));
447
448	for (int ndx = 0; ndx < numLookups; ndx++)
449		lookupIndices[ndx] = rnd.getInt(0, numSamplers-1);
450
451	getShaderSpec(&shaderSpec, numSamplers, numLookups, &lookupIndices[0]);
452
453	coords.resize(numInvocations * getDataTypeScalarSize(coordType));
454
455	if (isShadowSampler(m_samplerType))
456	{
457		// Use different comparison value per invocation.
458		// \note Texture uses odd values, comparison even values.
459		const int	numCoordComps	= getDataTypeScalarSize(coordType);
460		const float	cmpValues[]		= { 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f };
461
462		for (int invocationNdx = 0; invocationNdx < numInvocations; invocationNdx++)
463			coords[invocationNdx*numCoordComps + (numCoordComps-1)] = rnd.choose<float>(DE_ARRAY_BEGIN(cmpValues), DE_ARRAY_END(cmpValues));
464	}
465
466	fillTextureData(refTexAccess, rnd);
467
468	outData.resize(numLookups*outLookupStride);
469
470	{
471		const RenderContext&	renderCtx		= m_context.getRenderContext();
472		const glw::Functions&	gl				= renderCtx.getFunctions();
473		ShaderExecutorPtr		executor		(createExecutor(m_context.getRenderContext(), m_shaderType, shaderSpec));
474		TextureVector			textures		(renderCtx, numSamplers);
475		vector<void*>			inputs;
476		vector<void*>			outputs;
477		vector<int>				expandedIndices;
478		const int				maxIndex		= maxElement(lookupIndices);
479
480		m_testCtx.getLog() << *executor;
481
482		if (!executor->isOk())
483			TCU_FAIL("Compile failed");
484
485		executor->useProgram();
486
487		// \todo [2014-03-05 pyry] Do we want to randomize tex unit assignments?
488		for (int samplerNdx = 0; samplerNdx < numSamplers; samplerNdx++)
489		{
490			const string	samplerName	= string("sampler[") + de::toString(samplerNdx) + "]";
491			const int		samplerLoc	= gl.getUniformLocation(executor->getProgram(), samplerName.c_str());
492
493			if (samplerNdx > maxIndex && samplerLoc < 0)
494				continue; // Unused uniform eliminated by compiler
495
496			TCU_CHECK_MSG(samplerLoc >= 0, (string("No location for uniform '") + samplerName + "' found").c_str());
497
498			gl.activeTexture(GL_TEXTURE0 + samplerNdx);
499			setupTexture(gl, textures[samplerNdx], m_samplerType, texFormat, &texData[samplerNdx*texFormat.getPixelSize()]);
500
501			gl.uniform1i(samplerLoc, samplerNdx);
502		}
503
504		inputs.push_back(&coords[0]);
505
506		if (m_indexExprType == INDEX_EXPR_TYPE_DYNAMIC_UNIFORM)
507		{
508			expandedIndices.resize(numInvocations * lookupIndices.size());
509			for (int lookupNdx = 0; lookupNdx < numLookups; lookupNdx++)
510			{
511				for (int invNdx = 0; invNdx < numInvocations; invNdx++)
512					expandedIndices[lookupNdx*numInvocations + invNdx] = lookupIndices[lookupNdx];
513			}
514
515			for (int lookupNdx = 0; lookupNdx < numLookups; lookupNdx++)
516				inputs.push_back(&expandedIndices[lookupNdx*numInvocations]);
517		}
518		else if (m_indexExprType == INDEX_EXPR_TYPE_UNIFORM)
519			uploadUniformIndices(gl, executor->getProgram(), "index", numLookups, &lookupIndices[0]);
520
521		for (int lookupNdx = 0; lookupNdx < numLookups; lookupNdx++)
522			outputs.push_back(&outData[outLookupStride*lookupNdx]);
523
524		GLU_EXPECT_NO_ERROR(gl.getError(), "Setup failed");
525
526		executor->execute(numInvocations, &inputs[0], &outputs[0]);
527	}
528
529	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
530
531	if (isShadowSampler(m_samplerType))
532	{
533		const tcu::Sampler	refSampler		(tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE,
534											 tcu::Sampler::NEAREST, tcu::Sampler::NEAREST, 0.0f, false /* non-normalized */,
535											 tcu::Sampler::COMPAREMODE_LESS);
536		const int			numCoordComps	= getDataTypeScalarSize(coordType);
537
538		TCU_CHECK_INTERNAL(getDataTypeScalarSize(outputType) == 1);
539
540		// Each invocation may have different results.
541		for (int invocationNdx = 0; invocationNdx < numInvocations; invocationNdx++)
542		{
543			const float	coord	= coords[invocationNdx*numCoordComps + (numCoordComps-1)];
544
545			for (int lookupNdx = 0; lookupNdx < numLookups; lookupNdx++)
546			{
547				const int		texNdx		= lookupIndices[lookupNdx];
548				const float		result		= *((const float*)(const deUint8*)&outData[lookupNdx*outLookupStride + invocationNdx]);
549				const float		reference	= refTexAccess.sample2DCompare(refSampler, tcu::Sampler::NEAREST, coord, (float)texNdx, 0.0f, tcu::IVec3(0));
550
551				if (de::abs(result-reference) > 0.005f)
552				{
553					m_testCtx.getLog() << TestLog::Message << "ERROR: at invocation " << invocationNdx << ", lookup " << lookupNdx << ": expected "
554														   << reference << ", got " << result
555									   << TestLog::EndMessage;
556
557					if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
558						m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid lookup result");
559				}
560			}
561		}
562	}
563	else
564	{
565		TCU_CHECK_INTERNAL(getDataTypeScalarSize(outputType) == 4);
566
567		// Validate results from first invocation
568		for (int lookupNdx = 0; lookupNdx < numLookups; lookupNdx++)
569		{
570			const int		texNdx	= lookupIndices[lookupNdx];
571			const deUint8*	resPtr	= (const deUint8*)&outData[lookupNdx*outLookupStride];
572			bool			isOk;
573
574			if (outputType == TYPE_FLOAT_VEC4)
575			{
576				const float			threshold		= 1.0f / 256.0f;
577				const tcu::Vec4		reference		= refTexAccess.getPixel(texNdx, 0);
578				const float*		floatPtr		= (const float*)resPtr;
579				const tcu::Vec4		result			(floatPtr[0], floatPtr[1], floatPtr[2], floatPtr[3]);
580
581				isOk = boolAll(lessThanEqual(abs(reference-result), tcu::Vec4(threshold)));
582
583				if (!isOk)
584				{
585					m_testCtx.getLog() << TestLog::Message << "ERROR: at lookup " << lookupNdx << ": expected "
586														   << reference << ", got " << result
587									   << TestLog::EndMessage;
588				}
589			}
590			else
591			{
592				const tcu::UVec4	reference		= refTexAccess.getPixelUint(texNdx, 0);
593				const deUint32*		uintPtr			= (const deUint32*)resPtr;
594				const tcu::UVec4	result			(uintPtr[0], uintPtr[1], uintPtr[2], uintPtr[3]);
595
596				isOk = boolAll(equal(reference, result));
597
598				if (!isOk)
599				{
600					m_testCtx.getLog() << TestLog::Message << "ERROR: at lookup " << lookupNdx << ": expected "
601														   << reference << ", got " << result
602									   << TestLog::EndMessage;
603				}
604			}
605
606			if (!isOk && m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
607				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid lookup result");
608		}
609
610		// Check results of other invocations against first one
611		for (int invocationNdx = 1; invocationNdx < numInvocations; invocationNdx++)
612		{
613			for (int lookupNdx = 0; lookupNdx < numLookups; lookupNdx++)
614			{
615				const deUint32*		refPtr		= &outData[lookupNdx*outLookupStride];
616				const deUint32*		resPtr		= refPtr + invocationNdx*4;
617				bool				isOk		= true;
618
619				for (int ndx = 0; ndx < 4; ndx++)
620					isOk = isOk && (refPtr[ndx] == resPtr[ndx]);
621
622				if (!isOk)
623				{
624					m_testCtx.getLog() << TestLog::Message << "ERROR: invocation " << invocationNdx << " result "
625														   << tcu::formatArray(tcu::Format::HexIterator<deUint32>(resPtr), tcu::Format::HexIterator<deUint32>(resPtr+4))
626														   << " for lookup " << lookupNdx << " doesn't match result from first invocation "
627														   << tcu::formatArray(tcu::Format::HexIterator<deUint32>(refPtr), tcu::Format::HexIterator<deUint32>(refPtr+4))
628									   << TestLog::EndMessage;
629
630					if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
631						m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Inconsistent lookup results");
632				}
633			}
634		}
635	}
636
637	return STOP;
638}
639
640class BlockArrayIndexingCase : public TestCase
641{
642public:
643	enum BlockType
644	{
645		BLOCKTYPE_UNIFORM = 0,
646		BLOCKTYPE_BUFFER,
647
648		BLOCKTYPE_LAST
649	};
650								BlockArrayIndexingCase		(Context& context, const char* name, const char* description, BlockType blockType, IndexExprType indexExprType, ShaderType shaderType);
651								~BlockArrayIndexingCase		(void);
652
653	void						init						(void);
654	IterateResult				iterate						(void);
655
656private:
657								BlockArrayIndexingCase		(const BlockArrayIndexingCase&);
658	BlockArrayIndexingCase&		operator=					(const BlockArrayIndexingCase&);
659
660	void						getShaderSpec				(ShaderSpec* spec, int numInstances, int numReads, const int* readIndices) const;
661
662	const BlockType				m_blockType;
663	const IndexExprType			m_indexExprType;
664	const ShaderType			m_shaderType;
665
666	const int					m_numInstances;
667};
668
669BlockArrayIndexingCase::BlockArrayIndexingCase (Context& context, const char* name, const char* description, BlockType blockType, IndexExprType indexExprType, ShaderType shaderType)
670	: TestCase			(context, name, description)
671	, m_blockType		(blockType)
672	, m_indexExprType	(indexExprType)
673	, m_shaderType		(shaderType)
674	, m_numInstances	(4)
675{
676}
677
678BlockArrayIndexingCase::~BlockArrayIndexingCase (void)
679{
680}
681
682void BlockArrayIndexingCase::init (void)
683{
684	const char* extName = "GL_EXT_gpu_shader5";
685
686	if (m_indexExprType != INDEX_EXPR_TYPE_CONST_LITERAL &&
687		!m_context.getContextInfo().isExtensionSupported(extName))
688		throw tcu::NotSupportedError(string(extName) + " extension is required for dynamic indexing of interface blocks");
689
690	if (m_blockType == BLOCKTYPE_BUFFER)
691	{
692		const deUint32 limitPnames[] =
693		{
694			GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS,
695			GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS,
696			GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS,
697			GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS,
698			GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS,
699			GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS
700		};
701
702		const glw::Functions&	gl			= m_context.getRenderContext().getFunctions();
703		int						maxBlocks	= 0;
704
705		gl.getIntegerv(limitPnames[m_shaderType], &maxBlocks);
706		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv()");
707
708		if (maxBlocks < m_numInstances)
709			throw tcu::NotSupportedError("Not enough shader storage blocks supported for shader type");
710	}
711}
712
713void BlockArrayIndexingCase::getShaderSpec (ShaderSpec* spec, int numInstances, int numReads, const int* readIndices) const
714{
715	const int			binding			= 2;
716	const char*			blockName		= "Block";
717	const char*			instanceName	= "block";
718	const char*			indicesPrefix	= "index";
719	const char*			resultPrefix	= "result";
720	const char*			interfaceName	= m_blockType == BLOCKTYPE_UNIFORM ? "uniform" : "buffer";
721	const char*			layout			= m_blockType == BLOCKTYPE_UNIFORM ? "std140" : "std430";
722	std::ostringstream	global, code;
723
724	if (m_indexExprType != INDEX_EXPR_TYPE_CONST_LITERAL)
725		global << "#extension GL_EXT_gpu_shader5 : require\n";
726
727	if (m_indexExprType == INDEX_EXPR_TYPE_CONST_EXPRESSION)
728		global << "const highp int indexBase = 1;\n";
729
730	global <<
731		"layout(" << layout << ", binding = " << binding << ") " << interfaceName << " " << blockName << "\n"
732		"{\n"
733		"	uint value;\n"
734		"} " << instanceName << "[" << numInstances << "];\n";
735
736	if (m_indexExprType == INDEX_EXPR_TYPE_DYNAMIC_UNIFORM)
737	{
738		for (int readNdx = 0; readNdx < numReads; readNdx++)
739		{
740			const string varName = indicesPrefix + de::toString(readNdx);
741			spec->inputs.push_back(Symbol(varName, VarType(TYPE_INT, PRECISION_HIGHP)));
742		}
743	}
744	else if (m_indexExprType == INDEX_EXPR_TYPE_UNIFORM)
745		declareUniformIndexVars(global, indicesPrefix, numReads);
746
747	for (int readNdx = 0; readNdx < numReads; readNdx++)
748	{
749		const string varName = resultPrefix + de::toString(readNdx);
750		spec->outputs.push_back(Symbol(varName, VarType(TYPE_UINT, PRECISION_HIGHP)));
751	}
752
753	for (int readNdx = 0; readNdx < numReads; readNdx++)
754	{
755		code << resultPrefix << readNdx << " = " << instanceName << "[";
756
757		if (m_indexExprType == INDEX_EXPR_TYPE_CONST_LITERAL)
758			code << readIndices[readNdx];
759		else if (m_indexExprType == INDEX_EXPR_TYPE_CONST_EXPRESSION)
760			code << "indexBase + " << (readIndices[readNdx]-1);
761		else
762			code << indicesPrefix << readNdx;
763
764		code << "].value;\n";
765	}
766
767	spec->version				= GLSL_VERSION_310_ES;
768	spec->globalDeclarations	= global.str();
769	spec->source				= code.str();
770}
771
772BlockArrayIndexingCase::IterateResult BlockArrayIndexingCase::iterate (void)
773{
774	const int			numInvocations		= 32;
775	const int			numInstances		= m_numInstances;
776	const int			numReads			= 4;
777	vector<int>			readIndices			(numReads);
778	vector<deUint32>	inValues			(numInstances);
779	vector<deUint32>	outValues			(numInvocations*numReads);
780	ShaderSpec			shaderSpec;
781	de::Random			rnd					(deInt32Hash(m_shaderType) ^ deInt32Hash(m_blockType) ^ deInt32Hash(m_indexExprType));
782
783	for (int readNdx = 0; readNdx < numReads; readNdx++)
784		readIndices[readNdx] = rnd.getInt(0, numInstances-1);
785
786	for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
787		inValues[instanceNdx] = rnd.getUint32();
788
789	getShaderSpec(&shaderSpec, numInstances, numReads, &readIndices[0]);
790
791	{
792		const RenderContext&	renderCtx		= m_context.getRenderContext();
793		const glw::Functions&	gl				= renderCtx.getFunctions();
794		const int				baseBinding		= 2;
795		const BufferVector		buffers			(renderCtx, numInstances);
796		const deUint32			bufTarget		= m_blockType == BLOCKTYPE_BUFFER ? GL_SHADER_STORAGE_BUFFER : GL_UNIFORM_BUFFER;
797		ShaderExecutorPtr		shaderExecutor	(createExecutor(renderCtx, m_shaderType, shaderSpec));
798		vector<int>				expandedIndices;
799		vector<void*>			inputs;
800		vector<void*>			outputs;
801
802		m_testCtx.getLog() << *shaderExecutor;
803
804		if (!shaderExecutor->isOk())
805			TCU_FAIL("Compile failed");
806
807		shaderExecutor->useProgram();
808
809		for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
810		{
811			gl.bindBuffer(bufTarget, buffers[instanceNdx]);
812			gl.bufferData(bufTarget, (glw::GLsizeiptr)sizeof(deUint32), &inValues[instanceNdx], GL_STATIC_DRAW);
813			gl.bindBufferBase(bufTarget, baseBinding+instanceNdx, buffers[instanceNdx]);
814		}
815
816		if (m_indexExprType == INDEX_EXPR_TYPE_DYNAMIC_UNIFORM)
817		{
818			expandedIndices.resize(numInvocations * readIndices.size());
819
820			for (int readNdx = 0; readNdx < numReads; readNdx++)
821			{
822				int* dst = &expandedIndices[numInvocations*readNdx];
823				std::fill(dst, dst+numInvocations, readIndices[readNdx]);
824			}
825
826			for (int readNdx = 0; readNdx < numReads; readNdx++)
827				inputs.push_back(&expandedIndices[readNdx*numInvocations]);
828		}
829		else if (m_indexExprType == INDEX_EXPR_TYPE_UNIFORM)
830			uploadUniformIndices(gl, shaderExecutor->getProgram(), "index", numReads, &readIndices[0]);
831
832		for (int readNdx = 0; readNdx < numReads; readNdx++)
833			outputs.push_back(&outValues[readNdx*numInvocations]);
834
835		GLU_EXPECT_NO_ERROR(gl.getError(), "Setup failed");
836
837		shaderExecutor->execute(numInvocations, inputs.empty() ? DE_NULL : &inputs[0], &outputs[0]);
838	}
839
840	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
841
842	for (int invocationNdx = 0; invocationNdx < numInvocations; invocationNdx++)
843	{
844		for (int readNdx = 0; readNdx < numReads; readNdx++)
845		{
846			const deUint32	refValue	= inValues[readIndices[readNdx]];
847			const deUint32	resValue	= outValues[readNdx*numInvocations + invocationNdx];
848
849			if (refValue != resValue)
850			{
851				m_testCtx.getLog() << TestLog::Message << "ERROR: at invocation " << invocationNdx
852													   << ", read " << readNdx << ": expected "
853													   << tcu::toHex(refValue) << ", got " << tcu::toHex(resValue)
854								   << TestLog::EndMessage;
855
856				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
857					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid result value");
858			}
859		}
860	}
861
862	return STOP;
863}
864
865class AtomicCounterIndexingCase : public TestCase
866{
867public:
868								AtomicCounterIndexingCase		(Context& context, const char* name, const char* description, IndexExprType indexExprType, ShaderType shaderType);
869								~AtomicCounterIndexingCase		(void);
870
871	void						init							(void);
872	IterateResult				iterate							(void);
873
874private:
875								AtomicCounterIndexingCase		(const AtomicCounterIndexingCase&);
876	AtomicCounterIndexingCase&	operator=						(const AtomicCounterIndexingCase&);
877
878	void						getShaderSpec					(ShaderSpec* spec, int numCounters, int numOps, const int* opIndices) const;
879
880	const IndexExprType			m_indexExprType;
881	const glu::ShaderType		m_shaderType;
882};
883
884AtomicCounterIndexingCase::AtomicCounterIndexingCase (Context& context, const char* name, const char* description, IndexExprType indexExprType, ShaderType shaderType)
885	: TestCase			(context, name, description)
886	, m_indexExprType	(indexExprType)
887	, m_shaderType		(shaderType)
888{
889}
890
891AtomicCounterIndexingCase::~AtomicCounterIndexingCase (void)
892{
893}
894
895void AtomicCounterIndexingCase::init (void)
896{
897	const char* extName = "GL_EXT_gpu_shader5";
898
899	if (m_indexExprType != INDEX_EXPR_TYPE_CONST_LITERAL &&
900		!m_context.getContextInfo().isExtensionSupported(extName))
901		throw tcu::NotSupportedError(string(extName) + " extension is required for dynamic indexing of atomic counters");
902
903	if (m_shaderType == glu::SHADERTYPE_VERTEX || m_shaderType == glu::SHADERTYPE_FRAGMENT)
904	{
905		int numAtomicCounterBuffers = 0;
906		m_context.getRenderContext().getFunctions().getIntegerv(m_shaderType == glu::SHADERTYPE_VERTEX ? GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS
907																									   : GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS,
908																&numAtomicCounterBuffers);
909
910		if (numAtomicCounterBuffers == 0)
911			throw tcu::NotSupportedError(string("Atomic counters not supported in ") + glu::getShaderTypeName(m_shaderType) + " shader");
912	}
913}
914
915void AtomicCounterIndexingCase::getShaderSpec (ShaderSpec* spec, int numCounters, int numOps, const int* opIndices) const
916{
917	const char*			indicesPrefix	= "index";
918	const char*			resultPrefix	= "result";
919	std::ostringstream	global, code;
920
921	if (m_indexExprType != INDEX_EXPR_TYPE_CONST_LITERAL)
922		global << "#extension GL_EXT_gpu_shader5 : require\n";
923
924	if (m_indexExprType == INDEX_EXPR_TYPE_CONST_EXPRESSION)
925		global << "const highp int indexBase = 1;\n";
926
927	global <<
928		"layout(binding = 0) uniform atomic_uint counter[" << numCounters << "];\n";
929
930	if (m_indexExprType == INDEX_EXPR_TYPE_DYNAMIC_UNIFORM)
931	{
932		for (int opNdx = 0; opNdx < numOps; opNdx++)
933		{
934			const string varName = indicesPrefix + de::toString(opNdx);
935			spec->inputs.push_back(Symbol(varName, VarType(TYPE_INT, PRECISION_HIGHP)));
936		}
937	}
938	else if (m_indexExprType == INDEX_EXPR_TYPE_UNIFORM)
939		declareUniformIndexVars(global, indicesPrefix, numOps);
940
941	for (int opNdx = 0; opNdx < numOps; opNdx++)
942	{
943		const string varName = resultPrefix + de::toString(opNdx);
944		spec->outputs.push_back(Symbol(varName, VarType(TYPE_UINT, PRECISION_HIGHP)));
945	}
946
947	for (int opNdx = 0; opNdx < numOps; opNdx++)
948	{
949		code << resultPrefix << opNdx << " = atomicCounterIncrement(counter[";
950
951		if (m_indexExprType == INDEX_EXPR_TYPE_CONST_LITERAL)
952			code << opIndices[opNdx];
953		else if (m_indexExprType == INDEX_EXPR_TYPE_CONST_EXPRESSION)
954			code << "indexBase + " << (opIndices[opNdx]-1);
955		else
956			code << indicesPrefix << opNdx;
957
958		code << "]);\n";
959	}
960
961	spec->version				= GLSL_VERSION_310_ES;
962	spec->globalDeclarations	= global.str();
963	spec->source				= code.str();
964}
965
966AtomicCounterIndexingCase::IterateResult AtomicCounterIndexingCase::iterate (void)
967{
968	const RenderContext&	renderCtx			= m_context.getRenderContext();
969	const glw::Functions&	gl					= renderCtx.getFunctions();
970	const Buffer			counterBuffer		(renderCtx);
971
972	const int				numInvocations		= 32;
973	const int				numCounters			= 4;
974	const int				numOps				= 4;
975	vector<int>				opIndices			(numOps);
976	vector<deUint32>		outValues			(numInvocations*numOps);
977	ShaderSpec				shaderSpec;
978	de::Random				rnd					(deInt32Hash(m_shaderType) ^ deInt32Hash(m_indexExprType));
979
980	for (int opNdx = 0; opNdx < numOps; opNdx++)
981		opIndices[opNdx] = rnd.getInt(0, numOps-1);
982
983	getShaderSpec(&shaderSpec, numCounters, numOps, &opIndices[0]);
984
985	{
986		const BufferVector		buffers			(renderCtx, numCounters);
987		ShaderExecutorPtr		shaderExecutor	(createExecutor(renderCtx, m_shaderType, shaderSpec));
988		vector<int>				expandedIndices;
989		vector<void*>			inputs;
990		vector<void*>			outputs;
991
992		m_testCtx.getLog() << *shaderExecutor;
993
994		if (!shaderExecutor->isOk())
995			TCU_FAIL("Compile failed");
996
997		{
998			const int				bufSize		= getProgramResourceInt(gl, shaderExecutor->getProgram(), GL_ATOMIC_COUNTER_BUFFER, 0, GL_BUFFER_DATA_SIZE);
999			const int				maxNdx		= maxElement(opIndices);
1000			std::vector<deUint8>	emptyData	(numCounters*4, 0);
1001
1002			if (bufSize < (maxNdx+1)*4)
1003				TCU_FAIL((string("GL reported invalid buffer size " + de::toString(bufSize)).c_str()));
1004
1005			gl.bindBuffer(GL_ATOMIC_COUNTER_BUFFER, *counterBuffer);
1006			gl.bufferData(GL_ATOMIC_COUNTER_BUFFER, (glw::GLsizeiptr)emptyData.size(), &emptyData[0], GL_STATIC_DRAW);
1007			gl.bindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, *counterBuffer);
1008			GLU_EXPECT_NO_ERROR(gl.getError(), "Atomic counter buffer initialization failed");
1009		}
1010
1011		shaderExecutor->useProgram();
1012
1013		if (m_indexExprType == INDEX_EXPR_TYPE_DYNAMIC_UNIFORM)
1014		{
1015			expandedIndices.resize(numInvocations * opIndices.size());
1016
1017			for (int opNdx = 0; opNdx < numOps; opNdx++)
1018			{
1019				int* dst = &expandedIndices[numInvocations*opNdx];
1020				std::fill(dst, dst+numInvocations, opIndices[opNdx]);
1021			}
1022
1023			for (int opNdx = 0; opNdx < numOps; opNdx++)
1024				inputs.push_back(&expandedIndices[opNdx*numInvocations]);
1025		}
1026		else if (m_indexExprType == INDEX_EXPR_TYPE_UNIFORM)
1027			uploadUniformIndices(gl, shaderExecutor->getProgram(), "index", numOps, &opIndices[0]);
1028
1029		for (int opNdx = 0; opNdx < numOps; opNdx++)
1030			outputs.push_back(&outValues[opNdx*numInvocations]);
1031
1032		GLU_EXPECT_NO_ERROR(gl.getError(), "Setup failed");
1033
1034		shaderExecutor->execute(numInvocations, inputs.empty() ? DE_NULL : &inputs[0], &outputs[0]);
1035	}
1036
1037	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1038
1039	{
1040		vector<int>				numHits			(numCounters, 0);	// Number of hits per counter.
1041		vector<deUint32>		counterValues	(numCounters);
1042		vector<vector<bool> >	counterMasks	(numCounters);
1043
1044		for (int opNdx = 0; opNdx < numOps; opNdx++)
1045			numHits[opIndices[opNdx]] += 1;
1046
1047		// Read counter values
1048		{
1049			const void* mapPtr = DE_NULL;
1050
1051			try
1052			{
1053				mapPtr = gl.mapBufferRange(GL_ATOMIC_COUNTER_BUFFER, 0, numCounters*4, GL_MAP_READ_BIT);
1054				GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER)");
1055				TCU_CHECK(mapPtr);
1056				std::copy((const deUint32*)mapPtr, (const deUint32*)mapPtr + numCounters, &counterValues[0]);
1057				gl.unmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
1058			}
1059			catch (...)
1060			{
1061				if (mapPtr)
1062					gl.unmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
1063				throw;
1064			}
1065		}
1066
1067		// Verify counter values
1068		for (int counterNdx = 0; counterNdx < numCounters; counterNdx++)
1069		{
1070			const deUint32		refCount	= (deUint32)(numHits[counterNdx]*numInvocations);
1071			const deUint32		resCount	= counterValues[counterNdx];
1072
1073			if (refCount != resCount)
1074			{
1075				m_testCtx.getLog() << TestLog::Message << "ERROR: atomic counter " << counterNdx << " has value " << resCount
1076													   << ", expected " << refCount
1077								   << TestLog::EndMessage;
1078
1079				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
1080					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid atomic counter value");
1081			}
1082		}
1083
1084		// Allocate bitmasks - one bit per each valid result value
1085		for (int counterNdx = 0; counterNdx < numCounters; counterNdx++)
1086		{
1087			const int	counterValue	= numHits[counterNdx]*numInvocations;
1088			counterMasks[counterNdx].resize(counterValue, false);
1089		}
1090
1091		// Verify result values from shaders
1092		for (int invocationNdx = 0; invocationNdx < numInvocations; invocationNdx++)
1093		{
1094			for (int opNdx = 0; opNdx < numOps; opNdx++)
1095			{
1096				const int		counterNdx	= opIndices[opNdx];
1097				const deUint32	resValue	= outValues[opNdx*numInvocations + invocationNdx];
1098				const bool		rangeOk		= de::inBounds(resValue, 0u, (deUint32)counterMasks[counterNdx].size());
1099				const bool		notSeen		= rangeOk && !counterMasks[counterNdx][resValue];
1100				const bool		isOk		= rangeOk && notSeen;
1101
1102				if (!isOk)
1103				{
1104					m_testCtx.getLog() << TestLog::Message << "ERROR: at invocation " << invocationNdx
1105														   << ", op " << opNdx << ": got invalid result value "
1106														   << resValue
1107									   << TestLog::EndMessage;
1108
1109					if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
1110						m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid result value");
1111				}
1112				else
1113				{
1114					// Mark as used - no other invocation should see this value from same counter.
1115					counterMasks[counterNdx][resValue] = true;
1116				}
1117			}
1118		}
1119
1120		if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
1121		{
1122			// Consistency check - all masks should be 1 now
1123			for (int counterNdx = 0; counterNdx < numCounters; counterNdx++)
1124			{
1125				for (vector<bool>::const_iterator i = counterMasks[counterNdx].begin(); i != counterMasks[counterNdx].end(); i++)
1126					TCU_CHECK_INTERNAL(*i);
1127			}
1128		}
1129	}
1130
1131	return STOP;
1132}
1133
1134} // anonymous
1135
1136OpaqueTypeIndexingTests::OpaqueTypeIndexingTests (Context& context)
1137	: TestCaseGroup(context, "opaque_type_indexing", "Opaque Type Indexing Tests")
1138{
1139}
1140
1141OpaqueTypeIndexingTests::~OpaqueTypeIndexingTests (void)
1142{
1143}
1144
1145void OpaqueTypeIndexingTests::init (void)
1146{
1147	static const struct
1148	{
1149		IndexExprType	type;
1150		const char*		name;
1151		const char*		description;
1152	} indexingTypes[] =
1153	{
1154		{ INDEX_EXPR_TYPE_CONST_LITERAL,	"const_literal",		"Indexing by constant literal"					},
1155		{ INDEX_EXPR_TYPE_CONST_EXPRESSION,	"const_expression",		"Indexing by constant expression"				},
1156		{ INDEX_EXPR_TYPE_UNIFORM,			"uniform",				"Indexing by uniform value"						},
1157		{ INDEX_EXPR_TYPE_DYNAMIC_UNIFORM,	"dynamically_uniform",	"Indexing by dynamically uniform expression"	}
1158	};
1159
1160	static const struct
1161	{
1162		ShaderType		type;
1163		const char*		name;
1164	} shaderTypes[] =
1165	{
1166		{ SHADERTYPE_VERTEX,		"vertex"	},
1167		{ SHADERTYPE_FRAGMENT,		"fragment"	},
1168		{ SHADERTYPE_COMPUTE,		"compute"	}
1169	};
1170
1171	// .sampler
1172	{
1173		static const DataType samplerTypes[] =
1174		{
1175			// \note 1D images will be added by a later extension.
1176//			TYPE_SAMPLER_1D,
1177			TYPE_SAMPLER_2D,
1178			TYPE_SAMPLER_CUBE,
1179			TYPE_SAMPLER_2D_ARRAY,
1180			TYPE_SAMPLER_3D,
1181//			TYPE_SAMPLER_1D_SHADOW,
1182			TYPE_SAMPLER_2D_SHADOW,
1183			TYPE_SAMPLER_CUBE_SHADOW,
1184			TYPE_SAMPLER_2D_ARRAY_SHADOW,
1185//			TYPE_INT_SAMPLER_1D,
1186			TYPE_INT_SAMPLER_2D,
1187			TYPE_INT_SAMPLER_CUBE,
1188			TYPE_INT_SAMPLER_2D_ARRAY,
1189			TYPE_INT_SAMPLER_3D,
1190//			TYPE_UINT_SAMPLER_1D,
1191			TYPE_UINT_SAMPLER_2D,
1192			TYPE_UINT_SAMPLER_CUBE,
1193			TYPE_UINT_SAMPLER_2D_ARRAY,
1194			TYPE_UINT_SAMPLER_3D,
1195		};
1196
1197		tcu::TestCaseGroup* const samplerGroup = new tcu::TestCaseGroup(m_testCtx, "sampler", "Sampler Array Indexing Tests");
1198		addChild(samplerGroup);
1199
1200		for (int indexTypeNdx = 0; indexTypeNdx < DE_LENGTH_OF_ARRAY(indexingTypes); indexTypeNdx++)
1201		{
1202			const IndexExprType			indexExprType	= indexingTypes[indexTypeNdx].type;
1203			tcu::TestCaseGroup* const	indexGroup		= new tcu::TestCaseGroup(m_testCtx, indexingTypes[indexTypeNdx].name, indexingTypes[indexTypeNdx].description);
1204			samplerGroup->addChild(indexGroup);
1205
1206			for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(shaderTypes); shaderTypeNdx++)
1207			{
1208				const ShaderType			shaderType		= shaderTypes[shaderTypeNdx].type;
1209				tcu::TestCaseGroup* const	shaderGroup		= new tcu::TestCaseGroup(m_testCtx, shaderTypes[shaderTypeNdx].name, "");
1210				indexGroup->addChild(shaderGroup);
1211
1212				for (int samplerTypeNdx = 0; samplerTypeNdx < DE_LENGTH_OF_ARRAY(samplerTypes); samplerTypeNdx++)
1213				{
1214					const DataType	samplerType	= samplerTypes[samplerTypeNdx];
1215					const char*		samplerName	= getDataTypeName(samplerType);
1216					const string	caseName	= de::toLower(samplerName);
1217
1218					shaderGroup->addChild(new SamplerIndexingCase(m_context, caseName.c_str(), "", shaderType, samplerType, indexExprType));
1219				}
1220			}
1221		}
1222	}
1223
1224	// .ubo / .ssbo / .atomic_counter
1225	{
1226		tcu::TestCaseGroup* const	uboGroup	= new tcu::TestCaseGroup(m_testCtx, "ubo",				"Uniform Block Instance Array Indexing Tests");
1227		tcu::TestCaseGroup* const	ssboGroup	= new tcu::TestCaseGroup(m_testCtx, "ssbo",				"Buffer Block Instance Array Indexing Tests");
1228		tcu::TestCaseGroup* const	acGroup		= new tcu::TestCaseGroup(m_testCtx, "atomic_counter",	"Atomic Counter Array Indexing Tests");
1229		addChild(uboGroup);
1230		addChild(ssboGroup);
1231		addChild(acGroup);
1232
1233		for (int indexTypeNdx = 0; indexTypeNdx < DE_LENGTH_OF_ARRAY(indexingTypes); indexTypeNdx++)
1234		{
1235			const IndexExprType		indexExprType		= indexingTypes[indexTypeNdx].type;
1236			const char*				indexExprName		= indexingTypes[indexTypeNdx].name;
1237			const char*				indexExprDesc		= indexingTypes[indexTypeNdx].description;
1238
1239			for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(shaderTypes); shaderTypeNdx++)
1240			{
1241				const ShaderType		shaderType		= shaderTypes[shaderTypeNdx].type;
1242				const string			name			= string(indexExprName) + "_" + shaderTypes[shaderTypeNdx].name;
1243
1244				uboGroup->addChild	(new BlockArrayIndexingCase		(m_context, name.c_str(), indexExprDesc, BlockArrayIndexingCase::BLOCKTYPE_UNIFORM,	indexExprType, shaderType));
1245				acGroup->addChild	(new AtomicCounterIndexingCase	(m_context, name.c_str(), indexExprDesc, indexExprType, shaderType));
1246
1247				if (indexExprType == INDEX_EXPR_TYPE_CONST_LITERAL || indexExprType == INDEX_EXPR_TYPE_CONST_EXPRESSION)
1248					ssboGroup->addChild	(new BlockArrayIndexingCase		(m_context, name.c_str(), indexExprDesc, BlockArrayIndexingCase::BLOCKTYPE_BUFFER,	indexExprType, shaderType));
1249			}
1250		}
1251	}
1252}
1253
1254} // Functional
1255} // gles31
1256} // deqp
1257