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