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
48namespace deqp
49{
50namespace gles3
51{
52namespace Functional
53{
54
55using std::vector;
56using std::string;
57using tcu::IVec2;
58using tcu::IVec4;
59using tcu::UVec2;
60using tcu::UVec4;
61using tcu::Vec2;
62using tcu::Vec3;
63using tcu::Vec4;
64using tcu::BVec4;
65using tcu::TestLog;
66using FboTestUtil::getFormatName;
67using FboTestUtil::getFramebufferReadFormat;
68
69struct 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
93struct 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
117struct 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
133class FragmentOutputCase : public TestCase
134{
135public:
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
143private:
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
155FragmentOutputCase::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
164FragmentOutputCase::~FragmentOutputCase (void)
165{
166	deinit();
167}
168
169static 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
240void 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
300void 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
320static 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
331static 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
339static 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
353static 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
366static 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
379static 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
388static 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
397static 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
428static 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
446static 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
458template <typename T>
459inline 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
465namespace
466{
467
468struct 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
479template<typename Type>
480string 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
502void 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
545FragmentOutputCase::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
961FragmentOutputTests::FragmentOutputTests (Context& context)
962	: TestCaseGroup(context, "fragment_out", "Fragment output tests")
963{
964}
965
966FragmentOutputTests::~FragmentOutputTests (void)
967{
968}
969
970static 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
1093void 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