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 
44 namespace deqp
45 {
46 namespace gles3
47 {
48 namespace Functional
49 {
50 
51 using tcu::Vec2;
52 using tcu::Vec3;
53 using tcu::Vec4;
54 using tcu::IVec2;
55 using tcu::IVec4;
56 using tcu::TestLog;
57 using std::vector;
58 
59 static const GLenum		FBO_COLOR_FORMAT	= GL_RGBA8;
60 static const float		SQRT_HALF			= 0.707107f;
61 
62 namespace
63 {
64 
65 struct QuadCorners
66 {
67 	Vec2 p0;
68 	Vec2 p1;
69 	Vec2 p2;
70 	Vec2 p3;
71 
QuadCornersdeqp::gles3::Functional::__anoncabd87430111::QuadCorners72 	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 
getIterationCount(const tcu::TestContext & ctx,int defaultCount)77 static inline int getIterationCount (const tcu::TestContext& ctx, int defaultCount)
78 {
79 	int cmdLineValue = ctx.getCommandLine().getTestIterationCount();
80 	return cmdLineValue > 0 ? cmdLineValue : defaultCount;
81 }
82 
getGLInteger(GLenum name)83 static inline int getGLInteger (GLenum name)
84 {
85 	int result;
86 	GLU_CHECK_CALL(glGetIntegerv(name, &result));
87 	return result;
88 }
89 
90 template<typename T>
min4(T a,T b,T c,T d)91 static 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 
96 template<typename T>
max4(T a,T b,T c,T d)97 static 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 
isInsideQuad(const IVec2 & point,const IVec2 & p0,const IVec2 & p1,const IVec2 & p2,const IVec2 & p3)102 static 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  *//*--------------------------------------------------------------------*/
isPixelRegionUnicolored(const tcu::Surface & img,const IVec2 & p0,const IVec2 & p1,const IVec2 & p2,const IVec2 & p3)118 static 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 
drawUnicolorTestErrors(tcu::Surface & img,const tcu::PixelBufferAccess & errorImg,const IVec2 & p0,const IVec2 & p1,const IVec2 & p2,const IVec2 & p3)150 static 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  *//*--------------------------------------------------------------------*/
177 class MultisampleCase : public TestCase
178 {
179 public:
180 	struct FboParams
181 	{
182 		bool		useFbo;
183 		int			numSamples; //!< If 0, use implementation-defined maximum.
184 		bool		useDepth;
185 		bool		useStencil;
186 
FboParamsdeqp::gles3::Functional::MultisampleCase::FboParams187 		FboParams (int numSamples_, bool useDepth_, bool useStencil_)
188 			: useFbo			(true)
189 			, numSamples		(numSamples_)
190 			, useDepth			(useDepth_)
191 			, useStencil		(useStencil_)
192 		{
193 		}
194 
FboParamsdeqp::gles3::Functional::MultisampleCase::FboParams195 		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 
210 protected:
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 
getRenderTargetSize(void) const222 	IVec2				getRenderTargetSize		(void) const 				{ return IVec2(m_renderWidth, m_renderHeight); }
223 
224 	int					m_numSamples;
225 
226 	int					m_viewportSize;
227 
228 private:
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 
MultisampleCase(Context & context,const char * name,const char * desc,int desiredViewportSize,const FboParams & fboParams)252 MultisampleCase::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 
~MultisampleCase(void)276 MultisampleCase::~MultisampleCase (void)
277 {
278 	MultisampleCase::deinit();
279 }
280 
deinit(void)281 void 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 
renderTriangle(const Vec3 & p0,const Vec3 & p1,const Vec3 & p2,const Vec4 & c0,const Vec4 & c1,const Vec4 & c2) const317 void 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 
renderTriangle(const Vec3 & p0,const Vec3 & p1,const Vec3 & p2,const Vec4 & color) const342 void 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 
renderTriangle(const Vec2 & p0,const Vec2 & p1,const Vec2 & p2,const Vec4 & c0,const Vec4 & c1,const Vec4 & c2) const347 void 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 
renderTriangle(const Vec2 & p0,const Vec2 & p1,const Vec2 & p2,const Vec4 & color) const355 void 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 
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) const360 void 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 
renderQuad(const Vec2 & p0,const Vec2 & p1,const Vec2 & p2,const Vec2 & p3,const Vec4 & color) const366 void 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 
renderLine(const Vec2 & p0,const Vec2 & p1,const Vec4 & color) const371 void 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 
randomizeViewport(void)394 void 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 
readImage(tcu::Surface & dst) const402 void 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 
init(void)418 void 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  *//*--------------------------------------------------------------------*/
551 class NumSamplesCase : public MultisampleCase
552 {
553 public:
554 						NumSamplesCase			(Context& context, const char* name, const char* description, const FboParams& fboParams = FboParams());
~NumSamplesCase(void)555 						~NumSamplesCase			(void) {}
556 
557 	IterateResult		iterate					(void);
558 
559 protected:
560 	virtual void		renderPattern			(void) const = 0;
561 
562 	int					m_currentIteration;
563 
564 private:
565 	enum { DEFAULT_MAX_NUM_ITERATIONS = 16 };
566 
567 	const int			m_maxNumIterations;
568 	vector<tcu::RGBA>	m_detectedColors;
569 };
570 
NumSamplesCase(Context & context,const char * name,const char * description,const FboParams & fboParams)571 NumSamplesCase::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 
iterate(void)578 NumSamplesCase::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 
660 class PolygonNumSamplesCase : public NumSamplesCase
661 {
662 public:
663 			PolygonNumSamplesCase	(Context& context, const char* name, const char* description, int numFboSamples = 0);
~PolygonNumSamplesCase(void)664 			~PolygonNumSamplesCase	(void) {}
665 
666 protected:
667 	void	renderPattern			(void) const;
668 };
669 
PolygonNumSamplesCase(Context & context,const char * name,const char * description,int numFboSamples)670 PolygonNumSamplesCase::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 
renderPattern(void) const675 void 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 
692 class LineNumSamplesCase : public NumSamplesCase
693 {
694 public:
695 			LineNumSamplesCase		(Context& context, const char* name, const char* description, int numFboSamples = 0);
~LineNumSamplesCase(void)696 			~LineNumSamplesCase		(void) {}
697 
698 protected:
699 	void	renderPattern			(void) const;
700 };
701 
LineNumSamplesCase(Context & context,const char * name,const char * description,int numFboSamples)702 LineNumSamplesCase::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 
renderPattern(void) const707 void 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  *//*--------------------------------------------------------------------*/
733 class CommonEdgeCase : public MultisampleCase
734 {
735 public:
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);
~CommonEdgeCase(void)746 					~CommonEdgeCase			(void) {}
747 
748 	void			init					(void);
749 
750 	IterateResult	iterate					(void);
751 
752 private:
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 
CommonEdgeCase(Context & context,const char * name,const char * description,CaseType caseType,int numFboSamples)766 CommonEdgeCase::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 
init(void)776 void 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 
iterate(void)796 CommonEdgeCase::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  *//*--------------------------------------------------------------------*/
1045 class SampleDepthCase : public NumSamplesCase
1046 {
1047 public:
1048 						SampleDepthCase			(Context& context, const char* name, const char* description, int numFboSamples = 0);
~SampleDepthCase(void)1049 						~SampleDepthCase		(void) {}
1050 
1051 	void				init					(void);
1052 
1053 protected:
1054 	void				renderPattern			(void) const;
1055 };
1056 
SampleDepthCase(Context & context,const char * name,const char * description,int numFboSamples)1057 SampleDepthCase::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 
init(void)1062 void 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 
renderPattern(void) const1078 void 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  *//*--------------------------------------------------------------------*/
1108 class SampleStencilCase : public MultisampleCase
1109 {
1110 public:
1111 						SampleStencilCase		(Context& context, const char* name, const char* description, int numFboSamples = 0);
~SampleStencilCase(void)1112 						~SampleStencilCase		(void) {}
1113 
1114 	void				init					(void);
1115 	IterateResult		iterate					(void);
1116 };
1117 
SampleStencilCase(Context & context,const char * name,const char * description,int numFboSamples)1118 SampleStencilCase::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 
init(void)1123 void 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 
iterate(void)1131 SampleStencilCase::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  *//*--------------------------------------------------------------------*/
1228 class MaskProportionalityCase : public MultisampleCase
1229 {
1230 public:
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);
~MaskProportionalityCase(void)1241 					~MaskProportionalityCase			(void) {}
1242 
1243 	void			init								(void);
1244 
1245 	IterateResult	iterate								(void);
1246 
1247 private:
1248 	const CaseType	m_type;
1249 
1250 	int				m_numIterations;
1251 	int				m_currentIteration;
1252 
1253 	deInt32			m_previousIterationColorSum;
1254 };
1255 
MaskProportionalityCase(Context & context,const char * name,const char * description,CaseType type,int numFboSamples)1256 MaskProportionalityCase::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 
init(void)1264 void 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 
iterate(void)1288 MaskProportionalityCase::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  *//*--------------------------------------------------------------------*/
1404 class MaskConstancyCase : public MultisampleCase
1405 {
1406 public:
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);
~MaskConstancyCase(void)1419 					~MaskConstancyCase			(void) {}
1420 
1421 	IterateResult	iterate						(void);
1422 
1423 private:
1424 	const bool		m_isAlphaToCoverageCase;
1425 	const bool		m_isSampleCoverageCase;
1426 	const bool		m_isInvertedSampleCoverageCase;
1427 };
1428 
MaskConstancyCase(Context & context,const char * name,const char * description,CaseType type,int numFboSamples)1429 MaskConstancyCase::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 
iterate(void)1437 MaskConstancyCase::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  *//*--------------------------------------------------------------------*/
1534 class CoverageMaskInvertCase : public MultisampleCase
1535 {
1536 public:
1537 					CoverageMaskInvertCase		(Context& context, const char* name, const char* description, int numFboSamples = 0);
~CoverageMaskInvertCase(void)1538 					~CoverageMaskInvertCase		(void) {}
1539 
1540 	IterateResult	iterate						(void);
1541 
1542 private:
1543 	void			drawPattern					(bool invertSampleCoverage) const;
1544 };
1545 
CoverageMaskInvertCase(Context & context,const char * name,const char * description,int numFboSamples)1546 CoverageMaskInvertCase::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 
drawPattern(bool invertSampleCoverage) const1551 void 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 
iterate(void)1571 CoverageMaskInvertCase::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 
MultisampleTests(Context & context)1621 MultisampleTests::MultisampleTests (Context& context)
1622 	: TestCaseGroup(context, "multisample", "Multisampling tests")
1623 {
1624 }
1625 
~MultisampleTests(void)1626 MultisampleTests::~MultisampleTests (void)
1627 {
1628 }
1629 
init(void)1630 void 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