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*				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
239void 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
299void 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
319static 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
330static 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
338static 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
352static 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
365static 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
378static 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
387static 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
396static 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
427static 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
445static 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
457template <typename T>
458inline 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
464namespace
465{
466
467struct 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
480FragmentOutputCase::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
887FragmentOutputTests::FragmentOutputTests (Context& context)
888	: TestCaseGroup(context, "fragment_out", "Fragment output tests")
889{
890}
891
892FragmentOutputTests::~FragmentOutputTests (void)
893{
894}
895
896static 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
1019void 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