Home | History | Annotate | Download | only in functional
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program OpenGL ES 3.0 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 Fragment shader output tests.
     22  *
     23  * \todo [2012-04-10 pyry] Missing:
     24  *  + non-contiguous attachments in framebuffer
     25  *//*--------------------------------------------------------------------*/
     26 
     27 #include "es3fFragmentOutputTests.hpp"
     28 #include "gluShaderUtil.hpp"
     29 #include "gluShaderProgram.hpp"
     30 #include "gluTextureUtil.hpp"
     31 #include "gluStrUtil.hpp"
     32 #include "tcuTestLog.hpp"
     33 #include "tcuTexture.hpp"
     34 #include "tcuTextureUtil.hpp"
     35 #include "tcuVector.hpp"
     36 #include "tcuVectorUtil.hpp"
     37 #include "tcuImageCompare.hpp"
     38 #include "deRandom.hpp"
     39 #include "deStringUtil.hpp"
     40 #include "deMath.h"
     41 
     42 // For getFormatName() \todo [pyry] Move to glu?
     43 #include "es3fFboTestUtil.hpp"
     44 
     45 #include "glwEnums.hpp"
     46 #include "glwFunctions.hpp"
     47 
     48 namespace deqp
     49 {
     50 namespace gles3
     51 {
     52 namespace Functional
     53 {
     54 
     55 using std::vector;
     56 using std::string;
     57 using tcu::IVec2;
     58 using tcu::IVec4;
     59 using tcu::UVec2;
     60 using tcu::UVec4;
     61 using tcu::Vec2;
     62 using tcu::Vec3;
     63 using tcu::Vec4;
     64 using tcu::BVec4;
     65 using tcu::TestLog;
     66 using FboTestUtil::getFormatName;
     67 using FboTestUtil::getFramebufferReadFormat;
     68 
     69 struct BufferSpec
     70 {
     71 	BufferSpec (void)
     72 		: format	(GL_NONE)
     73 		, width		(0)
     74 		, height	(0)
     75 		, samples	(0)
     76 	{
     77 	}
     78 
     79 	BufferSpec (deUint32 format_, int width_, int height_, int samples_)
     80 		: format	(format_)
     81 		, width		(width_)
     82 		, height	(height_)
     83 		, samples	(samples_)
     84 	{
     85 	}
     86 
     87 	deUint32	format;
     88 	int			width;
     89 	int			height;
     90 	int			samples;
     91 };
     92 
     93 struct FragmentOutput
     94 {
     95 	FragmentOutput (void)
     96 		: type			(glu::TYPE_LAST)
     97 		, precision		(glu::PRECISION_LAST)
     98 		, location		(0)
     99 		, arrayLength	(0)
    100 	{
    101 	}
    102 
    103 	FragmentOutput (glu::DataType type_, glu::Precision precision_, int location_, int arrayLength_ = 0)
    104 		: type			(type_)
    105 		, precision		(precision_)
    106 		, location		(location_)
    107 		, arrayLength	(arrayLength_)
    108 	{
    109 	}
    110 
    111 	glu::DataType	type;
    112 	glu::Precision	precision;
    113 	int				location;
    114 	int				arrayLength;	//!< 0 if not an array.
    115 };
    116 
    117 struct OutputVec
    118 {
    119 	vector<FragmentOutput> outputs;
    120 
    121 	OutputVec& operator<< (const FragmentOutput& output)
    122 	{
    123 		outputs.push_back(output);
    124 		return *this;
    125 	}
    126 
    127 	vector<FragmentOutput> toVec (void) const
    128 	{
    129 		return outputs;
    130 	}
    131 };
    132 
    133 class FragmentOutputCase : public TestCase
    134 {
    135 public:
    136 								FragmentOutputCase			(Context& context, const char* name, const char* desc, const vector<BufferSpec>& fboSpec, const vector<FragmentOutput>& outputs);
    137 								~FragmentOutputCase			(void);
    138 
    139 	void						init						(void);
    140 	void						deinit						(void);
    141 	IterateResult				iterate						(void);
    142 
    143 private:
    144 								FragmentOutputCase			(const FragmentOutputCase& other);
    145 	FragmentOutputCase&			operator=					(const FragmentOutputCase& other);
    146 
    147 	vector<BufferSpec>			m_fboSpec;
    148 	vector<FragmentOutput>		m_outputs;
    149 
    150 	glu::ShaderProgram*			m_program;
    151 	deUint32					m_framebuffer;
    152 	vector<deUint32>			m_renderbuffers;
    153 };
    154 
    155 FragmentOutputCase::FragmentOutputCase (Context& context, const char* name, const char* desc, const vector<BufferSpec>& fboSpec, const vector<FragmentOutput>& outputs)
    156 	: TestCase		(context, name, desc)
    157 	, m_fboSpec		(fboSpec)
    158 	, m_outputs		(outputs)
    159 	, m_program		(DE_NULL)
    160 	, m_framebuffer	(0)
    161 {
    162 }
    163 
    164 FragmentOutputCase::~FragmentOutputCase (void)
    165 {
    166 	deinit();
    167 }
    168 
    169 static glu::ShaderProgram* createProgram (const glu::RenderContext& context, const vector<FragmentOutput>& outputs)
    170 {
    171 	std::ostringstream	vtx;
    172 	std::ostringstream	frag;
    173 
    174 	vtx << "#version 300 es\n"
    175 		<< "in highp vec4 a_position;\n";
    176 	frag << "#version 300 es\n";
    177 
    178 	// Input-output declarations.
    179 	for (int outNdx = 0; outNdx < (int)outputs.size(); outNdx++)
    180 	{
    181 		const FragmentOutput&	output		= outputs[outNdx];
    182 		bool					isArray		= output.arrayLength > 0;
    183 		const char*				typeName	= glu::getDataTypeName(output.type);
    184 		const char*				outputPrec	= glu::getPrecisionName(output.precision);
    185 		bool					isFloat		= glu::isDataTypeFloatOrVec(output.type);
    186 		const char*				interp		= isFloat ? "smooth" : "flat";
    187 		const char*				interpPrec	= isFloat ? "highp" : outputPrec;
    188 
    189 		if (isArray)
    190 		{
    191 			for (int elemNdx = 0; elemNdx < output.arrayLength; elemNdx++)
    192 			{
    193 				vtx << "in " << interpPrec << " " << typeName << " in" << outNdx << "_" << elemNdx << ";\n"
    194 					<< interp << " out " << interpPrec << " " << typeName << " var" << outNdx << "_" << elemNdx << ";\n";
    195 				frag << interp << " in " << interpPrec << " " << typeName << " var" << outNdx << "_" << elemNdx << ";\n";
    196 			}
    197 			frag << "layout(location = " << output.location << ") out " << outputPrec << " " << typeName << " out" << outNdx << "[" << output.arrayLength << "];\n";
    198 		}
    199 		else
    200 		{
    201 			vtx << "in " << interpPrec << " " << typeName << " in" << outNdx << ";\n"
    202 				<< interp << " out " << interpPrec << " " << typeName << " var" << outNdx << ";\n";
    203 			frag << interp << " in " << interpPrec << " " << typeName << " var" << outNdx << ";\n"
    204 				 << "layout(location = " << output.location << ") out " << outputPrec << " " << typeName << " out" << outNdx << ";\n";
    205 		}
    206 	}
    207 
    208 	vtx << "\nvoid main()\n{\n";
    209 	frag << "\nvoid main()\n{\n";
    210 
    211 	vtx << "	gl_Position = a_position;\n";
    212 
    213 	// Copy body
    214 	for (int outNdx = 0; outNdx < (int)outputs.size(); outNdx++)
    215 	{
    216 		const FragmentOutput&	output		= outputs[outNdx];
    217 		bool					isArray		= output.arrayLength > 0;
    218 
    219 		if (isArray)
    220 		{
    221 			for (int elemNdx = 0; elemNdx < output.arrayLength; elemNdx++)
    222 			{
    223 				vtx << "\tvar" << outNdx << "_" << elemNdx << " = in" << outNdx << "_" << elemNdx << ";\n";
    224 				frag << "\tout" << outNdx << "[" << elemNdx << "] = var" << outNdx << "_" << elemNdx << ";\n";
    225 			}
    226 		}
    227 		else
    228 		{
    229 			vtx << "\tvar" << outNdx << " = in" << outNdx << ";\n";
    230 			frag << "\tout" << outNdx << " = var" << outNdx << ";\n";
    231 		}
    232 	}
    233 
    234 	vtx << "}\n";
    235 	frag << "}\n";
    236 
    237 	return new glu::ShaderProgram(context, glu::makeVtxFragSources(vtx.str(), frag.str()));
    238 }
    239 
    240 void FragmentOutputCase::init (void)
    241 {
    242 	const glw::Functions&	gl		= m_context.getRenderContext().getFunctions();
    243 	TestLog&				log		= m_testCtx.getLog();
    244 
    245 	// Check that all attachments are supported
    246 	for (std::vector<BufferSpec>::const_iterator bufIter = m_fboSpec.begin(); bufIter != m_fboSpec.end(); ++bufIter)
    247 	{
    248 		if (!glu::isSizedFormatColorRenderable(m_context.getRenderContext(), m_context.getContextInfo(), bufIter->format))
    249 			throw tcu::NotSupportedError("Unsupported attachment format");
    250 	}
    251 
    252 	DE_ASSERT(!m_program);
    253 	m_program = createProgram(m_context.getRenderContext(), m_outputs);
    254 
    255 	log << *m_program;
    256 	if (!m_program->isOk())
    257 		TCU_FAIL("Compile failed");
    258 
    259 	// Print render target info to log.
    260 	log << TestLog::Section("Framebuffer", "Framebuffer configuration");
    261 
    262 	for (int ndx = 0; ndx < (int)m_fboSpec.size(); ndx++)
    263 		log << TestLog::Message << "COLOR_ATTACHMENT" << ndx << ": "
    264 								<< glu::getTextureFormatStr(m_fboSpec[ndx].format) << ", "
    265 								<< m_fboSpec[ndx].width << "x" << m_fboSpec[ndx].height << ", "
    266 								<< m_fboSpec[ndx].samples << " samples"
    267 			<< TestLog::EndMessage;
    268 
    269 	log << TestLog::EndSection;
    270 
    271 	// Create framebuffer.
    272 	m_renderbuffers.resize(m_fboSpec.size(), 0);
    273 	gl.genFramebuffers(1, &m_framebuffer);
    274 	gl.genRenderbuffers((int)m_renderbuffers.size(), &m_renderbuffers[0]);
    275 
    276 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
    277 
    278 	for (int bufNdx = 0; bufNdx < (int)m_renderbuffers.size(); bufNdx++)
    279 	{
    280 		deUint32			rbo			= m_renderbuffers[bufNdx];
    281 		const BufferSpec&	bufSpec		= m_fboSpec[bufNdx];
    282 		deUint32			attachment	= GL_COLOR_ATTACHMENT0+bufNdx;
    283 
    284 		gl.bindRenderbuffer(GL_RENDERBUFFER, rbo);
    285 		gl.renderbufferStorageMultisample(GL_RENDERBUFFER, bufSpec.samples, bufSpec.format, bufSpec.width, bufSpec.height);
    286 		gl.framebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, rbo);
    287 	}
    288 	GLU_EXPECT_NO_ERROR(gl.getError(), "After framebuffer setup");
    289 
    290 	deUint32 fboStatus = gl.checkFramebufferStatus(GL_FRAMEBUFFER);
    291 	if (fboStatus == GL_FRAMEBUFFER_UNSUPPORTED)
    292 		throw tcu::NotSupportedError("Framebuffer not supported", "", __FILE__, __LINE__);
    293 	else if (fboStatus != GL_FRAMEBUFFER_COMPLETE)
    294 		throw tcu::TestError((string("Incomplete framebuffer: ") + glu::getFramebufferStatusStr(fboStatus).toString()).c_str(), "", __FILE__, __LINE__);
    295 
    296 	gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
    297 	GLU_EXPECT_NO_ERROR(gl.getError(), "After init");
    298 }
    299 
    300 void FragmentOutputCase::deinit (void)
    301 {
    302 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
    303 
    304 	if (m_framebuffer)
    305 	{
    306 		gl.deleteFramebuffers(1, &m_framebuffer);
    307 		m_framebuffer = 0;
    308 	}
    309 
    310 	if (!m_renderbuffers.empty())
    311 	{
    312 		gl.deleteRenderbuffers((int)m_renderbuffers.size(), &m_renderbuffers[0]);
    313 		m_renderbuffers.clear();
    314 	}
    315 
    316 	delete m_program;
    317 	m_program = DE_NULL;
    318 }
    319 
    320 static IVec2 getMinSize (const vector<BufferSpec>& fboSpec)
    321 {
    322 	IVec2 minSize(0x7fffffff, 0x7fffffff);
    323 	for (vector<BufferSpec>::const_iterator i = fboSpec.begin(); i != fboSpec.end(); i++)
    324 	{
    325 		minSize.x() = de::min(minSize.x(), i->width);
    326 		minSize.y() = de::min(minSize.y(), i->height);
    327 	}
    328 	return minSize;
    329 }
    330 
    331 static int getNumInputVectors (const vector<FragmentOutput>& outputs)
    332 {
    333 	int numVecs = 0;
    334 	for (vector<FragmentOutput>::const_iterator i = outputs.begin(); i != outputs.end(); i++)
    335 		numVecs += (i->arrayLength > 0 ? i->arrayLength : 1);
    336 	return numVecs;
    337 }
    338 
    339 static Vec2 getFloatRange (glu::Precision precision)
    340 {
    341 	// \todo [2012-04-09 pyry] Not quite the full ranges.
    342 	static const Vec2 ranges[] =
    343 	{
    344 		Vec2(-2.0f, 2.0f),
    345 		Vec2(-16000.0f, 16000.0f),
    346 		Vec2(-1e35f, 1e35f)
    347 	};
    348 	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(ranges) == glu::PRECISION_LAST);
    349 	DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(ranges)));
    350 	return ranges[precision];
    351 }
    352 
    353 static IVec2 getIntRange (glu::Precision precision)
    354 {
    355 	static const IVec2 ranges[] =
    356 	{
    357 		IVec2(-(1<< 7), (1<< 7)-1),
    358 		IVec2(-(1<<15), (1<<15)-1),
    359 		IVec2(0x80000000, 0x7fffffff)
    360 	};
    361 	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(ranges) == glu::PRECISION_LAST);
    362 	DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(ranges)));
    363 	return ranges[precision];
    364 }
    365 
    366 static UVec2 getUintRange (glu::Precision precision)
    367 {
    368 	static const UVec2 ranges[] =
    369 	{
    370 		UVec2(0, (1<< 8)-1),
    371 		UVec2(0, (1<<16)-1),
    372 		UVec2(0, 0xffffffffu)
    373 	};
    374 	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(ranges) == glu::PRECISION_LAST);
    375 	DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(ranges)));
    376 	return ranges[precision];
    377 }
    378 
    379 static inline Vec4 readVec4 (const float* ptr, int numComponents)
    380 {
    381 	DE_ASSERT(numComponents >= 1);
    382 	return Vec4(ptr[0],
    383 				numComponents >= 2 ? ptr[1] : 0.0f,
    384 				numComponents >= 3 ? ptr[2] : 0.0f,
    385 				numComponents >= 4 ? ptr[3] : 0.0f);
    386 }
    387 
    388 static inline IVec4 readIVec4 (const int* ptr, int numComponents)
    389 {
    390 	DE_ASSERT(numComponents >= 1);
    391 	return IVec4(ptr[0],
    392 				 numComponents >= 2 ? ptr[1] : 0,
    393 				 numComponents >= 3 ? ptr[2] : 0,
    394 				 numComponents >= 4 ? ptr[3] : 0);
    395 }
    396 
    397 static void renderFloatReference (const tcu::PixelBufferAccess& dst, int gridWidth, int gridHeight, int numComponents, const float* vertices)
    398 {
    399 	const bool	isSRGB		= tcu::isSRGB(dst.getFormat());
    400 	const float	cellW		= (float)dst.getWidth() / (float)(gridWidth-1);
    401 	const float	cellH		= (float)dst.getHeight() / (float)(gridHeight-1);
    402 
    403 	for (int y = 0; y < dst.getHeight(); y++)
    404 	{
    405 		for (int x = 0; x < dst.getWidth(); x++)
    406 		{
    407 			const int		cellX	= de::clamp(deFloorFloatToInt32((float)x / cellW), 0, gridWidth-2);
    408 			const int		cellY	= de::clamp(deFloorFloatToInt32((float)y / cellH), 0, gridHeight-2);
    409 			const float		xf		= ((float)x - (float)cellX*cellW + 0.5f) / cellW;
    410 			const float		yf		= ((float)y - (float)cellY*cellH + 0.5f) / cellH;
    411 			const Vec4		v00		= readVec4(vertices + ((cellY+0)*gridWidth + cellX+0)*numComponents, numComponents);
    412 			const Vec4		v01		= readVec4(vertices + ((cellY+1)*gridWidth + cellX+0)*numComponents, numComponents);
    413 			const Vec4		v10		= readVec4(vertices + ((cellY+0)*gridWidth + cellX+1)*numComponents, numComponents);
    414 			const Vec4		v11		= readVec4(vertices + ((cellY+1)*gridWidth + cellX+1)*numComponents, numComponents);
    415 			const bool		tri		= xf + yf >= 1.0f;
    416 			const Vec4&		v0		= tri ? v11 : v00;
    417 			const Vec4&		v1		= tri ? v01 : v10;
    418 			const Vec4&		v2		= tri ? v10 : v01;
    419 			const float		s		= tri ? 1.0f-xf : xf;
    420 			const float		t		= tri ? 1.0f-yf : yf;
    421 			const Vec4		color	= v0 + (v1-v0)*s + (v2-v0)*t;
    422 
    423 			dst.setPixel(isSRGB ? tcu::linearToSRGB(color) : color, x, y);
    424 		}
    425 	}
    426 }
    427 
    428 static void renderIntReference (const tcu::PixelBufferAccess& dst, int gridWidth, int gridHeight, int numComponents, const int* vertices)
    429 {
    430 	float	cellW		= (float)dst.getWidth() / (float)(gridWidth-1);
    431 	float	cellH		= (float)dst.getHeight() / (float)(gridHeight-1);
    432 
    433 	for (int y = 0; y < dst.getHeight(); y++)
    434 	{
    435 		for (int x = 0; x < dst.getWidth(); x++)
    436 		{
    437 			int			cellX	= de::clamp(deFloorFloatToInt32((float)x / cellW), 0, gridWidth-2);
    438 			int			cellY	= de::clamp(deFloorFloatToInt32((float)y / cellH), 0, gridHeight-2);
    439 			IVec4		c		= readIVec4(vertices + (cellY*gridWidth + cellX+1)*numComponents, numComponents);
    440 
    441 			dst.setPixel(c, x, y);
    442 		}
    443 	}
    444 }
    445 
    446 static const IVec4 s_swizzles[] =
    447 {
    448 	IVec4(0,1,2,3),
    449 	IVec4(1,2,3,0),
    450 	IVec4(2,3,0,1),
    451 	IVec4(3,0,1,2),
    452 	IVec4(3,2,1,0),
    453 	IVec4(2,1,0,3),
    454 	IVec4(1,0,3,2),
    455 	IVec4(0,3,2,1)
    456 };
    457 
    458 template <typename T>
    459 inline tcu::Vector<T, 4> swizzleVec (const tcu::Vector<T, 4>& vec, int swzNdx)
    460 {
    461 	const IVec4& swz = s_swizzles[swzNdx % DE_LENGTH_OF_ARRAY(s_swizzles)];
    462 	return vec.swizzle(swz[0], swz[1], swz[2], swz[3]);
    463 }
    464 
    465 namespace
    466 {
    467 
    468 struct AttachmentData
    469 {
    470 	tcu::TextureFormat		format;					//!< Actual format of attachment.
    471 	tcu::TextureFormat		referenceFormat;		//!< Used for reference rendering.
    472 	tcu::TextureFormat		readFormat;
    473 	int						numWrittenChannels;
    474 	glu::Precision			outPrecision;
    475 	vector<deUint8>			renderedData;
    476 	vector<deUint8>			referenceData;
    477 };
    478 
    479 template<typename Type>
    480 string valueRangeToString (int numValidChannels, const tcu::Vector<Type, 4>& minValue, const tcu::Vector<Type, 4>& maxValue)
    481 {
    482 	std::ostringstream stream;
    483 
    484 	stream << "(";
    485 
    486 	for (int i = 0; i < 4; i++)
    487 	{
    488 		if (i != 0)
    489 			stream << ", ";
    490 
    491 		if (i < numValidChannels)
    492 			stream << minValue[i] << " -> " << maxValue[i];
    493 		else
    494 			stream << "Undef";
    495 	}
    496 
    497 	stream << ")";
    498 
    499 	return stream.str();
    500 }
    501 
    502 void clearUndefined (const tcu::PixelBufferAccess& access, int numValidChannels)
    503 {
    504 	for (int y = 0; y < access.getHeight(); y++)
    505 	for (int x = 0; x < access.getWidth(); x++)
    506 	{
    507 		switch (tcu::getTextureChannelClass(access.getFormat().type))
    508 		{
    509 			case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
    510 			{
    511 				const Vec4	srcPixel	= access.getPixel(x, y);
    512 				Vec4		dstPixel	(0.0f, 0.0f, 0.0f, 1.0f);
    513 
    514 				for (int channelNdx = 0; channelNdx < numValidChannels; channelNdx++)
    515 					dstPixel[channelNdx] = srcPixel[channelNdx];
    516 
    517 				access.setPixel(dstPixel, x, y);
    518 				break;
    519 			}
    520 
    521 			case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
    522 			case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
    523 			case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
    524 			case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
    525 			{
    526 				const IVec4	bitDepth	= tcu::getTextureFormatBitDepth(access.getFormat());
    527 				const IVec4	srcPixel	= access.getPixelInt(x, y);
    528 				IVec4		dstPixel	(0, 0, 0, (0x1u << (deUint64)bitDepth.w()) - 1);
    529 
    530 				for (int channelNdx = 0; channelNdx < numValidChannels; channelNdx++)
    531 					dstPixel[channelNdx] = srcPixel[channelNdx];
    532 
    533 				access.setPixel(dstPixel, x, y);
    534 				break;
    535 			}
    536 
    537 			default:
    538 				DE_ASSERT(false);
    539 		}
    540 	}
    541 }
    542 
    543 } // anonymous
    544 
    545 FragmentOutputCase::IterateResult FragmentOutputCase::iterate (void)
    546 {
    547 	TestLog&					log					= m_testCtx.getLog();
    548 	const glw::Functions&		gl					= m_context.getRenderContext().getFunctions();
    549 
    550 	// Compute grid size & index list.
    551 	const int					minCellSize			= 8;
    552 	const IVec2					minBufSize			= getMinSize(m_fboSpec);
    553 	const int					gridWidth			= de::clamp(minBufSize.x()/minCellSize, 1, 255)+1;
    554 	const int					gridHeight			= de::clamp(minBufSize.y()/minCellSize, 1, 255)+1;
    555 	const int					numVertices			= gridWidth*gridHeight;
    556 	const int					numQuads			= (gridWidth-1)*(gridHeight-1);
    557 	const int					numIndices			= numQuads*6;
    558 
    559 	const int					numInputVecs		= getNumInputVectors(m_outputs);
    560 	vector<vector<deUint32> >	inputs				(numInputVecs);
    561 	vector<float>				positions			(numVertices*4);
    562 	vector<deUint16>			indices				(numIndices);
    563 
    564 	const int					readAlignment		= 4;
    565 	const int					viewportW			= minBufSize.x();
    566 	const int					viewportH			= minBufSize.y();
    567 	const int					numAttachments		= (int)m_fboSpec.size();
    568 
    569 	vector<deUint32>			drawBuffers			(numAttachments);
    570 	vector<AttachmentData>		attachments			(numAttachments);
    571 
    572 	// Initialize attachment data.
    573 	for (int ndx = 0; ndx < numAttachments; ndx++)
    574 	{
    575 		const tcu::TextureFormat		texFmt			= glu::mapGLInternalFormat(m_fboSpec[ndx].format);
    576 		const tcu::TextureChannelClass	chnClass		= tcu::getTextureChannelClass(texFmt.type);
    577 		const bool						isFixedPoint	= chnClass == tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT ||
    578 														  chnClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
    579 
    580 		// \note Fixed-point formats use float reference to enable more accurate result verification.
    581 		const tcu::TextureFormat		refFmt			= isFixedPoint ? tcu::TextureFormat(texFmt.order, tcu::TextureFormat::FLOAT) : texFmt;
    582 		const tcu::TextureFormat		readFmt			= getFramebufferReadFormat(texFmt);
    583 		const int						attachmentW		= m_fboSpec[ndx].width;
    584 		const int						attachmentH		= m_fboSpec[ndx].height;
    585 
    586 		drawBuffers[ndx]					= GL_COLOR_ATTACHMENT0+ndx;
    587 		attachments[ndx].format				= texFmt;
    588 		attachments[ndx].readFormat			= readFmt;
    589 		attachments[ndx].referenceFormat	= refFmt;
    590 		attachments[ndx].renderedData.resize(readFmt.getPixelSize()*attachmentW*attachmentH);
    591 		attachments[ndx].referenceData.resize(refFmt.getPixelSize()*attachmentW*attachmentH);
    592 	}
    593 
    594 	// Initialize indices.
    595 	for (int quadNdx = 0; quadNdx < numQuads; quadNdx++)
    596 	{
    597 		int	quadY	= quadNdx / (gridWidth-1);
    598 		int quadX	= quadNdx - quadY*(gridWidth-1);
    599 
    600 		indices[quadNdx*6+0] = deUint16(quadX + quadY*gridWidth);
    601 		indices[quadNdx*6+1] = deUint16(quadX + (quadY+1)*gridWidth);
    602 		indices[quadNdx*6+2] = deUint16(quadX + quadY*gridWidth + 1);
    603 		indices[quadNdx*6+3] = indices[quadNdx*6+1];
    604 		indices[quadNdx*6+4] = deUint16(quadX + (quadY+1)*gridWidth + 1);
    605 		indices[quadNdx*6+5] = indices[quadNdx*6+2];
    606 	}
    607 
    608 	for (int y = 0; y < gridHeight; y++)
    609 	{
    610 		for (int x = 0; x < gridWidth; x++)
    611 		{
    612 			float	xf	= (float)x / (float)(gridWidth-1);
    613 			float	yf	= (float)y / (float)(gridHeight-1);
    614 
    615 			positions[(y*gridWidth + x)*4 + 0] = 2.0f*xf - 1.0f;
    616 			positions[(y*gridWidth + x)*4 + 1] = 2.0f*yf - 1.0f;
    617 			positions[(y*gridWidth + x)*4 + 2] = 0.0f;
    618 			positions[(y*gridWidth + x)*4 + 3] = 1.0f;
    619 		}
    620 	}
    621 
    622 	// Initialize input vectors.
    623 	{
    624 		int curInVec = 0;
    625 		for (int outputNdx = 0; outputNdx < (int)m_outputs.size(); outputNdx++)
    626 		{
    627 			const FragmentOutput&	output		= m_outputs[outputNdx];
    628 			bool					isFloat		= glu::isDataTypeFloatOrVec(output.type);
    629 			bool					isInt		= glu::isDataTypeIntOrIVec(output.type);
    630 			bool					isUint		= glu::isDataTypeUintOrUVec(output.type);
    631 			int						numVecs		= output.arrayLength > 0 ? output.arrayLength : 1;
    632 			int						numScalars	= glu::getDataTypeScalarSize(output.type);
    633 
    634 			for (int vecNdx = 0; vecNdx < numVecs; vecNdx++)
    635 			{
    636 				inputs[curInVec].resize(numVertices*numScalars);
    637 
    638 				// Record how many outputs are written in attachment.
    639 				DE_ASSERT(output.location+vecNdx < (int)attachments.size());
    640 				attachments[output.location+vecNdx].numWrittenChannels	= numScalars;
    641 				attachments[output.location+vecNdx].outPrecision		= output.precision;
    642 
    643 				if (isFloat)
    644 				{
    645 					Vec2		range	= getFloatRange(output.precision);
    646 					Vec4		minVal	(range.x());
    647 					Vec4		maxVal	(range.y());
    648 					float*		dst		= (float*)&inputs[curInVec][0];
    649 
    650 					if (de::inBounds(output.location+vecNdx, 0, (int)attachments.size()))
    651 					{
    652 						// \note Floating-point precision conversion is not well-defined. For that reason we must
    653 						//       limit value range to intersection of both data type and render target value ranges.
    654 						const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(attachments[output.location+vecNdx].format);
    655 						minVal = tcu::max(minVal, fmtInfo.valueMin);
    656 						maxVal = tcu::min(maxVal, fmtInfo.valueMax);
    657 					}
    658 
    659 					m_testCtx.getLog() << TestLog::Message << "out" << curInVec << " value range: " << valueRangeToString(numScalars, minVal, maxVal) << TestLog::EndMessage;
    660 
    661 					for (int y = 0; y < gridHeight; y++)
    662 					{
    663 						for (int x = 0; x < gridWidth; x++)
    664 						{
    665 							float	xf	= (float)x / (float)(gridWidth-1);
    666 							float	yf	= (float)y / (float)(gridHeight-1);
    667 
    668 							float	f0	= (xf + yf) * 0.5f;
    669 							float	f1	= 0.5f + (xf - yf) * 0.5f;
    670 							Vec4	f	= swizzleVec(Vec4(f0, f1, 1.0f-f0, 1.0f-f1), curInVec);
    671 							Vec4	c	= minVal + (maxVal-minVal)*f;
    672 							float*	v	= dst + (y*gridWidth + x)*numScalars;
    673 
    674 							for (int ndx = 0; ndx < numScalars; ndx++)
    675 								v[ndx] = c[ndx];
    676 						}
    677 					}
    678 				}
    679 				else if (isInt)
    680 				{
    681 					const IVec2	range	= getIntRange(output.precision);
    682 					IVec4		minVal	(range.x());
    683 					IVec4		maxVal	(range.y());
    684 
    685 					if (de::inBounds(output.location+vecNdx, 0, (int)attachments.size()))
    686 					{
    687 						// Limit to range of output format as conversion mode is not specified.
    688 						const IVec4 fmtBits		= tcu::getTextureFormatBitDepth(attachments[output.location+vecNdx].format);
    689 						const BVec4	isZero		= lessThanEqual(fmtBits, IVec4(0));
    690 						const IVec4	fmtMinVal	= (-(tcu::Vector<deInt64, 4>(1) << (fmtBits-1).cast<deInt64>())).asInt();
    691 						const IVec4	fmtMaxVal	= ((tcu::Vector<deInt64, 4>(1) << (fmtBits-1).cast<deInt64>())-deInt64(1)).asInt();
    692 
    693 						minVal = select(minVal, tcu::max(minVal, fmtMinVal), isZero);
    694 						maxVal = select(maxVal, tcu::min(maxVal, fmtMaxVal), isZero);
    695 					}
    696 
    697 					m_testCtx.getLog() << TestLog::Message << "out" << curInVec << " value range: " << valueRangeToString(numScalars, minVal, maxVal) << TestLog::EndMessage;
    698 
    699 					const IVec4	rangeDiv	= swizzleVec((IVec4(gridWidth, gridHeight, gridWidth, gridHeight)-1), curInVec);
    700 					const IVec4	step		= ((maxVal.cast<deInt64>() - minVal.cast<deInt64>()) / (rangeDiv.cast<deInt64>())).asInt();
    701 					deInt32*	dst			= (deInt32*)&inputs[curInVec][0];
    702 
    703 					for (int y = 0; y < gridHeight; y++)
    704 					{
    705 						for (int x = 0; x < gridWidth; x++)
    706 						{
    707 							int			ix	= gridWidth - x - 1;
    708 							int			iy	= gridHeight - y - 1;
    709 							IVec4		c	= minVal + step*swizzleVec(IVec4(x, y, ix, iy), curInVec);
    710 							deInt32*	v	= dst + (y*gridWidth + x)*numScalars;
    711 
    712 							DE_ASSERT(boolAll(logicalAnd(greaterThanEqual(c, minVal), lessThanEqual(c, maxVal))));
    713 
    714 							for (int ndx = 0; ndx < numScalars; ndx++)
    715 								v[ndx] = c[ndx];
    716 						}
    717 					}
    718 				}
    719 				else if (isUint)
    720 				{
    721 					const UVec2	range	= getUintRange(output.precision);
    722 					UVec4		maxVal	(range.y());
    723 
    724 					if (de::inBounds(output.location+vecNdx, 0, (int)attachments.size()))
    725 					{
    726 						// Limit to range of output format as conversion mode is not specified.
    727 						const IVec4	fmtBits		= tcu::getTextureFormatBitDepth(attachments[output.location+vecNdx].format);
    728 						const UVec4	fmtMaxVal	= ((tcu::Vector<deUint64, 4>(1) << fmtBits.cast<deUint64>())-deUint64(1)).asUint();
    729 
    730 						maxVal = tcu::min(maxVal, fmtMaxVal);
    731 					}
    732 
    733 					m_testCtx.getLog() << TestLog::Message << "out" << curInVec << " value range: "  << valueRangeToString(numScalars, UVec4(0), maxVal) << TestLog::EndMessage;
    734 
    735 					const IVec4	rangeDiv	= swizzleVec((IVec4(gridWidth, gridHeight, gridWidth, gridHeight)-1), curInVec);
    736 					const UVec4	step		= maxVal / rangeDiv.asUint();
    737 					deUint32*	dst			= &inputs[curInVec][0];
    738 
    739 					DE_ASSERT(range.x() == 0);
    740 
    741 					for (int y = 0; y < gridHeight; y++)
    742 					{
    743 						for (int x = 0; x < gridWidth; x++)
    744 						{
    745 							int			ix	= gridWidth - x - 1;
    746 							int			iy	= gridHeight - y - 1;
    747 							UVec4		c	= step*swizzleVec(IVec4(x, y, ix, iy).asUint(), curInVec);
    748 							deUint32*	v	= dst + (y*gridWidth + x)*numScalars;
    749 
    750 							DE_ASSERT(boolAll(lessThanEqual(c, maxVal)));
    751 
    752 							for (int ndx = 0; ndx < numScalars; ndx++)
    753 								v[ndx] = c[ndx];
    754 						}
    755 					}
    756 				}
    757 				else
    758 					DE_ASSERT(false);
    759 
    760 				curInVec += 1;
    761 			}
    762 		}
    763 	}
    764 
    765 	// Render using gl.
    766 	gl.useProgram(m_program->getProgram());
    767 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
    768 	gl.viewport(0, 0, viewportW, viewportH);
    769 	gl.drawBuffers((int)drawBuffers.size(), &drawBuffers[0]);
    770 	gl.disable(GL_DITHER); // Dithering causes issues with unorm formats. Those issues could be worked around in threshold, but it makes validation less accurate.
    771 	GLU_EXPECT_NO_ERROR(gl.getError(), "After program setup");
    772 
    773 	{
    774 		int curInVec = 0;
    775 		for (int outputNdx = 0; outputNdx < (int)m_outputs.size(); outputNdx++)
    776 		{
    777 			const FragmentOutput&	output			= m_outputs[outputNdx];
    778 			bool					isArray			= output.arrayLength > 0;
    779 			bool					isFloat			= glu::isDataTypeFloatOrVec(output.type);
    780 			bool					isInt			= glu::isDataTypeIntOrIVec(output.type);
    781 			bool					isUint			= glu::isDataTypeUintOrUVec(output.type);
    782 			int						scalarSize		= glu::getDataTypeScalarSize(output.type);
    783 			deUint32				glScalarType	= isFloat	? GL_FLOAT			:
    784 													  isInt		? GL_INT			:
    785 													  isUint	? GL_UNSIGNED_INT	: GL_NONE;
    786 			int						numVecs			= isArray ? output.arrayLength : 1;
    787 
    788 			for (int vecNdx = 0; vecNdx < numVecs; vecNdx++)
    789 			{
    790 				string	name	= string("in") + de::toString(outputNdx) + (isArray ? string("_") + de::toString(vecNdx) : string());
    791 				int		loc		= gl.getAttribLocation(m_program->getProgram(), name.c_str());
    792 
    793 				if (loc >= 0)
    794 				{
    795 					gl.enableVertexAttribArray(loc);
    796 					if (isFloat)
    797 						gl.vertexAttribPointer(loc, scalarSize, glScalarType, GL_FALSE, 0, &inputs[curInVec][0]);
    798 					else
    799 						gl.vertexAttribIPointer(loc, scalarSize, glScalarType, 0, &inputs[curInVec][0]);
    800 				}
    801 				else
    802 					log << TestLog::Message << "Warning: No location for attribute '" << name << "' found." << TestLog::EndMessage;
    803 
    804 				curInVec += 1;
    805 			}
    806 		}
    807 	}
    808 	{
    809 		int posLoc = gl.getAttribLocation(m_program->getProgram(), "a_position");
    810 		TCU_CHECK(posLoc >= 0);
    811 		gl.enableVertexAttribArray(posLoc);
    812 		gl.vertexAttribPointer(posLoc, 4, GL_FLOAT, GL_FALSE, 0, &positions[0]);
    813 	}
    814 	GLU_EXPECT_NO_ERROR(gl.getError(), "After attribute setup");
    815 
    816 	gl.drawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, &indices[0]);
    817 	GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawElements");
    818 
    819 	// Read all attachment points.
    820 	for (int ndx = 0; ndx < numAttachments; ndx++)
    821 	{
    822 		const glu::TransferFormat		transferFmt			= glu::getTransferFormat(attachments[ndx].readFormat);
    823 		void*							dst					= &attachments[ndx].renderedData[0];
    824 		const int						attachmentW			= m_fboSpec[ndx].width;
    825 		const int						attachmentH			= m_fboSpec[ndx].height;
    826 		const int						numValidChannels	= attachments[ndx].numWrittenChannels;
    827 		const tcu::PixelBufferAccess	rendered			(attachments[ndx].readFormat, attachmentW, attachmentH, 1, deAlign32(attachments[ndx].readFormat.getPixelSize()*attachmentW, readAlignment), 0, &attachments[ndx].renderedData[0]);
    828 
    829 		gl.readBuffer(GL_COLOR_ATTACHMENT0+ndx);
    830 		gl.readPixels(0, 0, minBufSize.x(), minBufSize.y(), transferFmt.format, transferFmt.dataType, dst);
    831 
    832 		clearUndefined(rendered, numValidChannels);
    833 	}
    834 
    835 	// Render reference images.
    836 	{
    837 		int curInNdx = 0;
    838 		for (int outputNdx = 0; outputNdx < (int)m_outputs.size(); outputNdx++)
    839 		{
    840 			const FragmentOutput&	output			= m_outputs[outputNdx];
    841 			const bool				isArray			= output.arrayLength > 0;
    842 			const bool				isFloat			= glu::isDataTypeFloatOrVec(output.type);
    843 			const bool				isInt			= glu::isDataTypeIntOrIVec(output.type);
    844 			const bool				isUint			= glu::isDataTypeUintOrUVec(output.type);
    845 			const int				scalarSize		= glu::getDataTypeScalarSize(output.type);
    846 			const int				numVecs			= isArray ? output.arrayLength : 1;
    847 
    848 			for (int vecNdx = 0; vecNdx < numVecs; vecNdx++)
    849 			{
    850 				const int		location	= output.location+vecNdx;
    851 				const void*		inputData	= &inputs[curInNdx][0];
    852 
    853 				DE_ASSERT(de::inBounds(location, 0, (int)m_fboSpec.size()));
    854 
    855 				const int						bufW			= m_fboSpec[location].width;
    856 				const int						bufH			= m_fboSpec[location].height;
    857 				const tcu::PixelBufferAccess	buf				(attachments[location].referenceFormat, bufW, bufH, 1, &attachments[location].referenceData[0]);
    858 				const tcu::PixelBufferAccess	viewportBuf		= getSubregion(buf, 0, 0, 0, viewportW, viewportH, 1);
    859 
    860 				if (isInt || isUint)
    861 					renderIntReference(viewportBuf, gridWidth, gridHeight, scalarSize, (const int*)inputData);
    862 				else if (isFloat)
    863 					renderFloatReference(viewportBuf, gridWidth, gridHeight, scalarSize, (const float*)inputData);
    864 				else
    865 					DE_ASSERT(false);
    866 
    867 				curInNdx += 1;
    868 			}
    869 		}
    870 	}
    871 
    872 	// Compare all images.
    873 	bool allLevelsOk = true;
    874 	for (int attachNdx = 0; attachNdx < numAttachments; attachNdx++)
    875 	{
    876 		const int						attachmentW			= m_fboSpec[attachNdx].width;
    877 		const int						attachmentH			= m_fboSpec[attachNdx].height;
    878 		const int						numValidChannels	= attachments[attachNdx].numWrittenChannels;
    879 		const tcu::BVec4				cmpMask				(numValidChannels >= 1, numValidChannels >= 2, numValidChannels >= 3, numValidChannels >= 4);
    880 		const glu::Precision			outPrecision		= attachments[attachNdx].outPrecision;
    881 		const tcu::TextureFormat&		format				= attachments[attachNdx].format;
    882 		tcu::ConstPixelBufferAccess		rendered			(attachments[attachNdx].readFormat, attachmentW, attachmentH, 1, deAlign32(attachments[attachNdx].readFormat.getPixelSize()*attachmentW, readAlignment), 0, &attachments[attachNdx].renderedData[0]);
    883 		tcu::ConstPixelBufferAccess		reference			(attachments[attachNdx].referenceFormat, attachmentW, attachmentH, 1, &attachments[attachNdx].referenceData[0]);
    884 		tcu::TextureChannelClass		texClass			= tcu::getTextureChannelClass(format.type);
    885 		bool							isOk				= true;
    886 		const string					name				= string("Attachment") + de::toString(attachNdx);
    887 		const string					desc				= string("Color attachment ") + de::toString(attachNdx);
    888 
    889 		log << TestLog::Message << "Attachment " << attachNdx << ": " << numValidChannels << " channels have defined values and used for comparison" << TestLog::EndMessage;
    890 
    891 		switch (texClass)
    892 		{
    893 			case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
    894 			{
    895 				const deUint32	interpThreshold		= 4;	//!< 4 ULP interpolation threshold (interpolation always in highp)
    896 				deUint32		outTypeThreshold	= 0;	//!< Threshold based on output type
    897 				UVec4			formatThreshold;			//!< Threshold computed based on format.
    898 				UVec4			finalThreshold;
    899 
    900 				// 1 ULP rounding error is allowed for smaller floating-point formats
    901 				switch (format.type)
    902 				{
    903 					case tcu::TextureFormat::FLOAT:							formatThreshold = UVec4(0);											break;
    904 					case tcu::TextureFormat::HALF_FLOAT:					formatThreshold = UVec4((1<<(23-10)));								break;
    905 					case tcu::TextureFormat::UNSIGNED_INT_11F_11F_10F_REV:	formatThreshold = UVec4((1<<(23-6)), (1<<(23-6)), (1<<(23-5)), 0);	break;
    906 					default:
    907 						DE_ASSERT(false);
    908 						break;
    909 				}
    910 
    911 				// 1 ULP rounding error for highp -> output precision cast
    912 				switch (outPrecision)
    913 				{
    914 					case glu::PRECISION_LOWP:		outTypeThreshold	= (1<<(23-8));	break;
    915 					case glu::PRECISION_MEDIUMP:	outTypeThreshold	= (1<<(23-10));	break;
    916 					case glu::PRECISION_HIGHP:		outTypeThreshold	= 0;			break;
    917 					default:
    918 						DE_ASSERT(false);
    919 				}
    920 
    921 				finalThreshold = select(max(formatThreshold, UVec4(deMax32(interpThreshold, outTypeThreshold))), UVec4(~0u), cmpMask);
    922 
    923 				isOk = tcu::floatUlpThresholdCompare(log, name.c_str(), desc.c_str(), reference, rendered, finalThreshold, tcu::COMPARE_LOG_RESULT);
    924 				break;
    925 			}
    926 
    927 			case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
    928 			{
    929 				// \note glReadPixels() allows only 8 bits to be read. This means that RGB10_A2 will loose some
    930 				// bits in the process and it must be taken into account when computing threshold.
    931 				const IVec4		bits			= min(IVec4(8), tcu::getTextureFormatBitDepth(format));
    932 				const Vec4		baseThreshold	= 1.0f / ((IVec4(1) << bits)-1).asFloat();
    933 				const Vec4		threshold		= select(baseThreshold, Vec4(2.0f), cmpMask);
    934 
    935 				isOk = tcu::floatThresholdCompare(log, name.c_str(), desc.c_str(), reference, rendered, threshold, tcu::COMPARE_LOG_RESULT);
    936 				break;
    937 			}
    938 
    939 			case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
    940 			case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
    941 			{
    942 				const tcu::UVec4 threshold = select(UVec4(0u), UVec4(~0u), cmpMask);
    943 				isOk = tcu::intThresholdCompare(log, name.c_str(), desc.c_str(), reference, rendered, threshold, tcu::COMPARE_LOG_RESULT);
    944 				break;
    945 			}
    946 
    947 			default:
    948 				TCU_FAIL("Unsupported comparison");
    949 				break;
    950 		}
    951 
    952 		if (!isOk)
    953 			allLevelsOk = false;
    954 	}
    955 
    956 	m_testCtx.setTestResult(allLevelsOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
    957 							allLevelsOk ? "Pass"				: "Image comparison failed");
    958 	return STOP;
    959 }
    960 
    961 FragmentOutputTests::FragmentOutputTests (Context& context)
    962 	: TestCaseGroup(context, "fragment_out", "Fragment output tests")
    963 {
    964 }
    965 
    966 FragmentOutputTests::~FragmentOutputTests (void)
    967 {
    968 }
    969 
    970 static FragmentOutputCase* createRandomCase (Context& context, int minRenderTargets, int maxRenderTargets, deUint32 seed)
    971 {
    972 	static const glu::DataType outputTypes[] =
    973 	{
    974 		glu::TYPE_FLOAT,
    975 		glu::TYPE_FLOAT_VEC2,
    976 		glu::TYPE_FLOAT_VEC3,
    977 		glu::TYPE_FLOAT_VEC4,
    978 		glu::TYPE_INT,
    979 		glu::TYPE_INT_VEC2,
    980 		glu::TYPE_INT_VEC3,
    981 		glu::TYPE_INT_VEC4,
    982 		glu::TYPE_UINT,
    983 		glu::TYPE_UINT_VEC2,
    984 		glu::TYPE_UINT_VEC3,
    985 		glu::TYPE_UINT_VEC4
    986 	};
    987 	static const glu::Precision precisions[] =
    988 	{
    989 		glu::PRECISION_LOWP,
    990 		glu::PRECISION_MEDIUMP,
    991 		glu::PRECISION_HIGHP
    992 	};
    993 	static const deUint32 floatFormats[] =
    994 	{
    995 		GL_RGBA32F,
    996 		GL_RGBA16F,
    997 		GL_R11F_G11F_B10F,
    998 		GL_RG32F,
    999 		GL_RG16F,
   1000 		GL_R32F,
   1001 		GL_R16F,
   1002 		GL_RGBA8,
   1003 		GL_SRGB8_ALPHA8,
   1004 		GL_RGB10_A2,
   1005 		GL_RGBA4,
   1006 		GL_RGB5_A1,
   1007 		GL_RGB8,
   1008 		GL_RGB565,
   1009 		GL_RG8,
   1010 		GL_R8
   1011 	};
   1012 	static const deUint32 intFormats[] =
   1013 	{
   1014 		GL_RGBA32I,
   1015 		GL_RGBA16I,
   1016 		GL_RGBA8I,
   1017 		GL_RG32I,
   1018 		GL_RG16I,
   1019 		GL_RG8I,
   1020 		GL_R32I,
   1021 		GL_R16I,
   1022 		GL_R8I
   1023 	};
   1024 	static const deUint32 uintFormats[] =
   1025 	{
   1026 		GL_RGBA32UI,
   1027 		GL_RGBA16UI,
   1028 		GL_RGBA8UI,
   1029 		GL_RGB10_A2UI,
   1030 		GL_RG32UI,
   1031 		GL_RG16UI,
   1032 		GL_RG8UI,
   1033 		GL_R32UI,
   1034 		GL_R16UI,
   1035 		GL_R8UI
   1036 	};
   1037 
   1038 	de::Random					rnd			(seed);
   1039 	vector<FragmentOutput>		outputs;
   1040 	vector<BufferSpec>			targets;
   1041 	vector<glu::DataType>		outTypes;
   1042 
   1043 	int							numTargets	= rnd.getInt(minRenderTargets, maxRenderTargets);
   1044 	const int					width		= 128; // \todo [2012-04-10 pyry] Separate randomized sizes per target?
   1045 	const int					height		= 64;
   1046 	const int					samples		= 0;
   1047 
   1048 	// Compute outputs.
   1049 	int curLoc = 0;
   1050 	while (curLoc < numTargets)
   1051 	{
   1052 		bool			useArray		= rnd.getFloat() < 0.3f;
   1053 		int				maxArrayLen		= numTargets-curLoc;
   1054 		int				arrayLen		= useArray ? rnd.getInt(1, maxArrayLen) : 0;
   1055 		glu::DataType	basicType		= rnd.choose<glu::DataType>(&outputTypes[0], &outputTypes[0] + DE_LENGTH_OF_ARRAY(outputTypes));
   1056 		glu::Precision	precision		= rnd.choose<glu::Precision>(&precisions[0], &precisions[0] + DE_LENGTH_OF_ARRAY(precisions));
   1057 		int				numLocations	= useArray ? arrayLen : 1;
   1058 
   1059 		outputs.push_back(FragmentOutput(basicType, precision, curLoc, arrayLen));
   1060 
   1061 		for (int ndx = 0; ndx < numLocations; ndx++)
   1062 			outTypes.push_back(basicType);
   1063 
   1064 		curLoc += numLocations;
   1065 	}
   1066 	DE_ASSERT(curLoc == numTargets);
   1067 	DE_ASSERT((int)outTypes.size() == numTargets);
   1068 
   1069 	// Compute buffers.
   1070 	while ((int)targets.size() < numTargets)
   1071 	{
   1072 		glu::DataType	outType		= outTypes[targets.size()];
   1073 		bool			isFloat		= glu::isDataTypeFloatOrVec(outType);
   1074 		bool			isInt		= glu::isDataTypeIntOrIVec(outType);
   1075 		bool			isUint		= glu::isDataTypeUintOrUVec(outType);
   1076 		deUint32		format		= 0;
   1077 
   1078 		if (isFloat)
   1079 			format = rnd.choose<deUint32>(&floatFormats[0], &floatFormats[0] + DE_LENGTH_OF_ARRAY(floatFormats));
   1080 		else if (isInt)
   1081 			format = rnd.choose<deUint32>(&intFormats[0], &intFormats[0] + DE_LENGTH_OF_ARRAY(intFormats));
   1082 		else if (isUint)
   1083 			format = rnd.choose<deUint32>(&uintFormats[0], &uintFormats[0] + DE_LENGTH_OF_ARRAY(uintFormats));
   1084 		else
   1085 			DE_ASSERT(false);
   1086 
   1087 		targets.push_back(BufferSpec(format, width, height, samples));
   1088 	}
   1089 
   1090 	return new FragmentOutputCase(context, de::toString(seed).c_str(), "", targets, outputs);
   1091 }
   1092 
   1093 void FragmentOutputTests::init (void)
   1094 {
   1095 	static const deUint32 requiredFloatFormats[] =
   1096 	{
   1097 		GL_RGBA32F,
   1098 		GL_RGBA16F,
   1099 		GL_R11F_G11F_B10F,
   1100 		GL_RG32F,
   1101 		GL_RG16F,
   1102 		GL_R32F,
   1103 		GL_R16F
   1104 	};
   1105 	static const deUint32 requiredFixedFormats[] =
   1106 	{
   1107 		GL_RGBA8,
   1108 		GL_SRGB8_ALPHA8,
   1109 		GL_RGB10_A2,
   1110 		GL_RGBA4,
   1111 		GL_RGB5_A1,
   1112 		GL_RGB8,
   1113 		GL_RGB565,
   1114 		GL_RG8,
   1115 		GL_R8
   1116 	};
   1117 	static const deUint32 requiredIntFormats[] =
   1118 	{
   1119 		GL_RGBA32I,
   1120 		GL_RGBA16I,
   1121 		GL_RGBA8I,
   1122 		GL_RG32I,
   1123 		GL_RG16I,
   1124 		GL_RG8I,
   1125 		GL_R32I,
   1126 		GL_R16I,
   1127 		GL_R8I
   1128 	};
   1129 	static const deUint32 requiredUintFormats[] =
   1130 	{
   1131 		GL_RGBA32UI,
   1132 		GL_RGBA16UI,
   1133 		GL_RGBA8UI,
   1134 		GL_RGB10_A2UI,
   1135 		GL_RG32UI,
   1136 		GL_RG16UI,
   1137 		GL_RG8UI,
   1138 		GL_R32UI,
   1139 		GL_R16UI,
   1140 		GL_R8UI
   1141 	};
   1142 
   1143 	static const glu::Precision precisions[] =
   1144 	{
   1145 		glu::PRECISION_LOWP,
   1146 		glu::PRECISION_MEDIUMP,
   1147 		glu::PRECISION_HIGHP
   1148 	};
   1149 
   1150 	// .basic.
   1151 	{
   1152 		tcu::TestCaseGroup* basicGroup = new tcu::TestCaseGroup(m_testCtx, "basic", "Basic fragment output tests");
   1153 		addChild(basicGroup);
   1154 
   1155 		const int	width	= 64;
   1156 		const int	height	= 64;
   1157 		const int	samples	= 0;
   1158 
   1159 		// .float
   1160 		tcu::TestCaseGroup* floatGroup = new tcu::TestCaseGroup(m_testCtx, "float", "Floating-point output tests");
   1161 		basicGroup->addChild(floatGroup);
   1162 		for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredFloatFormats); fmtNdx++)
   1163 		{
   1164 			deUint32			format		= requiredFloatFormats[fmtNdx];
   1165 			string				fmtName		= getFormatName(format);
   1166 			vector<BufferSpec>	fboSpec;
   1167 
   1168 			fboSpec.push_back(BufferSpec(format, width, height, samples));
   1169 
   1170 			for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
   1171 			{
   1172 				glu::Precision	prec		= precisions[precNdx];
   1173 				string			precName	= glu::getPrecisionName(prec);
   1174 
   1175 				floatGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_float").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT,		prec, 0)).toVec()));
   1176 				floatGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec2").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC2,	prec, 0)).toVec()));
   1177 				floatGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec3").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC3,	prec, 0)).toVec()));
   1178 				floatGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec4").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC4,	prec, 0)).toVec()));
   1179 			}
   1180 		}
   1181 
   1182 		// .fixed
   1183 		tcu::TestCaseGroup* fixedGroup = new tcu::TestCaseGroup(m_testCtx, "fixed", "Fixed-point output tests");
   1184 		basicGroup->addChild(fixedGroup);
   1185 		for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredFixedFormats); fmtNdx++)
   1186 		{
   1187 			deUint32			format		= requiredFixedFormats[fmtNdx];
   1188 			string				fmtName		= getFormatName(format);
   1189 			vector<BufferSpec>	fboSpec;
   1190 
   1191 			fboSpec.push_back(BufferSpec(format, width, height, samples));
   1192 
   1193 			for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
   1194 			{
   1195 				glu::Precision	prec		= precisions[precNdx];
   1196 				string			precName	= glu::getPrecisionName(prec);
   1197 
   1198 				fixedGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_float").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT,		prec, 0)).toVec()));
   1199 				fixedGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec2").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC2,	prec, 0)).toVec()));
   1200 				fixedGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec3").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC3,	prec, 0)).toVec()));
   1201 				fixedGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec4").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC4,	prec, 0)).toVec()));
   1202 			}
   1203 		}
   1204 
   1205 		// .int
   1206 		tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "int", "Integer output tests");
   1207 		basicGroup->addChild(intGroup);
   1208 		for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredIntFormats); fmtNdx++)
   1209 		{
   1210 			deUint32			format		= requiredIntFormats[fmtNdx];
   1211 			string				fmtName		= getFormatName(format);
   1212 			vector<BufferSpec>	fboSpec;
   1213 
   1214 			fboSpec.push_back(BufferSpec(format, width, height, samples));
   1215 
   1216 			for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
   1217 			{
   1218 				glu::Precision	prec		= precisions[precNdx];
   1219 				string			precName	= glu::getPrecisionName(prec);
   1220 
   1221 				intGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_int").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_INT,		prec, 0)).toVec()));
   1222 				intGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_ivec2").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC2,	prec, 0)).toVec()));
   1223 				intGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_ivec3").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC3,	prec, 0)).toVec()));
   1224 				intGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_ivec4").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC4,	prec, 0)).toVec()));
   1225 			}
   1226 		}
   1227 
   1228 		// .uint
   1229 		tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uint", "Usigned integer output tests");
   1230 		basicGroup->addChild(uintGroup);
   1231 		for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredUintFormats); fmtNdx++)
   1232 		{
   1233 			deUint32			format		= requiredUintFormats[fmtNdx];
   1234 			string				fmtName		= getFormatName(format);
   1235 			vector<BufferSpec>	fboSpec;
   1236 
   1237 			fboSpec.push_back(BufferSpec(format, width, height, samples));
   1238 
   1239 			for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
   1240 			{
   1241 				glu::Precision	prec		= precisions[precNdx];
   1242 				string			precName	= glu::getPrecisionName(prec);
   1243 
   1244 				uintGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uint").c_str(),		"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_UINT,			prec, 0)).toVec()));
   1245 				uintGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uvec2").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC2,	prec, 0)).toVec()));
   1246 				uintGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uvec3").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC3,	prec, 0)).toVec()));
   1247 				uintGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uvec4").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC4,	prec, 0)).toVec()));
   1248 			}
   1249 		}
   1250 	}
   1251 
   1252 	// .array
   1253 	{
   1254 		tcu::TestCaseGroup* arrayGroup = new tcu::TestCaseGroup(m_testCtx, "array", "Array outputs");
   1255 		addChild(arrayGroup);
   1256 
   1257 		const int	width		= 64;
   1258 		const int	height		= 64;
   1259 		const int	samples		= 0;
   1260 		const int	numTargets	= 3;
   1261 
   1262 		// .float
   1263 		tcu::TestCaseGroup* floatGroup = new tcu::TestCaseGroup(m_testCtx, "float", "Floating-point output tests");
   1264 		arrayGroup->addChild(floatGroup);
   1265 		for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredFloatFormats); fmtNdx++)
   1266 		{
   1267 			deUint32			format		= requiredFloatFormats[fmtNdx];
   1268 			string				fmtName		= getFormatName(format);
   1269 			vector<BufferSpec>	fboSpec;
   1270 
   1271 			for (int ndx = 0; ndx < numTargets; ndx++)
   1272 				fboSpec.push_back(BufferSpec(format, width, height, samples));
   1273 
   1274 			for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
   1275 			{
   1276 				glu::Precision	prec		= precisions[precNdx];
   1277 				string			precName	= glu::getPrecisionName(prec);
   1278 
   1279 				floatGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_float").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT,		prec, 0, numTargets)).toVec()));
   1280 				floatGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec2").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC2,	prec, 0, numTargets)).toVec()));
   1281 				floatGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec3").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC3,	prec, 0, numTargets)).toVec()));
   1282 				floatGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec4").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC4,	prec, 0, numTargets)).toVec()));
   1283 			}
   1284 		}
   1285 
   1286 		// .fixed
   1287 		tcu::TestCaseGroup* fixedGroup = new tcu::TestCaseGroup(m_testCtx, "fixed", "Fixed-point output tests");
   1288 		arrayGroup->addChild(fixedGroup);
   1289 		for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredFixedFormats); fmtNdx++)
   1290 		{
   1291 			deUint32			format		= requiredFixedFormats[fmtNdx];
   1292 			string				fmtName		= getFormatName(format);
   1293 			vector<BufferSpec>	fboSpec;
   1294 
   1295 			for (int ndx = 0; ndx < numTargets; ndx++)
   1296 				fboSpec.push_back(BufferSpec(format, width, height, samples));
   1297 
   1298 			for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
   1299 			{
   1300 				glu::Precision	prec		= precisions[precNdx];
   1301 				string			precName	= glu::getPrecisionName(prec);
   1302 
   1303 				fixedGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_float").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT,		prec, 0, numTargets)).toVec()));
   1304 				fixedGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec2").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC2,	prec, 0, numTargets)).toVec()));
   1305 				fixedGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec3").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC3,	prec, 0, numTargets)).toVec()));
   1306 				fixedGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_vec4").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_FLOAT_VEC4,	prec, 0, numTargets)).toVec()));
   1307 			}
   1308 		}
   1309 
   1310 		// .int
   1311 		tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "int", "Integer output tests");
   1312 		arrayGroup->addChild(intGroup);
   1313 		for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredIntFormats); fmtNdx++)
   1314 		{
   1315 			deUint32			format		= requiredIntFormats[fmtNdx];
   1316 			string				fmtName		= getFormatName(format);
   1317 			vector<BufferSpec>	fboSpec;
   1318 
   1319 			for (int ndx = 0; ndx < numTargets; ndx++)
   1320 				fboSpec.push_back(BufferSpec(format, width, height, samples));
   1321 
   1322 			for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
   1323 			{
   1324 				glu::Precision	prec		= precisions[precNdx];
   1325 				string			precName	= glu::getPrecisionName(prec);
   1326 
   1327 				intGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_int").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_INT,		prec, 0, numTargets)).toVec()));
   1328 				intGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_ivec2").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC2,	prec, 0, numTargets)).toVec()));
   1329 				intGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_ivec3").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC3,	prec, 0, numTargets)).toVec()));
   1330 				intGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_ivec4").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_INT_VEC4,	prec, 0, numTargets)).toVec()));
   1331 			}
   1332 		}
   1333 
   1334 		// .uint
   1335 		tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uint", "Usigned integer output tests");
   1336 		arrayGroup->addChild(uintGroup);
   1337 		for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(requiredUintFormats); fmtNdx++)
   1338 		{
   1339 			deUint32			format		= requiredUintFormats[fmtNdx];
   1340 			string				fmtName		= getFormatName(format);
   1341 			vector<BufferSpec>	fboSpec;
   1342 
   1343 			for (int ndx = 0; ndx < numTargets; ndx++)
   1344 				fboSpec.push_back(BufferSpec(format, width, height, samples));
   1345 
   1346 			for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
   1347 			{
   1348 				glu::Precision	prec		= precisions[precNdx];
   1349 				string			precName	= glu::getPrecisionName(prec);
   1350 
   1351 				uintGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uint").c_str(),		"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_UINT,			prec, 0, numTargets)).toVec()));
   1352 				uintGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uvec2").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC2,	prec, 0, numTargets)).toVec()));
   1353 				uintGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uvec3").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC3,	prec, 0, numTargets)).toVec()));
   1354 				uintGroup->addChild(new FragmentOutputCase(m_context, (fmtName + "_" + precName + "_uvec4").c_str(),	"",	fboSpec, (OutputVec() << FragmentOutput(glu::TYPE_UINT_VEC4,	prec, 0, numTargets)).toVec()));
   1355 			}
   1356 		}
   1357 	}
   1358 
   1359 	// .random
   1360 	{
   1361 		tcu::TestCaseGroup* randomGroup = new tcu::TestCaseGroup(m_testCtx, "random", "Random fragment output cases");
   1362 		addChild(randomGroup);
   1363 
   1364 		for (deUint32 seed = 0; seed < 100; seed++)
   1365 			randomGroup->addChild(createRandomCase(m_context, 2, 4, seed));
   1366 	}
   1367 }
   1368 
   1369 } // Functional
   1370 } // gles3
   1371 } // deqp
   1372