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