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 Multisampling tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es3fMultisampleTests.hpp"
25#include "gluStrUtil.hpp"
26#include "gluShaderProgram.hpp"
27#include "gluPixelTransfer.hpp"
28#include "tcuSurface.hpp"
29#include "tcuImageCompare.hpp"
30#include "tcuRenderTarget.hpp"
31#include "tcuTestLog.hpp"
32#include "tcuTextureUtil.hpp"
33#include "tcuCommandLine.hpp"
34#include "deStringUtil.hpp"
35#include "deRandom.hpp"
36#include "deMath.h"
37#include "deString.h"
38
39#include <string>
40#include <vector>
41
42#include "glw.h"
43
44namespace deqp
45{
46namespace gles3
47{
48namespace Functional
49{
50
51using tcu::Vec2;
52using tcu::Vec3;
53using tcu::Vec4;
54using tcu::IVec2;
55using tcu::IVec4;
56using tcu::TestLog;
57using std::vector;
58
59static const GLenum		FBO_COLOR_FORMAT	= GL_RGBA8;
60static const float		SQRT_HALF			= 0.707107f;
61
62namespace
63{
64
65struct QuadCorners
66{
67	Vec2 p0;
68	Vec2 p1;
69	Vec2 p2;
70	Vec2 p3;
71
72	QuadCorners(const Vec2& p0_, const Vec2& p1_, const Vec2& p2_, const Vec2& p3_) : p0(p0_), p1(p1_), p2(p2_), p3(p3_) {}
73};
74
75} // anonymous
76
77static inline int getIterationCount (const tcu::TestContext& ctx, int defaultCount)
78{
79	int cmdLineValue = ctx.getCommandLine().getTestIterationCount();
80	return cmdLineValue > 0 ? cmdLineValue : defaultCount;
81}
82
83static inline int getGLInteger (GLenum name)
84{
85	int result;
86	GLU_CHECK_CALL(glGetIntegerv(name, &result));
87	return result;
88}
89
90template<typename T>
91static inline T min4 (T a, T b, T c, T d)
92{
93	return de::min(de::min(de::min(a, b), c), d);
94}
95
96template<typename T>
97static inline T max4 (T a, T b, T c, T d)
98{
99	return de::max(de::max(de::max(a, b), c), d);
100}
101
102static inline bool isInsideQuad (const IVec2& point, const IVec2& p0, const IVec2& p1, const IVec2& p2, const IVec2& p3)
103{
104	int dot0 = (point.x()-p0.x()) * (p1.y()-p0.y()) + (point.y()-p0.y()) * (p0.x()-p1.x());
105	int dot1 = (point.x()-p1.x()) * (p2.y()-p1.y()) + (point.y()-p1.y()) * (p1.x()-p2.x());
106	int dot2 = (point.x()-p2.x()) * (p3.y()-p2.y()) + (point.y()-p2.y()) * (p2.x()-p3.x());
107	int dot3 = (point.x()-p3.x()) * (p0.y()-p3.y()) + (point.y()-p3.y()) * (p3.x()-p0.x());
108
109	return (dot0 > 0) == (dot1 > 0) && (dot1 > 0) == (dot2 > 0) && (dot2 > 0) == (dot3 > 0);
110}
111
112/*--------------------------------------------------------------------*//*!
113 * \brief Check if a region in an image is unicolored.
114 *
115 * Checks if the pixels in img inside the convex quadilateral defined by
116 * p0, p1, p2 and p3 are all (approximately) of the same color.
117 *//*--------------------------------------------------------------------*/
118static bool isPixelRegionUnicolored (const tcu::Surface& img, const IVec2& p0, const IVec2& p1, const IVec2& p2, const IVec2& p3)
119{
120	int			xMin				= de::clamp(min4(p0.x(), p1.x(), p2.x(), p3.x()), 0, img.getWidth()-1);
121	int			yMin				= de::clamp(min4(p0.y(), p1.y(), p2.y(), p3.y()), 0, img.getHeight()-1);
122	int			xMax				= de::clamp(max4(p0.x(), p1.x(), p2.x(), p3.x()), 0, img.getWidth()-1);
123	int			yMax				= de::clamp(max4(p0.y(), p1.y(), p2.y(), p3.y()), 0, img.getHeight()-1);
124	bool		insideEncountered	= false;	//!< Whether we have already seen at least one pixel inside the region.
125	tcu::RGBA	insideColor;					//!< Color of the first pixel inside the region.
126
127	for (int y = yMin; y <= yMax; y++)
128	for (int x = xMin; x <= xMax; x++)
129	{
130		if (isInsideQuad(IVec2(x, y), p0, p1, p2, p3))
131		{
132			tcu::RGBA pixColor = img.getPixel(x, y);
133
134			if (insideEncountered)
135			{
136				if (!tcu::compareThreshold(pixColor, insideColor, tcu::RGBA(3, 3, 3, 3))) // Pixel color differs from already-detected color inside same region - region not unicolored.
137					return false;
138			}
139			else
140			{
141				insideEncountered = true;
142				insideColor = pixColor;
143			}
144		}
145	}
146
147	return true;
148}
149
150static bool drawUnicolorTestErrors (tcu::Surface& img, const tcu::PixelBufferAccess& errorImg, const IVec2& p0, const IVec2& p1, const IVec2& p2, const IVec2& p3)
151{
152	int			xMin		= de::clamp(min4(p0.x(), p1.x(), p2.x(), p3.x()), 0, img.getWidth()-1);
153	int			yMin		= de::clamp(min4(p0.y(), p1.y(), p2.y(), p3.y()), 0, img.getHeight()-1);
154	int			xMax		= de::clamp(max4(p0.x(), p1.x(), p2.x(), p3.x()), 0, img.getWidth()-1);
155	int			yMax		= de::clamp(max4(p0.y(), p1.y(), p2.y(), p3.y()), 0, img.getHeight()-1);
156	tcu::RGBA	refColor	= img.getPixel((xMin + xMax) / 2, (yMin + yMax) / 2);
157
158	for (int y = yMin; y <= yMax; y++)
159	for (int x = xMin; x <= xMax; x++)
160	{
161		if (isInsideQuad(IVec2(x, y), p0, p1, p2, p3))
162		{
163			if (!tcu::compareThreshold(img.getPixel(x, y), refColor, tcu::RGBA(3, 3, 3, 3)))
164			{
165				img.setPixel(x, y, tcu::RGBA::red());
166				errorImg.setPixel(Vec4(1.0f, 0.0f, 0.0f, 1.0f), x, y);
167			}
168		}
169	}
170
171	return true;
172}
173
174/*--------------------------------------------------------------------*//*!
175 * \brief Abstract base class handling common stuff for multisample cases.
176 *//*--------------------------------------------------------------------*/
177class MultisampleCase : public TestCase
178{
179public:
180	struct FboParams
181	{
182		bool		useFbo;
183		int			numSamples; //!< If 0, use implementation-defined maximum.
184		bool		useDepth;
185		bool		useStencil;
186
187		FboParams (int numSamples_, bool useDepth_, bool useStencil_)
188			: useFbo			(true)
189			, numSamples		(numSamples_)
190			, useDepth			(useDepth_)
191			, useStencil		(useStencil_)
192		{
193		}
194
195		FboParams (void)
196			: useFbo			(false)
197			, numSamples		(-1)
198			, useDepth			(false)
199			, useStencil		(false)
200		{
201		}
202	};
203
204						MultisampleCase			(Context& context, const char* name, const char* desc, int desiredViewportSize, const FboParams& fboParams = FboParams());
205	virtual				~MultisampleCase		(void);
206
207	virtual void		init					(void);
208	virtual void		deinit					(void);
209
210protected:
211	void				renderTriangle			(const Vec3& p0, const Vec3& p1, const Vec3& p2, const Vec4& c0, const Vec4& c1, const Vec4& c2) const;
212	void				renderTriangle			(const Vec3& p0, const Vec3& p1, const Vec3& p2, const Vec4& color) const;
213	void				renderTriangle			(const Vec2& p0, const Vec2& p1, const Vec2& p2, const Vec4& c0, const Vec4& c1, const Vec4& c2) const;
214	void				renderTriangle			(const Vec2& p0, const Vec2& p1, const Vec2& p2, const Vec4& color) const;
215	void				renderQuad				(const Vec2& p0, const Vec2& p1, const Vec2& p2, const Vec2& p3, const Vec4& c0, const Vec4& c1, const Vec4& c2, const Vec4& c3) const;
216	void				renderQuad				(const Vec2& p0, const Vec2& p1, const Vec2& p2, const Vec2& p3, const Vec4& color) const;
217	void				renderLine				(const Vec2& p0, const Vec2& p1, const Vec4& color) const;
218
219	void				randomizeViewport		(void);
220	void				readImage				(tcu::Surface& dst) const;
221
222	IVec2				getRenderTargetSize		(void) const 				{ return IVec2(m_renderWidth, m_renderHeight); }
223
224	int					m_numSamples;
225
226	int					m_viewportSize;
227
228private:
229						MultisampleCase			(const MultisampleCase& other);
230	MultisampleCase&	operator=				(const MultisampleCase& other);
231
232	const int			m_desiredViewportSize;
233
234	const FboParams		m_fboParams;
235	deUint32			m_msColorRbo;
236	deUint32			m_msDepthStencilRbo;
237	deUint32			m_resolveColorRbo;
238	deUint32			m_msFbo;
239	deUint32			m_resolveFbo;
240
241	glu::ShaderProgram*	m_program;
242	int					m_attrPositionLoc;
243	int					m_attrColorLoc;
244
245	int					m_renderWidth;
246	int					m_renderHeight;
247	int					m_viewportX;
248	int					m_viewportY;
249	de::Random			m_rnd;
250};
251
252MultisampleCase::MultisampleCase (Context& context, const char* name, const char* desc, int desiredViewportSize, const FboParams& fboParams)
253	: TestCase				(context, name, desc)
254	, m_numSamples			(0)
255	, m_viewportSize		(0)
256	, m_desiredViewportSize	(desiredViewportSize)
257	, m_fboParams			(fboParams)
258	, m_msColorRbo			(0)
259	, m_msDepthStencilRbo	(0)
260	, m_resolveColorRbo		(0)
261	, m_msFbo				(0)
262	, m_resolveFbo			(0)
263	, m_program				(DE_NULL)
264	, m_attrPositionLoc		(-1)
265	, m_attrColorLoc		(-1)
266	, m_renderWidth			(fboParams.useFbo ? 2*desiredViewportSize : context.getRenderTarget().getWidth())
267	, m_renderHeight		(fboParams.useFbo ? 2*desiredViewportSize : context.getRenderTarget().getHeight())
268	, m_viewportX			(0)
269	, m_viewportY			(0)
270	, m_rnd					(deStringHash(name))
271{
272	if (m_fboParams.useFbo)
273		DE_ASSERT(m_fboParams.numSamples >= 0);
274}
275
276MultisampleCase::~MultisampleCase (void)
277{
278	MultisampleCase::deinit();
279}
280
281void MultisampleCase::deinit (void)
282{
283	delete m_program;
284	m_program = DE_NULL;
285
286	GLU_CHECK_CALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
287	GLU_CHECK_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
288
289	if (m_msColorRbo != 0)
290	{
291		GLU_CHECK_CALL(glDeleteRenderbuffers(1, &m_msColorRbo));
292		m_msColorRbo = 0;
293	}
294	if (m_msDepthStencilRbo != 0)
295	{
296		GLU_CHECK_CALL(glDeleteRenderbuffers(1, &m_msDepthStencilRbo));
297		m_msDepthStencilRbo = 0;
298	}
299	if (m_resolveColorRbo != 0)
300	{
301		GLU_CHECK_CALL(glDeleteRenderbuffers(1, &m_resolveColorRbo));
302		m_resolveColorRbo = 0;
303	}
304
305	if (m_msFbo != 0)
306	{
307		GLU_CHECK_CALL(glDeleteFramebuffers(1, &m_msFbo));
308		m_msFbo = 0;
309	}
310	if (m_resolveFbo != 0)
311	{
312		GLU_CHECK_CALL(glDeleteFramebuffers(1, &m_resolveFbo));
313		m_resolveFbo = 0;
314	}
315}
316
317void MultisampleCase::renderTriangle (const Vec3& p0, const Vec3& p1, const Vec3& p2, const Vec4& c0, const Vec4& c1, const Vec4& c2) const
318{
319	float vertexPositions[] =
320	{
321		p0.x(), p0.y(), p0.z(), 1.0f,
322		p1.x(), p1.y(), p1.z(), 1.0f,
323		p2.x(), p2.y(), p2.z(), 1.0f
324	};
325	float vertexColors[] =
326	{
327		c0.x(), c0.y(), c0.z(), c0.w(),
328		c1.x(), c1.y(), c1.z(), c1.w(),
329		c2.x(), c2.y(), c2.z(), c2.w(),
330	};
331
332	GLU_CHECK_CALL(glEnableVertexAttribArray(m_attrPositionLoc));
333	GLU_CHECK_CALL(glVertexAttribPointer(m_attrPositionLoc, 4, GL_FLOAT, false, 0, &vertexPositions[0]));
334
335	GLU_CHECK_CALL(glEnableVertexAttribArray(m_attrColorLoc));
336	GLU_CHECK_CALL(glVertexAttribPointer(m_attrColorLoc, 4, GL_FLOAT, false, 0, &vertexColors[0]));
337
338	GLU_CHECK_CALL(glUseProgram(m_program->getProgram()));
339	GLU_CHECK_CALL(glDrawArrays(GL_TRIANGLES, 0, 3));
340}
341
342void MultisampleCase::renderTriangle (const Vec3& p0, const Vec3& p1, const Vec3& p2, const Vec4& color) const
343{
344	renderTriangle(p0, p1, p2, color, color, color);
345}
346
347void MultisampleCase::renderTriangle (const Vec2& p0, const Vec2& p1, const Vec2& p2, const Vec4& c0, const Vec4& c1, const Vec4& c2) const
348{
349	renderTriangle(Vec3(p0.x(), p0.y(), 0.0f),
350				   Vec3(p1.x(), p1.y(), 0.0f),
351				   Vec3(p2.x(), p2.y(), 0.0f),
352				   c0, c1, c2);
353}
354
355void MultisampleCase::renderTriangle (const Vec2& p0, const Vec2& p1, const Vec2& p2, const Vec4& color) const
356{
357	renderTriangle(p0, p1, p2, color, color, color);
358}
359
360void MultisampleCase::renderQuad (const Vec2& p0, const Vec2& p1, const Vec2& p2, const Vec2& p3, const Vec4& c0, const Vec4& c1, const Vec4& c2, const Vec4& c3) const
361{
362	renderTriangle(p0, p1, p2, c0, c1, c2);
363	renderTriangle(p2, p1, p3, c2, c1, c3);
364}
365
366void MultisampleCase::renderQuad (const Vec2& p0, const Vec2& p1, const Vec2& p2, const Vec2& p3, const Vec4& color) const
367{
368	renderQuad(p0, p1, p2, p3, color, color, color, color);
369}
370
371void MultisampleCase::renderLine (const Vec2& p0, const Vec2& p1, const Vec4& color) const
372{
373	float vertexPositions[] =
374	{
375		p0.x(), p0.y(), 0.0f, 1.0f,
376		p1.x(), p1.y(), 0.0f, 1.0f
377	};
378	float vertexColors[] =
379	{
380		color.x(), color.y(), color.z(), color.w(),
381		color.x(), color.y(), color.z(), color.w()
382	};
383
384	GLU_CHECK_CALL(glEnableVertexAttribArray(m_attrPositionLoc));
385	GLU_CHECK_CALL(glVertexAttribPointer(m_attrPositionLoc, 4, GL_FLOAT, false, 0, &vertexPositions[0]));
386
387	GLU_CHECK_CALL(glEnableVertexAttribArray(m_attrColorLoc));
388	GLU_CHECK_CALL(glVertexAttribPointer(m_attrColorLoc, 4, GL_FLOAT, false, 0, &vertexColors[0]));
389
390	GLU_CHECK_CALL(glUseProgram(m_program->getProgram()));
391	GLU_CHECK_CALL(glDrawArrays(GL_LINES, 0, 2));
392}
393
394void MultisampleCase::randomizeViewport (void)
395{
396	m_viewportX = m_rnd.getInt(0, m_renderWidth - m_viewportSize);
397	m_viewportY = m_rnd.getInt(0, m_renderHeight - m_viewportSize);
398
399	GLU_CHECK_CALL(glViewport(m_viewportX, m_viewportY, m_viewportSize, m_viewportSize));
400}
401
402void MultisampleCase::readImage (tcu::Surface& dst) const
403{
404	if (m_fboParams.useFbo)
405	{
406		GLU_CHECK_CALL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_resolveFbo));
407		GLU_CHECK_CALL(glBlitFramebuffer(0, 0, m_renderWidth, m_renderHeight, 0, 0, m_renderWidth, m_renderHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST));
408		GLU_CHECK_CALL(glBindFramebuffer(GL_READ_FRAMEBUFFER, m_resolveFbo));
409
410		glu::readPixels(m_context.getRenderContext(), m_viewportX, m_viewportY, dst.getAccess());
411
412		GLU_CHECK_CALL(glBindFramebuffer(GL_FRAMEBUFFER, m_msFbo));
413	}
414	else
415		glu::readPixels(m_context.getRenderContext(), m_viewportX, m_viewportY, dst.getAccess());
416}
417
418void MultisampleCase::init (void)
419{
420	static const char* vertShaderSource =
421		"#version 300 es\n"
422		"in highp vec4 a_position;\n"
423		"in mediump vec4 a_color;\n"
424		"out mediump vec4 v_color;\n"
425		"void main()\n"
426		"{\n"
427		"	gl_Position = a_position;\n"
428		"	v_color = a_color;\n"
429		"}\n";
430
431	static const char* fragShaderSource =
432		"#version 300 es\n"
433		"in mediump vec4 v_color;\n"
434		"layout(location = 0) out mediump vec4 o_color;\n"
435		"void main()\n"
436		"{\n"
437		"	o_color = v_color;\n"
438		"}\n";
439
440	TestLog& log = m_testCtx.getLog();
441
442	if (!m_fboParams.useFbo && m_context.getRenderTarget().getNumSamples() <= 1)
443		throw tcu::NotSupportedError("No multisample buffers");
444
445	if (m_fboParams.useFbo)
446	{
447		if (m_fboParams.numSamples > 0)
448			m_numSamples = m_fboParams.numSamples;
449		else
450		{
451			log << TestLog::Message << "Querying maximum number of samples for " << glu::getTextureFormatName(FBO_COLOR_FORMAT) << " with glGetInternalformativ()" << TestLog::EndMessage;
452			GLU_CHECK_CALL(glGetInternalformativ(GL_RENDERBUFFER, FBO_COLOR_FORMAT, GL_SAMPLES, 1, &m_numSamples));
453		}
454
455		log << TestLog::Message << "Using FBO of size (" << m_renderWidth << ", " << m_renderHeight << ") with " << m_numSamples << " samples" << TestLog::EndMessage;
456	}
457	else
458	{
459		// Query and log number of samples per pixel.
460
461		m_numSamples = getGLInteger(GL_SAMPLES);
462		log << TestLog::Message << "GL_SAMPLES = " << m_numSamples << TestLog::EndMessage;
463	}
464
465	// Prepare program.
466
467	DE_ASSERT(!m_program);
468
469	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertShaderSource, fragShaderSource));
470	if (!m_program->isOk())
471		throw tcu::TestError("Failed to compile program", DE_NULL, __FILE__, __LINE__);
472
473	GLU_CHECK_CALL(m_attrPositionLoc	= glGetAttribLocation(m_program->getProgram(), "a_position"));
474	GLU_CHECK_CALL(m_attrColorLoc		= glGetAttribLocation(m_program->getProgram(), "a_color"));
475
476	if (m_attrPositionLoc < 0 || m_attrColorLoc < 0)
477	{
478		delete m_program;
479		throw tcu::TestError("Invalid attribute locations", DE_NULL, __FILE__, __LINE__);
480	}
481
482	if (m_fboParams.useFbo)
483	{
484		DE_STATIC_ASSERT(sizeof(deUint32) == sizeof(GLuint));
485
486		// Setup ms color RBO.
487		GLU_CHECK_CALL(glGenRenderbuffers(1, &m_msColorRbo));
488		GLU_CHECK_CALL(glBindRenderbuffer(GL_RENDERBUFFER, m_msColorRbo));
489
490		// If glRenderbufferStorageMultisample() fails, check if it's because of a too high sample count.
491		// \note We don't do the check until now because some implementations can't handle the GL_SAMPLES query with glGetInternalformativ(),
492		//		 and we don't want that to be the cause of test case failure.
493		try
494		{
495			GLU_CHECK_CALL(glRenderbufferStorageMultisample(GL_RENDERBUFFER, m_numSamples, FBO_COLOR_FORMAT, m_renderWidth, m_renderHeight));
496		}
497		catch (const glu::Error&)
498		{
499			GLint maxSampleCount = -1;
500			GLU_CHECK_CALL(glGetInternalformativ(GL_RENDERBUFFER, FBO_COLOR_FORMAT, GL_SAMPLES, 1, &maxSampleCount));
501			if (maxSampleCount < m_numSamples)
502				throw tcu::NotSupportedError(std::string("") + "Maximum sample count returned by glGetInternalformativ() for " + glu::getTextureFormatName(FBO_COLOR_FORMAT) + " is only " + de::toString(maxSampleCount));
503			else
504				throw;
505		}
506
507		if (m_fboParams.useDepth || m_fboParams.useStencil)
508		{
509			// Setup ms depth & stencil RBO.
510			GLU_CHECK_CALL(glGenRenderbuffers(1, &m_msDepthStencilRbo));
511			GLU_CHECK_CALL(glBindRenderbuffer(GL_RENDERBUFFER, m_msDepthStencilRbo));
512			GLU_CHECK_CALL(glRenderbufferStorageMultisample(GL_RENDERBUFFER, m_numSamples, GL_DEPTH24_STENCIL8, m_renderWidth, m_renderHeight));
513		}
514
515		// Setup ms FBO.
516		GLU_CHECK_CALL(glGenFramebuffers(1, &m_msFbo));
517		GLU_CHECK_CALL(glBindFramebuffer(GL_FRAMEBUFFER, m_msFbo));
518		GLU_CHECK_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_msColorRbo));
519		GLU_CHECK_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_msDepthStencilRbo));
520
521		// Setup resolve color RBO.
522		GLU_CHECK_CALL(glGenRenderbuffers(1, &m_resolveColorRbo));
523		GLU_CHECK_CALL(glBindRenderbuffer(GL_RENDERBUFFER, m_resolveColorRbo));
524		GLU_CHECK_CALL(glRenderbufferStorage(GL_RENDERBUFFER, FBO_COLOR_FORMAT, m_renderWidth, m_renderHeight));
525
526		// Setup resolve FBO.
527		GLU_CHECK_CALL(glGenFramebuffers(1, &m_resolveFbo));
528		GLU_CHECK_CALL(glBindFramebuffer(GL_FRAMEBUFFER, m_resolveFbo));
529		GLU_CHECK_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_resolveColorRbo));
530
531		// Use ms FBO.
532		GLU_CHECK_CALL(glBindFramebuffer(GL_FRAMEBUFFER, m_msFbo));
533	}
534
535	// Get suitable viewport size.
536
537	m_viewportSize = de::min<int>(m_desiredViewportSize, de::min(m_renderWidth, m_renderHeight));
538	randomizeViewport();
539}
540
541/*--------------------------------------------------------------------*//*!
542 * \brief Base class for cases testing the value of sample count.
543 *
544 * Draws a test pattern (defined by renderPattern() of an inheriting class)
545 * and counts the number of distinct colors in the resulting image. That
546 * number should be at least the value of sample count plus one. This is
547 * repeated with increased values of m_currentIteration until this correct
548 * number of colors is detected or m_currentIteration reaches
549 * m_maxNumIterations.
550 *//*--------------------------------------------------------------------*/
551class NumSamplesCase : public MultisampleCase
552{
553public:
554						NumSamplesCase			(Context& context, const char* name, const char* description, const FboParams& fboParams = FboParams());
555						~NumSamplesCase			(void) {}
556
557	IterateResult		iterate					(void);
558
559protected:
560	virtual void		renderPattern			(void) const = 0;
561
562	int					m_currentIteration;
563
564private:
565	enum { DEFAULT_MAX_NUM_ITERATIONS = 16 };
566
567	const int			m_maxNumIterations;
568	vector<tcu::RGBA>	m_detectedColors;
569};
570
571NumSamplesCase::NumSamplesCase (Context& context, const char* name, const char* description, const FboParams& fboParams)
572	: MultisampleCase		(context, name, description, 256, fboParams)
573	, m_currentIteration	(0)
574	, m_maxNumIterations	(getIterationCount(m_testCtx, DEFAULT_MAX_NUM_ITERATIONS))
575{
576}
577
578NumSamplesCase::IterateResult NumSamplesCase::iterate (void)
579{
580	TestLog&		log				= m_testCtx.getLog();
581	tcu::Surface	renderedImg		(m_viewportSize, m_viewportSize);
582
583	randomizeViewport();
584
585	GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
586	GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT));
587
588	renderPattern();
589
590	// Read and log rendered image.
591
592	readImage(renderedImg);
593
594	log << TestLog::Image("RenderedImage", "Rendered image", renderedImg, QP_IMAGE_COMPRESSION_MODE_PNG);
595
596	// Detect new, previously unseen colors from image.
597
598	int requiredNumDistinctColors = m_numSamples + 1;
599
600	for (int y = 0; y < renderedImg.getHeight() && (int)m_detectedColors.size() < requiredNumDistinctColors; y++)
601	for (int x = 0; x < renderedImg.getWidth() && (int)m_detectedColors.size() < requiredNumDistinctColors; x++)
602	{
603		tcu::RGBA color = renderedImg.getPixel(x, y);
604
605		int i;
606		for (i = 0; i < (int)m_detectedColors.size(); i++)
607		{
608			if (tcu::compareThreshold(color, m_detectedColors[i], tcu::RGBA(3, 3, 3, 3)))
609				break;
610		}
611
612		if (i == (int)m_detectedColors.size())
613			m_detectedColors.push_back(color); // Color not previously detected.
614	}
615
616	// Log results.
617
618	log << TestLog::Message
619		<< "Number of distinct colors detected so far: "
620		<< ((int)m_detectedColors.size() >= requiredNumDistinctColors ? "at least " : "")
621		<< de::toString(m_detectedColors.size())
622		<< TestLog::EndMessage;
623
624	if ((int)m_detectedColors.size() < requiredNumDistinctColors)
625	{
626		// Haven't detected enough different colors yet.
627
628		m_currentIteration++;
629
630		if (m_currentIteration >= m_maxNumIterations)
631		{
632			const IVec2 targetSize 			= getRenderTargetSize();
633			const int 	detectedNumSamples 	= (int)m_detectedColors.size() - 1; // One color is the background
634
635			log << TestLog::Message << "Failure: Number of distinct colors detected is lower than sample count+1" << TestLog::EndMessage;
636
637			// For high resolution render targets the lack of samples is not likely detected by a human
638			// and for GLES 3.0 the application cannot observe the sample count directly. So, it only
639			// warrants a quality warning.
640			if ((targetSize.x() >= 2048 || targetSize.y() >= 2048) && (detectedNumSamples >= (m_numSamples/2)))
641				m_context.getTestContext().setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Measured sample count below the advertised count");
642			else
643				m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed");
644			return STOP;
645		}
646		else
647		{
648			log << TestLog::Message << "The number of distinct colors detected is lower than sample count+1 - trying again with a slightly altered pattern" << TestLog::EndMessage;
649			return CONTINUE;
650		}
651	}
652	else
653	{
654		log << TestLog::Message << "Success: The number of distinct colors detected is at least sample count+1" << TestLog::EndMessage;
655		m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Passed");
656		return STOP;
657	}
658}
659
660class PolygonNumSamplesCase : public NumSamplesCase
661{
662public:
663			PolygonNumSamplesCase	(Context& context, const char* name, const char* description, int numFboSamples = 0);
664			~PolygonNumSamplesCase	(void) {}
665
666protected:
667	void	renderPattern			(void) const;
668};
669
670PolygonNumSamplesCase::PolygonNumSamplesCase (Context& context, const char* name, const char* description, int numFboSamples)
671	: NumSamplesCase(context, name, description, numFboSamples >= 0 ? FboParams(numFboSamples, false, false) : FboParams())
672{
673}
674
675void PolygonNumSamplesCase::renderPattern (void) const
676{
677	// The test pattern consists of several triangles with edges at different angles.
678
679	const int numTriangles = 25;
680	for (int i = 0; i < numTriangles; i++)
681	{
682		float angle0 = 2.0f*DE_PI * (float)i			/ (float)numTriangles + 0.001f*(float)m_currentIteration;
683		float angle1 = 2.0f*DE_PI * ((float)i + 0.5f)	/ (float)numTriangles + 0.001f*(float)m_currentIteration;
684
685		renderTriangle(Vec2(0.0f, 0.0f),
686					   Vec2(deFloatCos(angle0)*0.95f, deFloatSin(angle0)*0.95f),
687					   Vec2(deFloatCos(angle1)*0.95f, deFloatSin(angle1)*0.95f),
688					   Vec4(1.0f));
689	}
690}
691
692class LineNumSamplesCase : public NumSamplesCase
693{
694public:
695			LineNumSamplesCase		(Context& context, const char* name, const char* description, int numFboSamples = 0);
696			~LineNumSamplesCase		(void) {}
697
698protected:
699	void	renderPattern			(void) const;
700};
701
702LineNumSamplesCase::LineNumSamplesCase (Context& context, const char* name, const char* description, int numFboSamples)
703	: NumSamplesCase (context, name, description, numFboSamples >= 0 ? FboParams(numFboSamples, false, false) : FboParams())
704{
705}
706
707void LineNumSamplesCase::renderPattern (void) const
708{
709	// The test pattern consists of several lines at different angles.
710
711	// We scale the number of lines based on the viewport size. This is because a gl line's thickness is
712	// constant in pixel units, i.e. they get relatively thicker as viewport size decreases. Thus we must
713	// decrease the number of lines in order to decrease the extent of overlap among the lines in the
714	// center of the pattern.
715	const int numLines = (int)(100.0f * deFloatSqrt((float)m_viewportSize / 256.0f));
716
717	for (int i = 0; i < numLines; i++)
718	{
719		float angle = 2.0f*DE_PI * (float)i / (float)numLines + 0.001f*(float)m_currentIteration;
720		renderLine(Vec2(0.0f, 0.0f), Vec2(deFloatCos(angle)*0.95f, deFloatSin(angle)*0.95f), Vec4(1.0f));
721	}
722}
723
724/*--------------------------------------------------------------------*//*!
725 * \brief Case testing behaviour of common edges when multisampling.
726 *
727 * Draws a number of test patterns, each with a number of quads, each made
728 * of two triangles, rotated at different angles. The inner edge inside the
729 * quad (i.e. the common edge of the two triangles) still should not be
730 * visible, despite multisampling - i.e. the two triangles forming the quad
731 * should never get any common coverage bits in any pixel.
732 *//*--------------------------------------------------------------------*/
733class CommonEdgeCase : public MultisampleCase
734{
735public:
736	enum CaseType
737	{
738		CASETYPE_SMALL_QUADS = 0,				//!< Draw several small quads per iteration.
739		CASETYPE_BIGGER_THAN_VIEWPORT_QUAD,		//!< Draw one bigger-than-viewport quad per iteration.
740		CASETYPE_FIT_VIEWPORT_QUAD,				//!< Draw one exactly viewport-sized, axis aligned quad per iteration.
741
742		CASETYPE_LAST
743	};
744
745					CommonEdgeCase			(Context& context, const char* name, const char* description, CaseType caseType, int numFboSamples = 0);
746					~CommonEdgeCase			(void) {}
747
748	void			init					(void);
749
750	IterateResult	iterate					(void);
751
752private:
753	enum
754	{
755		DEFAULT_SMALL_QUADS_ITERATIONS					= 16,
756		DEFAULT_BIGGER_THAN_VIEWPORT_QUAD_ITERATIONS	= 8*8
757		// \note With CASETYPE_FIT_VIEWPORT_QUAD, we don't do rotations other than multiples of 90 deg -> constant number of iterations.
758	};
759
760	const CaseType	m_caseType;
761
762	const int		m_numIterations;
763	int				m_currentIteration;
764};
765
766CommonEdgeCase::CommonEdgeCase (Context& context, const char* name, const char* description, CaseType caseType, int numFboSamples)
767	: MultisampleCase		(context, name, description, caseType == CASETYPE_SMALL_QUADS ? 128 : 32, numFboSamples >= 0 ? FboParams(numFboSamples, false, false) : FboParams())
768	, m_caseType			(caseType)
769	, m_numIterations		(caseType == CASETYPE_SMALL_QUADS					? getIterationCount(m_testCtx, DEFAULT_SMALL_QUADS_ITERATIONS)
770							: caseType == CASETYPE_BIGGER_THAN_VIEWPORT_QUAD	? getIterationCount(m_testCtx, DEFAULT_BIGGER_THAN_VIEWPORT_QUAD_ITERATIONS)
771							: 8)
772	, m_currentIteration	(0)
773{
774}
775
776void CommonEdgeCase::init (void)
777{
778	MultisampleCase::init();
779
780	if (m_caseType == CASETYPE_SMALL_QUADS)
781	{
782		// Check for a big enough viewport. With too small viewports the test case can't analyze the resulting image well enough.
783
784		const int minViewportSize = 32;
785
786		if (m_viewportSize < minViewportSize)
787			throw tcu::InternalError("Render target width or height too low (is " + de::toString(m_viewportSize) + ", should be at least " + de::toString(minViewportSize) + ")");
788	}
789
790	GLU_CHECK_CALL(glEnable(GL_BLEND));
791	GLU_CHECK_CALL(glBlendEquation(GL_FUNC_ADD));
792	GLU_CHECK_CALL(glBlendFunc(GL_ONE, GL_ONE));
793	m_testCtx.getLog() << TestLog::Message << "Additive blending enabled in order to detect (erroneously) overlapping samples" << TestLog::EndMessage;
794}
795
796CommonEdgeCase::IterateResult CommonEdgeCase::iterate (void)
797{
798	TestLog&		log				= m_testCtx.getLog();
799	tcu::Surface	renderedImg		(m_viewportSize, m_viewportSize);
800	tcu::Surface	errorImg		(m_viewportSize, m_viewportSize);
801
802	randomizeViewport();
803
804	GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
805	GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT));
806
807	// Draw test pattern. Test patterns consist of quads formed with two triangles.
808	// After drawing the pattern, we check that the interior pixels of each quad are
809	// all the same color - this is meant to verify that there are no artifacts on the inner edge.
810
811	vector<QuadCorners> unicoloredRegions;
812
813	if (m_caseType == CASETYPE_SMALL_QUADS)
814	{
815		// Draw several quads, rotated at different angles.
816
817		const float		quadDiagLen = 2.0f / 3.0f * 0.9f; // \note Fit 3 quads in both x and y directions.
818		float			angleCos;
819		float			angleSin;
820
821		// \note First and second iteration get exact 0 (and 90, 180, 270) and 45 (and 135, 225, 315) angle quads, as they are kind of a special case.
822
823		if (m_currentIteration == 0)
824		{
825			angleCos = 1.0f;
826			angleSin = 0.0f;
827		}
828		else if (m_currentIteration == 1)
829		{
830			angleCos = SQRT_HALF;
831			angleSin = SQRT_HALF;
832		}
833		else
834		{
835			float angle = 0.5f * DE_PI * (float)(m_currentIteration-1) / (float)(m_numIterations-1);
836			angleCos = deFloatCos(angle);
837			angleSin = deFloatSin(angle);
838		}
839
840		Vec2 corners[4] =
841		{
842			0.5f * quadDiagLen * Vec2( angleCos,  angleSin),
843			0.5f * quadDiagLen * Vec2(-angleSin,  angleCos),
844			0.5f * quadDiagLen * Vec2(-angleCos, -angleSin),
845			0.5f * quadDiagLen * Vec2( angleSin, -angleCos)
846		};
847
848		unicoloredRegions.reserve(8);
849
850		// Draw 8 quads.
851		// First four are rotated at angles angle+0, angle+90, angle+180 and angle+270.
852		// Last four are rotated the same angles as the first four, but the ordering of the last triangle's vertices is reversed.
853
854		for (int quadNdx = 0; quadNdx < 8; quadNdx++)
855		{
856			Vec2 center = (2.0f-quadDiagLen) * Vec2((float)(quadNdx%3), (float)(quadNdx/3)) / 2.0f - 0.5f*(2.0f-quadDiagLen);
857
858			renderTriangle(corners[(0+quadNdx) % 4] + center,
859						   corners[(1+quadNdx) % 4] + center,
860						   corners[(2+quadNdx) % 4] + center,
861						   Vec4(0.5f, 0.5f, 0.5f, 1.0f));
862
863			if (quadNdx >= 4)
864			{
865				renderTriangle(corners[(3+quadNdx) % 4] + center,
866							   corners[(2+quadNdx) % 4] + center,
867							   corners[(0+quadNdx) % 4] + center,
868							   Vec4(0.5f, 0.5f, 0.5f, 1.0f));
869			}
870			else
871			{
872				renderTriangle(corners[(0+quadNdx) % 4] + center,
873							   corners[(2+quadNdx) % 4] + center,
874							   corners[(3+quadNdx) % 4] + center,
875							   Vec4(0.5f, 0.5f, 0.5f, 1.0f));
876			}
877
878			// The size of the "interior" of a quad is assumed to be approximately unicolorRegionScale*<actual size of quad>.
879			// By "interior" we here mean the region of non-boundary pixels of the rendered quad for which we can safely assume
880			// that it has all coverage bits set to 1, for every pixel.
881			float unicolorRegionScale = 1.0f - 6.0f*2.0f / (float)m_viewportSize / quadDiagLen;
882			unicoloredRegions.push_back(QuadCorners((center + corners[0]*unicolorRegionScale),
883													(center + corners[1]*unicolorRegionScale),
884													(center + corners[2]*unicolorRegionScale),
885													(center + corners[3]*unicolorRegionScale)));
886		}
887	}
888	else if (m_caseType == CASETYPE_BIGGER_THAN_VIEWPORT_QUAD)
889	{
890		// Draw a bigger-than-viewport quad, rotated at an angle depending on m_currentIteration.
891
892		int				quadBaseAngleNdx		= m_currentIteration / 8;
893		int				quadSubAngleNdx			= m_currentIteration % 8;
894		float			angleCos;
895		float			angleSin;
896
897		if (quadBaseAngleNdx == 0)
898		{
899			angleCos = 1.0f;
900			angleSin = 0.0f;
901		}
902		else if (quadBaseAngleNdx == 1)
903		{
904			angleCos = SQRT_HALF;
905			angleSin = SQRT_HALF;
906		}
907		else
908		{
909			float angle = 0.5f * DE_PI * (float)(m_currentIteration-1) / (float)(m_numIterations-1);
910			angleCos = deFloatCos(angle);
911			angleSin = deFloatSin(angle);
912		}
913
914		float quadDiagLen = 2.5f / de::max(angleCos, angleSin);
915
916		Vec2 corners[4] =
917		{
918			0.5f * quadDiagLen * Vec2( angleCos,  angleSin),
919			0.5f * quadDiagLen * Vec2(-angleSin,  angleCos),
920			0.5f * quadDiagLen * Vec2(-angleCos, -angleSin),
921			0.5f * quadDiagLen * Vec2( angleSin, -angleCos)
922		};
923
924		renderTriangle(corners[(0+quadSubAngleNdx) % 4],
925					   corners[(1+quadSubAngleNdx) % 4],
926					   corners[(2+quadSubAngleNdx) % 4],
927					   Vec4(0.5f, 0.5f, 0.5f, 1.0f));
928
929		if (quadSubAngleNdx >= 4)
930		{
931			renderTriangle(corners[(3+quadSubAngleNdx) % 4],
932						   corners[(2+quadSubAngleNdx) % 4],
933						   corners[(0+quadSubAngleNdx) % 4],
934						   Vec4(0.5f, 0.5f, 0.5f, 1.0f));
935		}
936		else
937		{
938			renderTriangle(corners[(0+quadSubAngleNdx) % 4],
939						   corners[(2+quadSubAngleNdx) % 4],
940						   corners[(3+quadSubAngleNdx) % 4],
941						   Vec4(0.5f, 0.5f, 0.5f, 1.0f));
942		}
943
944		float unicolorRegionScale = 1.0f - 6.0f*2.0f / (float)m_viewportSize / quadDiagLen;
945		unicoloredRegions.push_back(QuadCorners((corners[0]*unicolorRegionScale),
946												(corners[1]*unicolorRegionScale),
947												(corners[2]*unicolorRegionScale),
948												(corners[3]*unicolorRegionScale)));
949	}
950	else if (m_caseType == CASETYPE_FIT_VIEWPORT_QUAD)
951	{
952		// Draw an exactly viewport-sized quad, rotated by multiples of 90 degrees angle depending on m_currentIteration.
953
954		int quadSubAngleNdx = m_currentIteration % 8;
955
956		Vec2 corners[4] =
957		{
958			Vec2( 1.0f,  1.0f),
959			Vec2(-1.0f,  1.0f),
960			Vec2(-1.0f, -1.0f),
961			Vec2( 1.0f, -1.0f)
962		};
963
964		renderTriangle(corners[(0+quadSubAngleNdx) % 4],
965					   corners[(1+quadSubAngleNdx) % 4],
966					   corners[(2+quadSubAngleNdx) % 4],
967					   Vec4(0.5f, 0.5f, 0.5f, 1.0f));
968
969		if (quadSubAngleNdx >= 4)
970		{
971			renderTriangle(corners[(3+quadSubAngleNdx) % 4],
972						   corners[(2+quadSubAngleNdx) % 4],
973						   corners[(0+quadSubAngleNdx) % 4],
974						   Vec4(0.5f, 0.5f, 0.5f, 1.0f));
975		}
976		else
977		{
978			renderTriangle(corners[(0+quadSubAngleNdx) % 4],
979						   corners[(2+quadSubAngleNdx) % 4],
980						   corners[(3+quadSubAngleNdx) % 4],
981						   Vec4(0.5f, 0.5f, 0.5f, 1.0f));
982		}
983
984		unicoloredRegions.push_back(QuadCorners(corners[0], corners[1], corners[2], corners[3]));
985	}
986	else
987		DE_ASSERT(false);
988
989	// Read pixels and check unicolored regions.
990
991	readImage(renderedImg);
992
993	tcu::clear(errorImg.getAccess(), Vec4(0.0f, 1.0f, 0.0f, 1.0f));
994
995	log << TestLog::Image("RenderedImage", "Rendered image", renderedImg, QP_IMAGE_COMPRESSION_MODE_PNG);
996
997	bool errorsDetected = false;
998	for (int i = 0; i < (int)unicoloredRegions.size(); i++)
999	{
1000		const QuadCorners&	region					= unicoloredRegions[i];
1001		IVec2				p0Win					= ((region.p0+1.0f) * 0.5f * (float)(m_viewportSize-1) + 0.5f).asInt();
1002		IVec2				p1Win					= ((region.p1+1.0f) * 0.5f * (float)(m_viewportSize-1) + 0.5f).asInt();
1003		IVec2				p2Win					= ((region.p2+1.0f) * 0.5f * (float)(m_viewportSize-1) + 0.5f).asInt();
1004		IVec2				p3Win					= ((region.p3+1.0f) * 0.5f * (float)(m_viewportSize-1) + 0.5f).asInt();
1005		bool				errorsInCurrentRegion	= !isPixelRegionUnicolored(renderedImg, p0Win, p1Win, p2Win, p3Win);
1006
1007		if (errorsInCurrentRegion)
1008			drawUnicolorTestErrors(renderedImg, errorImg.getAccess(), p0Win, p1Win, p2Win, p3Win);
1009
1010		errorsDetected = errorsDetected || errorsInCurrentRegion;
1011	}
1012
1013	m_currentIteration++;
1014
1015	if (errorsDetected)
1016	{
1017		log << TestLog::Message << "Failure: Not all quad interiors seem unicolored - common-edge artifacts?" << TestLog::EndMessage;
1018		log << TestLog::Message << "Erroneous pixels are drawn red in the following image" << TestLog::EndMessage;
1019		log << TestLog::Image("RenderedImageWithErrors",	"Rendered image with errors marked",	renderedImg,	QP_IMAGE_COMPRESSION_MODE_PNG);
1020		log << TestLog::Image("ErrorsOnly",					"Image with error pixels only",			errorImg,		QP_IMAGE_COMPRESSION_MODE_PNG);
1021		m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed");
1022		return STOP;
1023	}
1024	else if (m_currentIteration < m_numIterations)
1025	{
1026		log << TestLog::Message << "Quads seem OK - moving on to next pattern" << TestLog::EndMessage;
1027		return CONTINUE;
1028	}
1029	else
1030	{
1031		log << TestLog::Message << "Success: All quad interiors seem unicolored (no common-edge artifacts)" << TestLog::EndMessage;
1032		m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Passed");
1033		return STOP;
1034	}
1035}
1036
1037/*--------------------------------------------------------------------*//*!
1038 * \brief Test that depth values are per-sample.
1039 *
1040 * Draws intersecting, differently-colored polygons and checks that there
1041 * are at least sample count+1 distinct colors present, due to some of the
1042 * samples at the intersection line belonging to one and some to another
1043 * polygon.
1044 *//*--------------------------------------------------------------------*/
1045class SampleDepthCase : public NumSamplesCase
1046{
1047public:
1048						SampleDepthCase			(Context& context, const char* name, const char* description, int numFboSamples = 0);
1049						~SampleDepthCase		(void) {}
1050
1051	void				init					(void);
1052
1053protected:
1054	void				renderPattern			(void) const;
1055};
1056
1057SampleDepthCase::SampleDepthCase (Context& context, const char* name, const char* description, int numFboSamples)
1058	: NumSamplesCase (context, name, description, numFboSamples >= 0 ? FboParams(numFboSamples, true, false) : FboParams())
1059{
1060}
1061
1062void SampleDepthCase::init (void)
1063{
1064	TestLog& log = m_testCtx.getLog();
1065
1066	if (m_context.getRenderTarget().getDepthBits() == 0)
1067		TCU_THROW(NotSupportedError, "Test requires depth buffer");
1068
1069	MultisampleCase::init();
1070
1071	GLU_CHECK_CALL(glEnable(GL_DEPTH_TEST));
1072	GLU_CHECK_CALL(glDepthFunc(GL_LESS));
1073
1074	log << TestLog::Message << "Depth test enabled, depth func is GL_LESS" << TestLog::EndMessage;
1075	log << TestLog::Message << "Drawing several bigger-than-viewport black or white polygons intersecting each other" << TestLog::EndMessage;
1076}
1077
1078void SampleDepthCase::renderPattern (void) const
1079{
1080	GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 0.0f));
1081	GLU_CHECK_CALL(glClearDepthf(1.0f));
1082	GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
1083
1084	{
1085		const int numPolygons = 50;
1086
1087		for (int i = 0; i < numPolygons; i++)
1088		{
1089			Vec4	color	= i % 2 == 0 ? Vec4(1.0f, 1.0f, 1.0f, 1.0f) : Vec4(0.0f, 0.0f, 0.0f, 1.0f);
1090			float	angle	= 2.0f * DE_PI * (float)i / (float)numPolygons + 0.001f*(float)m_currentIteration;
1091			Vec3	pt0		(3.0f*deFloatCos(angle + 2.0f*DE_PI*0.0f/3.0f), 3.0f*deFloatSin(angle + 2.0f*DE_PI*0.0f/3.0f), 1.0f);
1092			Vec3	pt1		(3.0f*deFloatCos(angle + 2.0f*DE_PI*1.0f/3.0f), 3.0f*deFloatSin(angle + 2.0f*DE_PI*1.0f/3.0f), 0.0f);
1093			Vec3	pt2		(3.0f*deFloatCos(angle + 2.0f*DE_PI*2.0f/3.0f), 3.0f*deFloatSin(angle + 2.0f*DE_PI*2.0f/3.0f), 0.0f);
1094
1095			renderTriangle(pt0, pt1, pt2, color);
1096		}
1097	}
1098}
1099
1100/*--------------------------------------------------------------------*//*!
1101 * \brief Test that stencil buffer values are per-sample.
1102 *
1103 * Draws a unicolored pattern and marks drawn samples in stencil buffer;
1104 * then clears and draws a viewport-size quad with that color and with
1105 * proper stencil test such that the resulting image should be exactly the
1106 * same as after the pattern was first drawn.
1107 *//*--------------------------------------------------------------------*/
1108class SampleStencilCase : public MultisampleCase
1109{
1110public:
1111						SampleStencilCase		(Context& context, const char* name, const char* description, int numFboSamples = 0);
1112						~SampleStencilCase		(void) {}
1113
1114	void				init					(void);
1115	IterateResult		iterate					(void);
1116};
1117
1118SampleStencilCase::SampleStencilCase (Context& context, const char* name, const char* description, int numFboSamples)
1119	: MultisampleCase (context, name, description, 256, numFboSamples >= 0 ? FboParams(numFboSamples, false, true) : FboParams())
1120{
1121}
1122
1123void SampleStencilCase::init (void)
1124{
1125	if (m_context.getRenderTarget().getStencilBits() == 0)
1126		TCU_THROW(NotSupportedError, "Test requires stencil buffer");
1127
1128	MultisampleCase::init();
1129}
1130
1131SampleStencilCase::IterateResult SampleStencilCase::iterate (void)
1132{
1133	TestLog&		log					= m_testCtx.getLog();
1134	tcu::Surface	renderedImgFirst	(m_viewportSize, m_viewportSize);
1135	tcu::Surface	renderedImgSecond	(m_viewportSize, m_viewportSize);
1136
1137	randomizeViewport();
1138
1139	GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
1140	GLU_CHECK_CALL(glClearStencil(0));
1141	GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT));
1142	GLU_CHECK_CALL(glEnable(GL_STENCIL_TEST));
1143	GLU_CHECK_CALL(glStencilFunc(GL_ALWAYS, 1, 1));
1144	GLU_CHECK_CALL(glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE));
1145
1146	log << TestLog::Message << "Drawing a pattern with glStencilFunc(GL_ALWAYS, 1, 1) and glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)" << TestLog::EndMessage;
1147
1148	{
1149		const int numTriangles = 25;
1150		for (int i = 0; i < numTriangles; i++)
1151		{
1152			float angle0 = 2.0f*DE_PI * (float)i			/ (float)numTriangles;
1153			float angle1 = 2.0f*DE_PI * ((float)i + 0.5f)	/ (float)numTriangles;
1154
1155			renderTriangle(Vec2(0.0f, 0.0f),
1156						   Vec2(deFloatCos(angle0)*0.95f, deFloatSin(angle0)*0.95f),
1157						   Vec2(deFloatCos(angle1)*0.95f, deFloatSin(angle1)*0.95f),
1158						   Vec4(1.0f));
1159		}
1160	}
1161
1162	readImage(renderedImgFirst);
1163	log << TestLog::Image("RenderedImgFirst", "First image rendered", renderedImgFirst, QP_IMAGE_COMPRESSION_MODE_PNG);
1164
1165	log << TestLog::Message << "Clearing color buffer to black" << TestLog::EndMessage;
1166
1167	GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT));
1168	GLU_CHECK_CALL(glStencilFunc(GL_EQUAL, 1, 1));
1169	GLU_CHECK_CALL(glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP));
1170
1171	{
1172		log << TestLog::Message << "Checking that color buffer was actually cleared to black" << TestLog::EndMessage;
1173
1174		tcu::Surface clearedImg(m_viewportSize, m_viewportSize);
1175		readImage(clearedImg);
1176
1177		for (int y = 0; y < clearedImg.getHeight(); y++)
1178		for (int x = 0; x < clearedImg.getWidth(); x++)
1179		{
1180			const tcu::RGBA& clr = clearedImg.getPixel(x, y);
1181			if (clr != tcu::RGBA::black())
1182			{
1183				log << TestLog::Message << "Failure: first non-black pixel, color " << clr << ", detected at coordinates (" << x << ", " << y << ")" << TestLog::EndMessage;
1184				log << TestLog::Image("ClearedImg", "Image after clearing, erroneously non-black", clearedImg);
1185				m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed");
1186				return STOP;
1187			}
1188		}
1189	}
1190
1191	log << TestLog::Message << "Drawing a viewport-sized quad with glStencilFunc(GL_EQUAL, 1, 1) and glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP) - should result in same image as the first" << TestLog::EndMessage;
1192
1193	renderQuad(Vec2(-1.0f, -1.0f),
1194			   Vec2( 1.0f, -1.0f),
1195			   Vec2(-1.0f,  1.0f),
1196			   Vec2( 1.0f,  1.0f),
1197			   Vec4(1.0f));
1198
1199	readImage(renderedImgSecond);
1200	log << TestLog::Image("RenderedImgSecond", "Second image rendered", renderedImgSecond, QP_IMAGE_COMPRESSION_MODE_PNG);
1201
1202	bool passed = tcu::pixelThresholdCompare(log,
1203											 "ImageCompare",
1204											 "Image comparison",
1205											 renderedImgFirst,
1206											 renderedImgSecond,
1207											 tcu::RGBA(0),
1208											 tcu::COMPARE_LOG_ON_ERROR);
1209
1210	if (passed)
1211		log << TestLog::Message << "Success: The two images rendered are identical" << TestLog::EndMessage;
1212
1213	m_context.getTestContext().setTestResult(passed ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
1214											 passed ? "Passed"				: "Failed");
1215
1216	return STOP;
1217}
1218
1219/*--------------------------------------------------------------------*//*!
1220 * \brief Tests coverage mask generation proportionality property.
1221 *
1222 * Tests that the number of coverage bits in a coverage mask created by
1223 * GL_SAMPLE_ALPHA_TO_COVERAGE or GL_SAMPLE_COVERAGE is, on average,
1224 * proportional to the alpha or coverage value, respectively. Draws
1225 * multiple frames, each time increasing the alpha or coverage value used,
1226 * and checks that the average color is changing appropriately.
1227 *//*--------------------------------------------------------------------*/
1228class MaskProportionalityCase : public MultisampleCase
1229{
1230public:
1231	enum CaseType
1232	{
1233		CASETYPE_ALPHA_TO_COVERAGE = 0,
1234		CASETYPE_SAMPLE_COVERAGE,
1235		CASETYPE_SAMPLE_COVERAGE_INVERTED,
1236
1237		CASETYPE_LAST
1238	};
1239
1240					MaskProportionalityCase				(Context& context, const char* name, const char* description, CaseType type, int numFboSamples = 0);
1241					~MaskProportionalityCase			(void) {}
1242
1243	void			init								(void);
1244
1245	IterateResult	iterate								(void);
1246
1247private:
1248	const CaseType	m_type;
1249
1250	int				m_numIterations;
1251	int				m_currentIteration;
1252
1253	deInt32			m_previousIterationColorSum;
1254};
1255
1256MaskProportionalityCase::MaskProportionalityCase (Context& context, const char* name, const char* description, CaseType type, int numFboSamples)
1257	: MultisampleCase				(context, name, description, 32, numFboSamples >= 0 ? FboParams(numFboSamples, false, false) : FboParams())
1258	, m_type						(type)
1259	, m_currentIteration			(0)
1260	, m_previousIterationColorSum	(-1)
1261{
1262}
1263
1264void MaskProportionalityCase::init (void)
1265{
1266	TestLog& log = m_testCtx.getLog();
1267
1268	MultisampleCase::init();
1269
1270	if (m_type == CASETYPE_ALPHA_TO_COVERAGE)
1271	{
1272		GLU_CHECK_CALL(glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE));
1273		log << TestLog::Message << "GL_SAMPLE_ALPHA_TO_COVERAGE is enabled" << TestLog::EndMessage;
1274	}
1275	else
1276	{
1277		DE_ASSERT(m_type == CASETYPE_SAMPLE_COVERAGE || m_type == CASETYPE_SAMPLE_COVERAGE_INVERTED);
1278
1279		GLU_CHECK_CALL(glEnable(GL_SAMPLE_COVERAGE));
1280		log << TestLog::Message << "GL_SAMPLE_COVERAGE is enabled" << TestLog::EndMessage;
1281	}
1282
1283	m_numIterations = de::max(2, getIterationCount(m_testCtx, m_numSamples * 5));
1284
1285	randomizeViewport(); // \note Using the same viewport for every iteration since coverage mask may depend on window-relative pixel coordinate.
1286}
1287
1288MaskProportionalityCase::IterateResult MaskProportionalityCase::iterate (void)
1289{
1290	TestLog&		log				= m_testCtx.getLog();
1291	tcu::Surface	renderedImg		(m_viewportSize, m_viewportSize);
1292	deInt32			numPixels		= (deInt32)renderedImg.getWidth()*(deInt32)renderedImg.getHeight();
1293
1294	log << TestLog::Message << "Clearing color to black" << TestLog::EndMessage;
1295	GLU_CHECK_CALL(glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
1296	GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
1297	GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT));
1298
1299	if (m_type == CASETYPE_ALPHA_TO_COVERAGE)
1300	{
1301		GLU_CHECK_CALL(glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE));
1302		log << TestLog::Message << "Using color mask TRUE, TRUE, TRUE, FALSE" << TestLog::EndMessage;
1303	}
1304
1305	// Draw quad.
1306
1307	{
1308		const Vec2		pt0						(-1.0f, -1.0f);
1309		const Vec2		pt1						( 1.0f, -1.0f);
1310		const Vec2		pt2						(-1.0f,  1.0f);
1311		const Vec2		pt3						( 1.0f,  1.0f);
1312		Vec4			quadColor				(1.0f, 0.0f, 0.0f, 1.0f);
1313		float			alphaOrCoverageValue	= (float)m_currentIteration / (float)(m_numIterations-1);
1314
1315		if (m_type == CASETYPE_ALPHA_TO_COVERAGE)
1316		{
1317			log << TestLog::Message << "Drawing a red quad using alpha value " + de::floatToString(alphaOrCoverageValue, 2) << TestLog::EndMessage;
1318			quadColor.w() = alphaOrCoverageValue;
1319		}
1320		else
1321		{
1322			DE_ASSERT(m_type == CASETYPE_SAMPLE_COVERAGE || m_type == CASETYPE_SAMPLE_COVERAGE_INVERTED);
1323
1324			bool	isInverted		= m_type == CASETYPE_SAMPLE_COVERAGE_INVERTED;
1325			float	coverageValue	= isInverted ? 1.0f - alphaOrCoverageValue : alphaOrCoverageValue;
1326			log << TestLog::Message << "Drawing a red quad using sample coverage value " + de::floatToString(coverageValue, 2) << (isInverted ? " (inverted)" : "") << TestLog::EndMessage;
1327			GLU_CHECK_CALL(glSampleCoverage(coverageValue, isInverted ? GL_TRUE : GL_FALSE));
1328		}
1329
1330		renderQuad(pt0, pt1, pt2, pt3, quadColor);
1331	}
1332
1333	// Read ang log image.
1334
1335	readImage(renderedImg);
1336
1337	log << TestLog::Image("RenderedImage", "Rendered image", renderedImg, QP_IMAGE_COMPRESSION_MODE_PNG);
1338
1339	// Compute average red component in rendered image.
1340
1341	deInt32 sumRed = 0;
1342
1343	for (int y = 0; y < renderedImg.getHeight(); y++)
1344	for (int x = 0; x < renderedImg.getWidth(); x++)
1345		sumRed += renderedImg.getPixel(x, y).getRed();
1346
1347	log << TestLog::Message << "Average red color component: " << de::floatToString((float)sumRed / 255.0f / (float)numPixels, 2) << TestLog::EndMessage;
1348
1349	// Check if average color has decreased from previous frame's color.
1350
1351	if (sumRed < m_previousIterationColorSum)
1352	{
1353		log << TestLog::Message << "Failure: Current average red color component is lower than previous" << TestLog::EndMessage;
1354		m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed");
1355		return STOP;
1356	}
1357
1358	// Check if coverage mask is not all-zeros if alpha or coverage value is 0 (or 1, if inverted).
1359
1360	if (m_currentIteration == 0 && sumRed != 0)
1361	{
1362		log << TestLog::Message << "Failure: Image should be completely black" << TestLog::EndMessage;
1363		m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed");
1364		return STOP;
1365	}
1366
1367	if (m_currentIteration == m_numIterations-1 && sumRed != 0xff*numPixels)
1368	{
1369		log << TestLog::Message << "Failure: Image should be completely red" << TestLog::EndMessage;
1370
1371		m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed");
1372		return STOP;
1373	}
1374
1375	m_previousIterationColorSum = sumRed;
1376
1377	m_currentIteration++;
1378
1379	if (m_currentIteration >= m_numIterations)
1380	{
1381		log << TestLog::Message
1382			<< "Success: Number of coverage mask bits set appears to be, on average, proportional to "
1383			<< (m_type == CASETYPE_ALPHA_TO_COVERAGE ? "alpha" : m_type == CASETYPE_SAMPLE_COVERAGE ? "sample coverage value" : "inverted sample coverage value")
1384			<< TestLog::EndMessage;
1385
1386		m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Passed");
1387		return STOP;
1388	}
1389	else
1390		return CONTINUE;
1391}
1392
1393/*--------------------------------------------------------------------*//*!
1394 * \brief Tests coverage mask generation constancy property.
1395 *
1396 * Tests that the coverage mask created by GL_SAMPLE_ALPHA_TO_COVERAGE or
1397 * GL_SAMPLE_COVERAGE is constant at given pixel coordinates, with a given
1398 * alpha component or coverage value, respectively. Draws two quads, with
1399 * the second one fully overlapping the first one such that at any given
1400 * pixel, both quads have the same alpha or coverage value. This way, if
1401 * the constancy property is fulfilled, only the second quad should be
1402 * visible.
1403 *//*--------------------------------------------------------------------*/
1404class MaskConstancyCase : public MultisampleCase
1405{
1406public:
1407	enum CaseType
1408	{
1409		CASETYPE_ALPHA_TO_COVERAGE = 0,		//!< Use only alpha-to-coverage.
1410		CASETYPE_SAMPLE_COVERAGE,			//!< Use only sample coverage.
1411		CASETYPE_SAMPLE_COVERAGE_INVERTED,	//!< Use only inverted sample coverage.
1412		CASETYPE_BOTH,						//!< Use both alpha-to-coverage and sample coverage.
1413		CASETYPE_BOTH_INVERTED,				//!< Use both alpha-to-coverage and inverted sample coverage.
1414
1415		CASETYPE_LAST
1416	};
1417
1418					MaskConstancyCase			(Context& context, const char* name, const char* description, CaseType type, int numFboSamples = 0);
1419					~MaskConstancyCase			(void) {}
1420
1421	IterateResult	iterate						(void);
1422
1423private:
1424	const bool		m_isAlphaToCoverageCase;
1425	const bool		m_isSampleCoverageCase;
1426	const bool		m_isInvertedSampleCoverageCase;
1427};
1428
1429MaskConstancyCase::MaskConstancyCase (Context& context, const char* name, const char* description, CaseType type, int numFboSamples)
1430	: MultisampleCase					(context, name, description, 256, numFboSamples >= 0 ? FboParams(numFboSamples, false, false) : FboParams())
1431	, m_isAlphaToCoverageCase			(type == CASETYPE_ALPHA_TO_COVERAGE			|| type == CASETYPE_BOTH						|| type == CASETYPE_BOTH_INVERTED)
1432	, m_isSampleCoverageCase			(type == CASETYPE_SAMPLE_COVERAGE			|| type == CASETYPE_SAMPLE_COVERAGE_INVERTED	|| type == CASETYPE_BOTH			|| type == CASETYPE_BOTH_INVERTED)
1433	, m_isInvertedSampleCoverageCase	(type == CASETYPE_SAMPLE_COVERAGE_INVERTED	|| type == CASETYPE_BOTH_INVERTED)
1434{
1435}
1436
1437MaskConstancyCase::IterateResult MaskConstancyCase::iterate (void)
1438{
1439	TestLog&		log				= m_testCtx.getLog();
1440	tcu::Surface	renderedImg		(m_viewportSize, m_viewportSize);
1441
1442	randomizeViewport();
1443
1444	log << TestLog::Message << "Clearing color to black" << TestLog::EndMessage;
1445	GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
1446	GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT));
1447
1448	if (m_isAlphaToCoverageCase)
1449	{
1450		GLU_CHECK_CALL(glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE));
1451		GLU_CHECK_CALL(glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE));
1452		log << TestLog::Message << "GL_SAMPLE_ALPHA_TO_COVERAGE is enabled" << TestLog::EndMessage;
1453		log << TestLog::Message << "Color mask is TRUE, TRUE, TRUE, FALSE" << TestLog::EndMessage;
1454	}
1455
1456	if (m_isSampleCoverageCase)
1457	{
1458		GLU_CHECK_CALL(glEnable(GL_SAMPLE_COVERAGE));
1459		log << TestLog::Message << "GL_SAMPLE_COVERAGE is enabled" << TestLog::EndMessage;
1460	}
1461
1462	log << TestLog::Message
1463		<< "Drawing several green quads, each fully overlapped by a red quad with the same "
1464		<< (m_isAlphaToCoverageCase ? "alpha" : "")
1465		<< (m_isAlphaToCoverageCase && m_isSampleCoverageCase ? " and " : "")
1466		<< (m_isInvertedSampleCoverageCase ? "inverted " : "")
1467		<< (m_isSampleCoverageCase ? "sample coverage" : "")
1468		<< " values"
1469		<< TestLog::EndMessage;
1470
1471	const int numQuadRowsCols = m_numSamples*4;
1472
1473	for (int row = 0; row < numQuadRowsCols; row++)
1474	{
1475		for (int col = 0; col < numQuadRowsCols; col++)
1476		{
1477			float		x0			= (float)(col+0) / (float)numQuadRowsCols * 2.0f - 1.0f;
1478			float		x1			= (float)(col+1) / (float)numQuadRowsCols * 2.0f - 1.0f;
1479			float		y0			= (float)(row+0) / (float)numQuadRowsCols * 2.0f - 1.0f;
1480			float		y1			= (float)(row+1) / (float)numQuadRowsCols * 2.0f - 1.0f;
1481			const Vec4	baseGreen	(0.0f, 1.0f, 0.0f, 0.0f);
1482			const Vec4	baseRed		(1.0f, 0.0f, 0.0f, 0.0f);
1483			Vec4		alpha0		(0.0f, 0.0f, 0.0f, m_isAlphaToCoverageCase ? (float)col / (float)(numQuadRowsCols-1) : 1.0f);
1484			Vec4		alpha1		(0.0f, 0.0f, 0.0f, m_isAlphaToCoverageCase ? (float)row / (float)(numQuadRowsCols-1) : 1.0f);
1485
1486			if (m_isSampleCoverageCase)
1487			{
1488				float value = (float)(row*numQuadRowsCols + col) / (float)(numQuadRowsCols*numQuadRowsCols-1);
1489				GLU_CHECK_CALL(glSampleCoverage(m_isInvertedSampleCoverageCase ? 1.0f - value : value, m_isInvertedSampleCoverageCase ? GL_TRUE : GL_FALSE));
1490			}
1491
1492			renderQuad(Vec2(x0, y0), Vec2(x1, y0), Vec2(x0, y1), Vec2(x1, y1), baseGreen + alpha0,	baseGreen + alpha1,	baseGreen + alpha0,	baseGreen + alpha1);
1493			renderQuad(Vec2(x0, y0), Vec2(x1, y0), Vec2(x0, y1), Vec2(x1, y1), baseRed + alpha0,	baseRed + alpha1,	baseRed + alpha0,	baseRed + alpha1);
1494		}
1495	}
1496
1497	readImage(renderedImg);
1498
1499	log << TestLog::Image("RenderedImage", "Rendered image", renderedImg, QP_IMAGE_COMPRESSION_MODE_PNG);
1500
1501	for (int y = 0; y < renderedImg.getHeight(); y++)
1502	for (int x = 0; x < renderedImg.getWidth(); x++)
1503	{
1504		if (renderedImg.getPixel(x, y).getGreen() > 0)
1505		{
1506			log << TestLog::Message << "Failure: Non-zero green color component detected - should have been completely overwritten by red quad" << TestLog::EndMessage;
1507			m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed");
1508			return STOP;
1509		}
1510	}
1511
1512	log << TestLog::Message
1513		<< "Success: Coverage mask appears to be constant at a given pixel coordinate with a given "
1514		<< (m_isAlphaToCoverageCase ? "alpha" : "")
1515		<< (m_isAlphaToCoverageCase && m_isSampleCoverageCase ? " and " : "")
1516		<< (m_isSampleCoverageCase ? "coverage value" : "")
1517		<< TestLog::EndMessage;
1518
1519	m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Passed");
1520
1521	return STOP;
1522}
1523
1524/*--------------------------------------------------------------------*//*!
1525 * \brief Tests coverage mask inversion validity.
1526 *
1527 * Tests that the coverage masks obtained by glSampleCoverage(..., GL_TRUE)
1528 * and glSampleCoverage(..., GL_FALSE) are indeed each others' inverses.
1529 * This is done by drawing a pattern, with varying coverage values,
1530 * overlapped by a pattern that has inverted masks and is otherwise
1531 * identical. The resulting image is compared to one obtained by drawing
1532 * the same pattern but with all-ones coverage masks.
1533 *//*--------------------------------------------------------------------*/
1534class CoverageMaskInvertCase : public MultisampleCase
1535{
1536public:
1537					CoverageMaskInvertCase		(Context& context, const char* name, const char* description, int numFboSamples = 0);
1538					~CoverageMaskInvertCase		(void) {}
1539
1540	IterateResult	iterate						(void);
1541
1542private:
1543	void			drawPattern					(bool invertSampleCoverage) const;
1544};
1545
1546CoverageMaskInvertCase::CoverageMaskInvertCase (Context& context, const char* name, const char* description, int numFboSamples)
1547	: MultisampleCase (context, name, description, 256, numFboSamples >= 0 ? FboParams(numFboSamples, false, false) : FboParams())
1548{
1549}
1550
1551void CoverageMaskInvertCase::drawPattern (bool invertSampleCoverage) const
1552{
1553	const int numTriangles = 25;
1554	for (int i = 0; i < numTriangles; i++)
1555	{
1556		GLU_CHECK_CALL(glSampleCoverage((float)i / (float)(numTriangles-1), invertSampleCoverage ? GL_TRUE : GL_FALSE));
1557
1558		float angle0 = 2.0f*DE_PI * (float)i			/ (float)numTriangles;
1559		float angle1 = 2.0f*DE_PI * ((float)i + 0.5f)	/ (float)numTriangles;
1560
1561		renderTriangle(Vec2(0.0f, 0.0f),
1562					   Vec2(deFloatCos(angle0)*0.95f, deFloatSin(angle0)*0.95f),
1563					   Vec2(deFloatCos(angle1)*0.95f, deFloatSin(angle1)*0.95f),
1564					   Vec4(0.4f + (float)i/(float)numTriangles*0.6f,
1565							0.5f + (float)i/(float)numTriangles*0.3f,
1566							0.6f - (float)i/(float)numTriangles*0.5f,
1567							0.7f - (float)i/(float)numTriangles*0.7f));
1568	}
1569}
1570
1571CoverageMaskInvertCase::IterateResult CoverageMaskInvertCase::iterate (void)
1572{
1573	TestLog&		log								= m_testCtx.getLog();
1574	tcu::Surface	renderedImgNoSampleCoverage		(m_viewportSize, m_viewportSize);
1575	tcu::Surface	renderedImgSampleCoverage		(m_viewportSize, m_viewportSize);
1576
1577	randomizeViewport();
1578
1579	GLU_CHECK_CALL(glEnable(GL_BLEND));
1580	GLU_CHECK_CALL(glBlendEquation(GL_FUNC_ADD));
1581	GLU_CHECK_CALL(glBlendFunc(GL_ONE, GL_ONE));
1582	log << TestLog::Message << "Additive blending enabled in order to detect (erroneously) overlapping samples" << TestLog::EndMessage;
1583
1584	log << TestLog::Message << "Clearing color to all-zeros" << TestLog::EndMessage;
1585	GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 0.0f));
1586	GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT));
1587	log << TestLog::Message << "Drawing the pattern with GL_SAMPLE_COVERAGE disabled" << TestLog::EndMessage;
1588	drawPattern(false);
1589	readImage(renderedImgNoSampleCoverage);
1590
1591	log << TestLog::Image("RenderedImageNoSampleCoverage", "Rendered image with GL_SAMPLE_COVERAGE disabled", renderedImgNoSampleCoverage, QP_IMAGE_COMPRESSION_MODE_PNG);
1592
1593	log << TestLog::Message << "Clearing color to all-zeros" << TestLog::EndMessage;
1594	GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT));
1595	GLU_CHECK_CALL(glEnable(GL_SAMPLE_COVERAGE));
1596	log << TestLog::Message << "Drawing the pattern with GL_SAMPLE_COVERAGE enabled, using non-inverted masks" << TestLog::EndMessage;
1597	drawPattern(false);
1598	log << TestLog::Message << "Drawing the pattern with GL_SAMPLE_COVERAGE enabled, using same sample coverage values but inverted masks" << TestLog::EndMessage;
1599	drawPattern(true);
1600	readImage(renderedImgSampleCoverage);
1601
1602	log << TestLog::Image("RenderedImageSampleCoverage", "Rendered image with GL_SAMPLE_COVERAGE enabled", renderedImgSampleCoverage, QP_IMAGE_COMPRESSION_MODE_PNG);
1603
1604	bool passed = tcu::pixelThresholdCompare(log,
1605											 "CoverageVsNoCoverage",
1606											 "Comparison of same pattern with GL_SAMPLE_COVERAGE disabled and enabled",
1607											 renderedImgNoSampleCoverage,
1608											 renderedImgSampleCoverage,
1609											 tcu::RGBA(0),
1610											 tcu::COMPARE_LOG_ON_ERROR);
1611
1612	if (passed)
1613		log << TestLog::Message << "Success: The two images rendered are identical" << TestLog::EndMessage;
1614
1615	m_context.getTestContext().setTestResult(passed ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
1616											 passed ? "Passed"				: "Failed");
1617
1618	return STOP;
1619}
1620
1621MultisampleTests::MultisampleTests (Context& context)
1622	: TestCaseGroup(context, "multisample", "Multisampling tests")
1623{
1624}
1625
1626MultisampleTests::~MultisampleTests (void)
1627{
1628}
1629
1630void MultisampleTests::init (void)
1631{
1632	enum CaseType
1633	{
1634		CASETYPE_DEFAULT_FRAMEBUFFER = 0,
1635		CASETYPE_FBO_4_SAMPLES,
1636		CASETYPE_FBO_8_SAMPLES,
1637		CASETYPE_FBO_MAX_SAMPLES,
1638
1639		CASETYPE_LAST
1640	};
1641
1642	for (int caseTypeI = 0; caseTypeI < (int)CASETYPE_LAST; caseTypeI++)
1643	{
1644		CaseType		caseType		= (CaseType)caseTypeI;
1645		int				numFboSamples	= caseType == CASETYPE_DEFAULT_FRAMEBUFFER	? -1
1646										: caseType == CASETYPE_FBO_4_SAMPLES		? 4
1647										: caseType == CASETYPE_FBO_8_SAMPLES		? 8
1648										: caseType == CASETYPE_FBO_MAX_SAMPLES		? 0
1649										: -2;
1650
1651		TestCaseGroup*	group			= new TestCaseGroup(m_context,
1652															caseType == CASETYPE_DEFAULT_FRAMEBUFFER	? "default_framebuffer" :
1653															caseType == CASETYPE_FBO_4_SAMPLES			? "fbo_4_samples" :
1654															caseType == CASETYPE_FBO_8_SAMPLES			? "fbo_8_samples" :
1655															caseType == CASETYPE_FBO_MAX_SAMPLES		? "fbo_max_samples" :
1656															DE_NULL,
1657															caseType == CASETYPE_DEFAULT_FRAMEBUFFER	? "Render into default framebuffer" :
1658															caseType == CASETYPE_FBO_4_SAMPLES			? "Render into a framebuffer object with 4 samples" :
1659															caseType == CASETYPE_FBO_8_SAMPLES			? "Render into a framebuffer object with 8 samples" :
1660															caseType == CASETYPE_FBO_MAX_SAMPLES		? "Render into a framebuffer object with the maximum number of samples" :
1661															DE_NULL);
1662		DE_ASSERT(group->getName() != DE_NULL);
1663		DE_ASSERT(group->getDescription() != DE_NULL);
1664		DE_ASSERT(numFboSamples >= -1);
1665		addChild(group);
1666
1667		group->addChild(new PolygonNumSamplesCase		(m_context, "num_samples_polygon",			"Test sanity of the sample count, with polygons",										numFboSamples));
1668		group->addChild(new LineNumSamplesCase			(m_context, "num_samples_line",				"Test sanity of the sample count, with lines",											numFboSamples));
1669		group->addChild(new CommonEdgeCase				(m_context, "common_edge_small_quads",		"Test polygons' common edges with small quads",											CommonEdgeCase::CASETYPE_SMALL_QUADS,				numFboSamples));
1670		group->addChild(new CommonEdgeCase				(m_context, "common_edge_big_quad",			"Test polygons' common edges with bigger-than-viewport quads",							CommonEdgeCase::CASETYPE_BIGGER_THAN_VIEWPORT_QUAD,	numFboSamples));
1671		group->addChild(new CommonEdgeCase				(m_context, "common_edge_viewport_quad",	"Test polygons' common edges with exactly viewport-sized quads",						CommonEdgeCase::CASETYPE_FIT_VIEWPORT_QUAD,			numFboSamples));
1672		group->addChild(new SampleDepthCase				(m_context, "depth",						"Test that depth values are per-sample",												numFboSamples));
1673		group->addChild(new SampleStencilCase			(m_context, "stencil",						"Test that stencil values are per-sample",												numFboSamples));
1674		group->addChild(new CoverageMaskInvertCase		(m_context, "sample_coverage_invert",		"Test that non-inverted and inverted sample coverage masks are each other's negations",	numFboSamples));
1675
1676		group->addChild(new MaskProportionalityCase		(m_context, "proportionality_alpha_to_coverage",
1677																	"Test the proportionality property of GL_SAMPLE_ALPHA_TO_COVERAGE",
1678																	MaskProportionalityCase::CASETYPE_ALPHA_TO_COVERAGE, numFboSamples));
1679		group->addChild(new MaskProportionalityCase		(m_context, "proportionality_sample_coverage",
1680																	"Test the proportionality property of GL_SAMPLE_COVERAGE",
1681																	MaskProportionalityCase::CASETYPE_SAMPLE_COVERAGE, numFboSamples));
1682		group->addChild(new MaskProportionalityCase		(m_context, "proportionality_sample_coverage_inverted",
1683																	"Test the proportionality property of inverted-mask GL_SAMPLE_COVERAGE",
1684																	MaskProportionalityCase::CASETYPE_SAMPLE_COVERAGE_INVERTED, numFboSamples));
1685
1686		group->addChild(new MaskConstancyCase			(m_context, "constancy_alpha_to_coverage",
1687																	"Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_ALPHA_TO_COVERAGE",
1688																	MaskConstancyCase::CASETYPE_ALPHA_TO_COVERAGE, numFboSamples));
1689		group->addChild(new MaskConstancyCase			(m_context, "constancy_sample_coverage",
1690																	"Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_COVERAGE",
1691																	MaskConstancyCase::CASETYPE_SAMPLE_COVERAGE, numFboSamples));
1692		group->addChild(new MaskConstancyCase			(m_context, "constancy_sample_coverage_inverted",
1693																	"Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using inverted-mask GL_SAMPLE_COVERAGE",
1694																	MaskConstancyCase::CASETYPE_SAMPLE_COVERAGE_INVERTED, numFboSamples));
1695		group->addChild(new MaskConstancyCase			(m_context, "constancy_both",
1696																	"Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_ALPHA_TO_COVERAGE and GL_SAMPLE_COVERAGE",
1697																	MaskConstancyCase::CASETYPE_BOTH, numFboSamples));
1698		group->addChild(new MaskConstancyCase			(m_context, "constancy_both_inverted",
1699																	"Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_ALPHA_TO_COVERAGE and inverted-mask GL_SAMPLE_COVERAGE",
1700																	MaskConstancyCase::CASETYPE_BOTH_INVERTED, numFboSamples));
1701	}
1702}
1703
1704} // Functional
1705} // gles3
1706} // deqp
1707