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