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 Functional rasterization tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es2fRasterizationTests.hpp"
25#include "tcuRasterizationVerifier.hpp"
26#include "tcuSurface.hpp"
27#include "tcuRenderTarget.hpp"
28#include "tcuVectorUtil.hpp"
29#include "tcuStringTemplate.hpp"
30#include "tcuResultCollector.hpp"
31#include "gluShaderProgram.hpp"
32#include "gluRenderContext.hpp"
33#include "gluPixelTransfer.hpp"
34#include "gluStrUtil.hpp"
35#include "deStringUtil.hpp"
36#include "deRandom.hpp"
37#include "glwFunctions.hpp"
38#include "glwEnums.hpp"
39
40#include <vector>
41
42namespace deqp
43{
44namespace gles2
45{
46namespace Functional
47{
48namespace
49{
50
51using tcu::RasterizationArguments;
52using tcu::TriangleSceneSpec;
53using tcu::PointSceneSpec;
54using tcu::LineSceneSpec;
55using tcu::LineInterpolationMethod;
56
57static const char* const s_shaderVertexTemplate =	"attribute highp vec4 a_position;\n"
58													"attribute highp vec4 a_color;\n"
59													"varying highp vec4 v_color;\n"
60													"uniform highp float u_pointSize;\n"
61													"void main ()\n"
62													"{\n"
63													"	gl_Position = a_position;\n"
64													"	gl_PointSize = u_pointSize;\n"
65													"	v_color = a_color;\n"
66													"}\n";
67static const char* const s_shaderFragmentTemplate =	"varying mediump vec4 v_color;\n"
68													"void main ()\n"
69													"{\n"
70													"	gl_FragColor = v_color;\n"
71													"}\n";
72enum InterpolationCaseFlags
73{
74	INTERPOLATIONFLAGS_NONE = 0,
75	INTERPOLATIONFLAGS_PROJECTED = (1 << 1),
76};
77
78enum PrimitiveWideness
79{
80	PRIMITIVEWIDENESS_NARROW = 0,
81	PRIMITIVEWIDENESS_WIDE,
82
83	PRIMITIVEWIDENESS_LAST
84};
85
86class BaseRenderingCase : public TestCase
87{
88public:
89							BaseRenderingCase	(Context& context, const char* name, const char* desc, int renderSize = 256);
90							~BaseRenderingCase	(void);
91	virtual void			init				(void);
92	void					deinit				(void);
93
94protected:
95	void					drawPrimitives		(tcu::Surface& result, const std::vector<tcu::Vec4>& vertexData, glw::GLenum primitiveType);
96	void					drawPrimitives		(tcu::Surface& result, const std::vector<tcu::Vec4>& vertexData, const std::vector<tcu::Vec4>& coloDrata, glw::GLenum primitiveType);
97
98	const int				m_renderSize;
99	int						m_numSamples;
100	int						m_subpixelBits;
101	float					m_pointSize;
102	float					m_lineWidth;
103
104	glu::ShaderProgram*		m_shader;
105};
106
107BaseRenderingCase::BaseRenderingCase (Context& context, const char* name, const char* desc, int renderSize)
108	: TestCase				(context, name, desc)
109	, m_renderSize			(renderSize)
110	, m_numSamples			(-1)
111	, m_subpixelBits		(-1)
112	, m_pointSize			(1.0f)
113	, m_lineWidth			(1.0f)
114	, m_shader				(DE_NULL)
115{
116}
117
118BaseRenderingCase::~BaseRenderingCase (void)
119{
120	deinit();
121}
122
123void BaseRenderingCase::init (void)
124{
125	const int width	 = m_context.getRenderTarget().getWidth();
126	const int height = m_context.getRenderTarget().getHeight();
127
128	// Requirements
129
130	if (width < m_renderSize || height < m_renderSize)
131		throw tcu::NotSupportedError(std::string("Render target size must be at least ") + de::toString(m_renderSize) + "x" + de::toString(m_renderSize));
132
133	if (m_lineWidth != 1.0f)
134	{
135		float range[2] = { 0.0f, 0.0f };
136		m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_LINE_WIDTH_RANGE, range);
137
138		if (m_lineWidth < range[0] || m_lineWidth > range[1])
139			throw tcu::NotSupportedError(std::string("Support for line width ") + de::toString(m_lineWidth) + " is required.");
140
141		m_testCtx.getLog() << tcu::TestLog::Message << "ALIASED_LINE_WIDTH_RANGE = [" << range[0] << ", " << range[1] << "]" << tcu::TestLog::EndMessage;
142	}
143
144	if (m_pointSize != 1.0f)
145	{
146		float range[2] = { 0.0f, 0.0f };
147		m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_POINT_SIZE_RANGE, range);
148
149		if (m_pointSize < range[0] || m_pointSize > range[1])
150			throw tcu::NotSupportedError(std::string("Support for point size ") + de::toString(m_pointSize) + " is required.");
151
152		m_testCtx.getLog() << tcu::TestLog::Message << "ALIASED_POINT_SIZE_RANGE = [" << range[0] << ", " << range[1] << "]" << tcu::TestLog::EndMessage;
153	}
154
155	// Query info
156
157	m_numSamples = m_context.getRenderTarget().getNumSamples();
158	m_context.getRenderContext().getFunctions().getIntegerv(GL_SUBPIXEL_BITS, &m_subpixelBits);
159
160	m_testCtx.getLog() << tcu::TestLog::Message << "Sample count = " << m_numSamples << tcu::TestLog::EndMessage;
161	m_testCtx.getLog() << tcu::TestLog::Message << "SUBPIXEL_BITS = " << m_subpixelBits << tcu::TestLog::EndMessage;
162
163	// Gen shader
164
165	{
166		tcu::StringTemplate					vertexSource	(s_shaderVertexTemplate);
167		tcu::StringTemplate					fragmentSource	(s_shaderFragmentTemplate);
168		std::map<std::string, std::string>	params;
169
170		m_shader = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(vertexSource.specialize(params)) << glu::FragmentSource(fragmentSource.specialize(params)));
171		if (!m_shader->isOk())
172			throw tcu::TestError("could not create shader");
173	}
174}
175
176void BaseRenderingCase::deinit (void)
177{
178	if (m_shader)
179	{
180		delete m_shader;
181		m_shader = DE_NULL;
182	}
183}
184
185void BaseRenderingCase::drawPrimitives (tcu::Surface& result, const std::vector<tcu::Vec4>& vertexData, glw::GLenum primitiveType)
186{
187	// default to color white
188	const std::vector<tcu::Vec4> colorData(vertexData.size(), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
189
190	drawPrimitives(result, vertexData, colorData, primitiveType);
191}
192
193void BaseRenderingCase::drawPrimitives (tcu::Surface& result, const std::vector<tcu::Vec4>& vertexData, const std::vector<tcu::Vec4>& colorData, glw::GLenum primitiveType)
194{
195	const glw::Functions&	gl				= m_context.getRenderContext().getFunctions();
196	const glw::GLint		positionLoc		= gl.getAttribLocation(m_shader->getProgram(), "a_position");
197	const glw::GLint		colorLoc		= gl.getAttribLocation(m_shader->getProgram(), "a_color");
198	const glw::GLint		pointSizeLoc	= gl.getUniformLocation(m_shader->getProgram(), "u_pointSize");
199
200	gl.clearColor					(0, 0, 0, 1);
201	gl.clear						(GL_COLOR_BUFFER_BIT);
202	gl.viewport						(0, 0, m_renderSize, m_renderSize);
203	gl.useProgram					(m_shader->getProgram());
204	gl.enableVertexAttribArray		(positionLoc);
205	gl.vertexAttribPointer			(positionLoc, 4, GL_FLOAT, GL_FALSE, 0, &vertexData[0]);
206	gl.enableVertexAttribArray		(colorLoc);
207	gl.vertexAttribPointer			(colorLoc, 4, GL_FLOAT, GL_FALSE, 0, &colorData[0]);
208	gl.uniform1f					(pointSizeLoc, m_pointSize);
209	gl.lineWidth					(m_lineWidth);
210	gl.drawArrays					(primitiveType, 0, (glw::GLsizei)vertexData.size());
211	gl.disableVertexAttribArray		(colorLoc);
212	gl.disableVertexAttribArray		(positionLoc);
213	gl.useProgram					(0);
214	gl.finish						();
215	GLU_EXPECT_NO_ERROR				(gl.getError(), "draw primitives");
216
217	glu::readPixels(m_context.getRenderContext(), 0, 0, result.getAccess());
218}
219
220class BaseTriangleCase : public BaseRenderingCase
221{
222public:
223							BaseTriangleCase	(Context& context, const char* name, const char* desc, glw::GLenum primitiveDrawType);
224							~BaseTriangleCase	(void);
225	IterateResult			iterate				(void);
226
227private:
228	virtual void			generateTriangles	(int iteration, std::vector<tcu::Vec4>& outData, std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles) = DE_NULL;
229
230	int						m_iteration;
231	const int				m_iterationCount;
232	const glw::GLenum		m_primitiveDrawType;
233	bool					m_allIterationsPassed;
234};
235
236BaseTriangleCase::BaseTriangleCase (Context& context, const char* name, const char* desc, glw::GLenum primitiveDrawType)
237	: BaseRenderingCase		(context, name, desc)
238	, m_iteration			(0)
239	, m_iterationCount		(3)
240	, m_primitiveDrawType	(primitiveDrawType)
241	, m_allIterationsPassed	(true)
242{
243}
244
245BaseTriangleCase::~BaseTriangleCase (void)
246{
247}
248
249BaseTriangleCase::IterateResult BaseTriangleCase::iterate (void)
250{
251	const std::string								iterationDescription	= "Test iteration " + de::toString(m_iteration+1) + " / " + de::toString(m_iterationCount);
252	const tcu::ScopedLogSection						section					(m_testCtx.getLog(), iterationDescription, iterationDescription);
253	tcu::Surface									resultImage				(m_renderSize, m_renderSize);
254	std::vector<tcu::Vec4>							drawBuffer;
255	std::vector<TriangleSceneSpec::SceneTriangle>	triangles;
256
257	generateTriangles(m_iteration, drawBuffer, triangles);
258
259	// draw image
260	drawPrimitives(resultImage, drawBuffer, m_primitiveDrawType);
261
262	// compare
263	{
264		bool					compareOk;
265		RasterizationArguments	args;
266		TriangleSceneSpec		scene;
267
268		args.numSamples		= m_numSamples;
269		args.subpixelBits	= m_subpixelBits;
270		args.redBits		= m_context.getRenderTarget().getPixelFormat().redBits;
271		args.greenBits		= m_context.getRenderTarget().getPixelFormat().greenBits;
272		args.blueBits		= m_context.getRenderTarget().getPixelFormat().blueBits;
273
274		scene.triangles.swap(triangles);
275
276		compareOk = verifyTriangleGroupRasterization(resultImage, scene, args, m_testCtx.getLog());
277
278		if (!compareOk)
279			m_allIterationsPassed = false;
280	}
281
282	// result
283	if (++m_iteration == m_iterationCount)
284	{
285		if (m_allIterationsPassed)
286			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
287		else
288			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Incorrect rasterization");
289
290		return STOP;
291	}
292	else
293		return CONTINUE;
294}
295
296class BaseLineCase : public BaseRenderingCase
297{
298public:
299							BaseLineCase		(Context& context, const char* name, const char* desc, glw::GLenum primitiveDrawType, PrimitiveWideness wideness);
300							~BaseLineCase		(void);
301	IterateResult			iterate				(void);
302
303private:
304	virtual void			generateLines		(int iteration, std::vector<tcu::Vec4>& outData, std::vector<LineSceneSpec::SceneLine>& outLines) = DE_NULL;
305
306	int						m_iteration;
307	const int				m_iterationCount;
308	const glw::GLenum		m_primitiveDrawType;
309	const PrimitiveWideness	m_primitiveWideness;
310	bool					m_allIterationsPassed;
311	bool					m_multisampleRelaxationRequired;
312
313	static const float		s_wideSize;
314};
315
316const float BaseLineCase::s_wideSize = 5.0f;
317
318BaseLineCase::BaseLineCase (Context& context, const char* name, const char* desc, glw::GLenum primitiveDrawType, PrimitiveWideness wideness)
319	: BaseRenderingCase					(context, name, desc)
320	, m_iteration						(0)
321	, m_iterationCount					(3)
322	, m_primitiveDrawType				(primitiveDrawType)
323	, m_primitiveWideness				(wideness)
324	, m_allIterationsPassed				(true)
325	, m_multisampleRelaxationRequired	(false)
326{
327	DE_ASSERT(m_primitiveWideness < PRIMITIVEWIDENESS_LAST);
328	m_lineWidth = (m_primitiveWideness == PRIMITIVEWIDENESS_WIDE) ? (s_wideSize) : (1.0f);
329}
330
331BaseLineCase::~BaseLineCase (void)
332{
333}
334
335BaseLineCase::IterateResult BaseLineCase::iterate (void)
336{
337	const std::string						iterationDescription	= "Test iteration " + de::toString(m_iteration+1) + " / " + de::toString(m_iterationCount);
338	const tcu::ScopedLogSection				section					(m_testCtx.getLog(), iterationDescription, iterationDescription);
339	tcu::Surface							resultImage				(m_renderSize, m_renderSize);
340	std::vector<tcu::Vec4>					drawBuffer;
341	std::vector<LineSceneSpec::SceneLine>	lines;
342
343	// last iteration, max out size
344	if (m_primitiveWideness == PRIMITIVEWIDENESS_WIDE &&
345		m_iteration+1 == m_iterationCount)
346	{
347		float range[2] = { 0.0f, 0.0f };
348		m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_LINE_WIDTH_RANGE, range);
349
350		m_lineWidth = range[1];
351	}
352
353	// gen data
354	generateLines(m_iteration, drawBuffer, lines);
355
356	// draw image
357	drawPrimitives(resultImage, drawBuffer, m_primitiveDrawType);
358
359	// compare
360	{
361		bool					compareOk;
362		RasterizationArguments	args;
363		LineSceneSpec			scene;
364
365		args.numSamples		= m_numSamples;
366		args.subpixelBits	= m_subpixelBits;
367		args.redBits		= m_context.getRenderTarget().getPixelFormat().redBits;
368		args.greenBits		= m_context.getRenderTarget().getPixelFormat().greenBits;
369		args.blueBits		= m_context.getRenderTarget().getPixelFormat().blueBits;
370
371		scene.lines.swap(lines);
372		scene.lineWidth = m_lineWidth;
373
374		compareOk = verifyLineGroupRasterization(resultImage, scene, args, m_testCtx.getLog());
375
376		// multisampled wide lines might not be supported
377		if (scene.lineWidth != 1.0f && m_numSamples > 1 && !compareOk)
378		{
379			m_multisampleRelaxationRequired = true;
380			compareOk = true;
381		}
382
383		if (!compareOk)
384			m_allIterationsPassed = false;
385	}
386
387	// result
388	if (++m_iteration == m_iterationCount)
389	{
390		if (m_allIterationsPassed && m_multisampleRelaxationRequired)
391			m_testCtx.setTestResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, "Rasterization of multisampled wide lines failed");
392		else if (m_allIterationsPassed)
393			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
394		else
395			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Incorrect rasterization");
396
397		return STOP;
398	}
399	else
400		return CONTINUE;
401}
402
403class PointCase : public BaseRenderingCase
404{
405public:
406							PointCase		(Context& context, const char* name, const char* desc, PrimitiveWideness wideness);
407							~PointCase		(void);
408	IterateResult			iterate			(void);
409
410private:
411	void					generatePoints	(int iteration, std::vector<tcu::Vec4>& outData, std::vector<PointSceneSpec::ScenePoint>& outPoints);
412
413	int						m_iteration;
414	const int				m_iterationCount;
415	const PrimitiveWideness	m_primitiveWideness;
416	bool					m_allIterationsPassed;
417
418	static const float		s_wideSize;
419};
420
421const float PointCase::s_wideSize = 10.0f;
422
423PointCase::PointCase (Context& context, const char* name, const char* desc, PrimitiveWideness wideness)
424	: BaseRenderingCase		(context, name, desc)
425	, m_iteration			(0)
426	, m_iterationCount		(3)
427	, m_primitiveWideness	(wideness)
428	, m_allIterationsPassed	(true)
429{
430	m_pointSize = (m_primitiveWideness == PRIMITIVEWIDENESS_WIDE) ? (s_wideSize) : (1.0f);
431}
432
433PointCase::~PointCase (void)
434{
435}
436
437PointCase::IterateResult PointCase::iterate (void)
438{
439	const std::string						iterationDescription	= "Test iteration " + de::toString(m_iteration+1) + " / " + de::toString(m_iterationCount);
440	const tcu::ScopedLogSection				section					(m_testCtx.getLog(), iterationDescription, iterationDescription);
441	tcu::Surface							resultImage				(m_renderSize, m_renderSize);
442	std::vector<tcu::Vec4>					drawBuffer;
443	std::vector<PointSceneSpec::ScenePoint>	points;
444
445	// last iteration, max out size
446	if (m_primitiveWideness == PRIMITIVEWIDENESS_WIDE &&
447		m_iteration+1 == m_iterationCount)
448	{
449		float range[2] = { 0.0f, 0.0f };
450		m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_POINT_SIZE_RANGE, range);
451
452		m_pointSize = range[1];
453	}
454
455	// gen data
456	generatePoints(m_iteration, drawBuffer, points);
457
458	// draw image
459	drawPrimitives(resultImage, drawBuffer, GL_POINTS);
460
461	// compare
462	{
463		bool					compareOk;
464		RasterizationArguments	args;
465		PointSceneSpec			scene;
466
467		args.numSamples		= m_numSamples;
468		args.subpixelBits	= m_subpixelBits;
469		args.redBits		= m_context.getRenderTarget().getPixelFormat().redBits;
470		args.greenBits		= m_context.getRenderTarget().getPixelFormat().greenBits;
471		args.blueBits		= m_context.getRenderTarget().getPixelFormat().blueBits;
472
473		scene.points.swap(points);
474
475		compareOk = verifyPointGroupRasterization(resultImage, scene, args, m_testCtx.getLog());
476
477		if (!compareOk)
478			m_allIterationsPassed = false;
479	}
480
481	// result
482	if (++m_iteration == m_iterationCount)
483	{
484		if (m_allIterationsPassed)
485			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
486		else
487			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Incorrect rasterization");
488
489		return STOP;
490	}
491	else
492		return CONTINUE;
493}
494
495void PointCase::generatePoints	(int iteration, std::vector<tcu::Vec4>& outData, std::vector<PointSceneSpec::ScenePoint>& outPoints)
496{
497	outData.resize(6);
498
499	switch (iteration)
500	{
501		case 0:
502			// \note: these values are chosen arbitrarily
503			outData[0] = tcu::Vec4( 0.2f,  0.8f, 0.0f, 1.0f);
504			outData[1] = tcu::Vec4( 0.5f,  0.2f, 0.0f, 1.0f);
505			outData[2] = tcu::Vec4( 0.5f,  0.3f, 0.0f, 1.0f);
506			outData[3] = tcu::Vec4(-0.5f,  0.2f, 0.0f, 1.0f);
507			outData[4] = tcu::Vec4(-0.2f, -0.4f, 0.0f, 1.0f);
508			outData[5] = tcu::Vec4(-0.4f,  0.2f, 0.0f, 1.0f);
509			break;
510
511		case 1:
512			outData[0] = tcu::Vec4(-0.499f, 0.128f, 0.0f, 1.0f);
513			outData[1] = tcu::Vec4(-0.501f,  -0.3f, 0.0f, 1.0f);
514			outData[2] = tcu::Vec4(  0.11f,  -0.2f, 0.0f, 1.0f);
515			outData[3] = tcu::Vec4(  0.11f,   0.2f, 0.0f, 1.0f);
516			outData[4] = tcu::Vec4(  0.88f,   0.9f, 0.0f, 1.0f);
517			outData[5] = tcu::Vec4(   0.4f,   1.2f, 0.0f, 1.0f);
518			break;
519
520		case 2:
521			outData[0] = tcu::Vec4( -0.9f, -0.3f, 0.0f, 1.0f);
522			outData[1] = tcu::Vec4(  0.3f, -0.9f, 0.0f, 1.0f);
523			outData[2] = tcu::Vec4( -0.4f, -0.1f, 0.0f, 1.0f);
524			outData[3] = tcu::Vec4(-0.11f,  0.2f, 0.0f, 1.0f);
525			outData[4] = tcu::Vec4( 0.88f,  0.7f, 0.0f, 1.0f);
526			outData[5] = tcu::Vec4( -0.4f,  0.4f, 0.0f, 1.0f);
527			break;
528	}
529
530	outPoints.resize(outData.size());
531	for (int pointNdx = 0; pointNdx < (int)outPoints.size(); ++pointNdx)
532	{
533		outPoints[pointNdx].position = outData[pointNdx];
534		outPoints[pointNdx].pointSize = m_pointSize;
535	}
536
537	// log
538	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering " << outPoints.size() << " point(s): (point size = " << m_pointSize << ")" << tcu::TestLog::EndMessage;
539	for (int pointNdx = 0; pointNdx < (int)outPoints.size(); ++pointNdx)
540		m_testCtx.getLog() << tcu::TestLog::Message << "Point " << (pointNdx+1) << ":\t" << outPoints[pointNdx].position << tcu::TestLog::EndMessage;
541}
542
543class PointSizeClampedTest : public BaseRenderingCase
544{
545public:
546	PointSizeClampedTest (Context& context, const char* name, const char* desc)
547		: BaseRenderingCase	(context, name, desc)
548	{
549	}
550
551	IterateResult iterate ()
552	{
553		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
554		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
555
556		// Tests that point sizes (written to gl_PointSize) are clamped,
557		// before rasterization, to the ALIASED_POINT_SIZE_RANGE
558		// given by the implementation.
559		static const int fboHeight = 4;
560		static const int testAreaWidth = 4;
561		static const int testAreaWidthWithMargin = testAreaWidth + 4;
562		static const float pointRadiusOverage = 8;
563		int fboWidth = 0;
564		int maxPointDiameter = 0;
565		{
566			int maxRenderbufferSize = 0;
567			int maxViewportDims[2] = {};
568			gl.getIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxRenderbufferSize);
569			gl.getIntegerv(GL_MAX_VIEWPORT_DIMS, maxViewportDims);
570			int maxFboWidth = std::min(maxRenderbufferSize, maxViewportDims[0]);
571
572			float pointSizeRange[2] = {};
573			gl.getFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
574			m_testCtx.getLog() << tcu::TestLog::Message
575				<< "GL_ALIASED_POINT_SIZE_RANGE is [" << pointSizeRange[0] << ", " << pointSizeRange[1] << "]"
576				<< tcu::TestLog::EndMessage;
577			// Typically (in the correct case), maxPointDiameter is an odd integer.
578			maxPointDiameter = (int) pointSizeRange[1];
579			// maxPointRadius is inclusive of the center point.
580			int maxPointRadius = (maxPointDiameter + 1) / 2;
581			if (maxPointRadius > maxFboWidth - testAreaWidthWithMargin)
582			{
583				m_testCtx.setTestResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, "max framebuffer size isn't large enough to test max point size");
584				return STOP;
585			}
586			fboWidth = maxPointRadius + testAreaWidthWithMargin;
587			// Round up to the nearest multiple of 2:
588			fboWidth = ((fboWidth + 1) / 2) * 2;
589		}
590		float pointSize = ((float) maxPointDiameter) + pointRadiusOverage * 2;
591		TCU_CHECK(gl.getError() == GL_NO_ERROR);
592
593		m_testCtx.getLog() << tcu::TestLog::Message
594			<< "Testing with pointSize = " << pointSize
595			<< ", fboWidth = " << fboWidth
596			<< tcu::TestLog::EndMessage;
597
598		// Create a framebuffer that is (fboWidth)x(fboHeight), cleared to green:
599		// +---------------------------+
600		// |ggggggggggggggggggggggggggg|
601		// +---------------------------+
602		gl.viewport(0, 0, fboWidth, fboHeight);
603		deUint32 fbo = 0;
604		gl.genFramebuffers(1, &fbo);
605		gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
606		deUint32 rbo = 0;
607		gl.genRenderbuffers(1, &rbo);
608		gl.bindRenderbuffer(GL_RENDERBUFFER, rbo);
609		gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, fboWidth, fboHeight);
610		gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
611		if (gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
612		{
613			m_testCtx.setTestResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, "couldn't complete a framebuffer suitable to test max point size");
614			return STOP;
615		}
616		gl.clearColor(0.0f, 1.0f, 0.0f, 1.0f);
617		gl.clear(GL_COLOR_BUFFER_BIT);
618		TCU_CHECK(gl.getError() == GL_NO_ERROR);
619
620		// (Framebuffer is still bound.)
621
622		// Draw a red point, with size pointSize, at the far right:
623		// +---------------------------+
624		// |ggggggggRRRRRRRRRRRRRRRRRRR|
625		// +---------------------------+
626		//                            x                           point center
627		//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^                           fboWidth
628		//  ^^^^                                                  testAreaWidth = 4 (this is the area that's tested)
629		//  ^^^^^^^^                                              testAreaWidthWithMargin = 8 (extra 4 pixels for tolerance)
630		//          ^^^^^^^^^^^^^^^^^^x^^^^^^^^^^^^^^^^^^         maxPointDiameter = 37
631		//  ^^^^^^^^                                     ^^^^^^^^ pointRadiusOverage = 8 * 2
632		//  ^^^^^^^^^^^^^^^^^^^^^^^^^^x^^^^^^^^^^^^^^^^^^^^^^^^^^ pointSize = 53
633		//          ^^^^^^^^^^^^^^^^^^^ area of resulting draw, if the size is clamped properly = 19
634		{
635			const glw::GLint		positionLoc		= gl.getAttribLocation(m_shader->getProgram(), "a_position");
636			const glw::GLint		colorLoc		= gl.getAttribLocation(m_shader->getProgram(), "a_color");
637			const glw::GLint		pointSizeLoc	= gl.getUniformLocation(m_shader->getProgram(), "u_pointSize");
638			static const float position[] = {1.0f, 0.0f, 0.0f, 1.0f};
639			static const float color[] = {1.0f, 0.0f, 0.0f, 1.0f};
640			gl.useProgram(m_shader->getProgram());
641			gl.enableVertexAttribArray(positionLoc);
642			gl.vertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, 0, position);
643			gl.enableVertexAttribArray(colorLoc);
644			gl.vertexAttribPointer(colorLoc, 4, GL_FLOAT, GL_FALSE, 0, color);
645			gl.uniform1f(pointSizeLoc, pointSize);
646			gl.drawArrays(GL_POINTS, 0, 1);
647			gl.disableVertexAttribArray(colorLoc);
648			gl.disableVertexAttribArray(positionLoc);
649			gl.useProgram(0);
650			TCU_CHECK(gl.getError() == GL_NO_ERROR);
651		}
652
653		// And test the resulting draw (the test area should still be green).
654		deUint32 pixels[testAreaWidth * fboHeight] = {};
655		gl.readPixels(0, 0, testAreaWidth, fboHeight, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
656		TCU_CHECK(gl.getError() == GL_NO_ERROR);
657
658		const tcu::RGBA threshold(12, 12, 12, 12);
659		for (deUint32 y = 0; y < fboHeight; ++y)
660		{
661			for (deUint32 x = 0; x < testAreaWidth; ++x)
662			{
663				tcu::RGBA color(pixels[y * testAreaWidth + x]);
664				TCU_CHECK(compareThreshold(color, tcu::RGBA::green(), threshold));
665			}
666		}
667
668		return STOP;
669	}
670};
671
672class TrianglesCase : public BaseTriangleCase
673{
674public:
675			TrianglesCase		(Context& context, const char* name, const char* desc);
676			~TrianglesCase		(void);
677
678	void	generateTriangles	(int iteration, std::vector<tcu::Vec4>& outData, std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles);
679};
680
681TrianglesCase::TrianglesCase (Context& context, const char* name, const char* desc)
682	: BaseTriangleCase(context, name, desc, GL_TRIANGLES)
683{
684}
685
686TrianglesCase::~TrianglesCase (void)
687{
688
689}
690
691void TrianglesCase::generateTriangles (int iteration, std::vector<tcu::Vec4>& outData, std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles)
692{
693	outData.resize(6);
694
695	switch (iteration)
696	{
697		case 0:
698			// \note: these values are chosen arbitrarily
699			outData[0] = tcu::Vec4( 0.2f,  0.8f, 0.0f, 1.0f);
700			outData[1] = tcu::Vec4( 0.5f,  0.2f, 0.0f, 1.0f);
701			outData[2] = tcu::Vec4( 0.5f,  0.3f, 0.0f, 1.0f);
702			outData[3] = tcu::Vec4(-0.5f,  0.2f, 0.0f, 1.0f);
703			outData[4] = tcu::Vec4(-1.5f, -0.4f, 0.0f, 1.0f);
704			outData[5] = tcu::Vec4(-0.4f,  0.2f, 0.0f, 1.0f);
705			break;
706
707		case 1:
708			outData[0] = tcu::Vec4(-0.499f, 0.128f, 0.0f, 1.0f);
709			outData[1] = tcu::Vec4(-0.501f,  -0.3f, 0.0f, 1.0f);
710			outData[2] = tcu::Vec4(  0.11f,  -0.2f, 0.0f, 1.0f);
711			outData[3] = tcu::Vec4(  0.11f,   0.2f, 0.0f, 1.0f);
712			outData[4] = tcu::Vec4(  0.88f,   0.9f, 0.0f, 1.0f);
713			outData[5] = tcu::Vec4(   0.4f,   1.2f, 0.0f, 1.0f);
714			break;
715
716		case 2:
717			outData[0] = tcu::Vec4( -0.9f, -0.3f, 0.0f, 1.0f);
718			outData[1] = tcu::Vec4(  1.1f, -0.9f, 0.0f, 1.0f);
719			outData[2] = tcu::Vec4( -1.1f, -0.1f, 0.0f, 1.0f);
720			outData[3] = tcu::Vec4(-0.11f,  0.2f, 0.0f, 1.0f);
721			outData[4] = tcu::Vec4( 0.88f,  0.7f, 0.0f, 1.0f);
722			outData[5] = tcu::Vec4( -0.4f,  0.4f, 0.0f, 1.0f);
723			break;
724	}
725
726	outTriangles.resize(2);
727	outTriangles[0].positions[0] = outData[0];	outTriangles[0].sharedEdge[0] = false;
728	outTriangles[0].positions[1] = outData[1];	outTriangles[0].sharedEdge[1] = false;
729	outTriangles[0].positions[2] = outData[2];	outTriangles[0].sharedEdge[2] = false;
730
731	outTriangles[1].positions[0] = outData[3];	outTriangles[1].sharedEdge[0] = false;
732	outTriangles[1].positions[1] = outData[4];	outTriangles[1].sharedEdge[1] = false;
733	outTriangles[1].positions[2] = outData[5];	outTriangles[1].sharedEdge[2] = false;
734
735	// log
736	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering " << outTriangles.size() << " triangle(s):" << tcu::TestLog::EndMessage;
737	for (int triangleNdx = 0; triangleNdx < (int)outTriangles.size(); ++triangleNdx)
738	{
739		m_testCtx.getLog()
740			<< tcu::TestLog::Message
741			<< "Triangle " << (triangleNdx+1) << ":"
742			<< "\n\t" << outTriangles[triangleNdx].positions[0]
743			<< "\n\t" << outTriangles[triangleNdx].positions[1]
744			<< "\n\t" << outTriangles[triangleNdx].positions[2]
745			<< tcu::TestLog::EndMessage;
746	}
747}
748
749class TriangleStripCase : public BaseTriangleCase
750{
751public:
752			TriangleStripCase	(Context& context, const char* name, const char* desc);
753
754	void	generateTriangles	(int iteration, std::vector<tcu::Vec4>& outData, std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles);
755};
756
757TriangleStripCase::TriangleStripCase (Context& context, const char* name, const char* desc)
758	: BaseTriangleCase(context, name, desc, GL_TRIANGLE_STRIP)
759{
760}
761
762void TriangleStripCase::generateTriangles (int iteration, std::vector<tcu::Vec4>& outData, std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles)
763{
764	outData.resize(5);
765
766	switch (iteration)
767	{
768		case 0:
769			// \note: these values are chosen arbitrarily
770			outData[0] = tcu::Vec4(-0.504f,  0.8f,   0.0f, 1.0f);
771			outData[1] = tcu::Vec4(-0.2f,   -0.2f,   0.0f, 1.0f);
772			outData[2] = tcu::Vec4(-0.2f,    0.199f, 0.0f, 1.0f);
773			outData[3] = tcu::Vec4( 0.5f,    0.201f, 0.0f, 1.0f);
774			outData[4] = tcu::Vec4( 1.5f,    0.4f,   0.0f, 1.0f);
775			break;
776
777		case 1:
778			outData[0] = tcu::Vec4(-0.499f, 0.129f,  0.0f, 1.0f);
779			outData[1] = tcu::Vec4(-0.501f,  -0.3f,  0.0f, 1.0f);
780			outData[2] = tcu::Vec4(  0.11f,  -0.2f,  0.0f, 1.0f);
781			outData[3] = tcu::Vec4(  0.11f,  -0.31f, 0.0f, 1.0f);
782			outData[4] = tcu::Vec4(  0.88f,   0.9f,  0.0f, 1.0f);
783			break;
784
785		case 2:
786			outData[0] = tcu::Vec4( -0.9f, -0.3f,  0.0f, 1.0f);
787			outData[1] = tcu::Vec4(  1.1f, -0.9f,  0.0f, 1.0f);
788			outData[2] = tcu::Vec4(-0.87f, -0.1f,  0.0f, 1.0f);
789			outData[3] = tcu::Vec4(-0.11f,  0.19f, 0.0f, 1.0f);
790			outData[4] = tcu::Vec4( 0.88f,  0.7f,  0.0f, 1.0f);
791			break;
792	}
793
794	outTriangles.resize(3);
795	outTriangles[0].positions[0] = outData[0];	outTriangles[0].sharedEdge[0] = false;
796	outTriangles[0].positions[1] = outData[1];	outTriangles[0].sharedEdge[1] = true;
797	outTriangles[0].positions[2] = outData[2];	outTriangles[0].sharedEdge[2] = false;
798
799	outTriangles[1].positions[0] = outData[2];	outTriangles[1].sharedEdge[0] = true;
800	outTriangles[1].positions[1] = outData[1];	outTriangles[1].sharedEdge[1] = false;
801	outTriangles[1].positions[2] = outData[3];	outTriangles[1].sharedEdge[2] = true;
802
803	outTriangles[2].positions[0] = outData[2];	outTriangles[2].sharedEdge[0] = true;
804	outTriangles[2].positions[1] = outData[3];	outTriangles[2].sharedEdge[1] = false;
805	outTriangles[2].positions[2] = outData[4];	outTriangles[2].sharedEdge[2] = false;
806
807	// log
808	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering triangle strip, " << outData.size() << " vertices." << tcu::TestLog::EndMessage;
809	for (int vtxNdx = 0; vtxNdx < (int)outData.size(); ++vtxNdx)
810	{
811		m_testCtx.getLog()
812			<< tcu::TestLog::Message
813			<< "\t" << outData[vtxNdx]
814			<< tcu::TestLog::EndMessage;
815	}
816}
817
818class TriangleFanCase : public BaseTriangleCase
819{
820public:
821			TriangleFanCase		(Context& context, const char* name, const char* desc);
822
823	void	generateTriangles	(int iteration, std::vector<tcu::Vec4>& outData, std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles);
824};
825
826TriangleFanCase::TriangleFanCase (Context& context, const char* name, const char* desc)
827	: BaseTriangleCase(context, name, desc, GL_TRIANGLE_FAN)
828{
829}
830
831void TriangleFanCase::generateTriangles (int iteration, std::vector<tcu::Vec4>& outData, std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles)
832{
833	outData.resize(5);
834
835	switch (iteration)
836	{
837		case 0:
838			// \note: these values are chosen arbitrarily
839			outData[0] = tcu::Vec4( 0.01f,  0.0f, 0.0f, 1.0f);
840			outData[1] = tcu::Vec4( 0.5f,   0.2f, 0.0f, 1.0f);
841			outData[2] = tcu::Vec4( 0.46f,  0.3f, 0.0f, 1.0f);
842			outData[3] = tcu::Vec4(-0.5f,   0.2f, 0.0f, 1.0f);
843			outData[4] = tcu::Vec4(-1.5f,  -0.4f, 0.0f, 1.0f);
844			break;
845
846		case 1:
847			outData[0] = tcu::Vec4(-0.499f, 0.128f, 0.0f, 1.0f);
848			outData[1] = tcu::Vec4(-0.501f,  -0.3f, 0.0f, 1.0f);
849			outData[2] = tcu::Vec4(  0.11f,  -0.2f, 0.0f, 1.0f);
850			outData[3] = tcu::Vec4(  0.11f,   0.2f, 0.0f, 1.0f);
851			outData[4] = tcu::Vec4(  0.88f,   0.9f, 0.0f, 1.0f);
852			break;
853
854		case 2:
855			outData[0] = tcu::Vec4( -0.9f, -0.3f, 0.0f, 1.0f);
856			outData[1] = tcu::Vec4(  1.1f, -0.9f, 0.0f, 1.0f);
857			outData[2] = tcu::Vec4(  0.7f, -0.1f, 0.0f, 1.0f);
858			outData[3] = tcu::Vec4( 0.11f,  0.2f, 0.0f, 1.0f);
859			outData[4] = tcu::Vec4( 0.88f,  0.7f, 0.0f, 1.0f);
860			break;
861	}
862
863	outTriangles.resize(3);
864	outTriangles[0].positions[0] = outData[0];	outTriangles[0].sharedEdge[0] = false;
865	outTriangles[0].positions[1] = outData[1];	outTriangles[0].sharedEdge[1] = false;
866	outTriangles[0].positions[2] = outData[2];	outTriangles[0].sharedEdge[2] = true;
867
868	outTriangles[1].positions[0] = outData[0];	outTriangles[1].sharedEdge[0] = true;
869	outTriangles[1].positions[1] = outData[2];	outTriangles[1].sharedEdge[1] = false;
870	outTriangles[1].positions[2] = outData[3];	outTriangles[1].sharedEdge[2] = true;
871
872	outTriangles[2].positions[0] = outData[0];	outTriangles[2].sharedEdge[0] = true;
873	outTriangles[2].positions[1] = outData[3];	outTriangles[2].sharedEdge[1] = false;
874	outTriangles[2].positions[2] = outData[4];	outTriangles[2].sharedEdge[2] = false;
875
876	// log
877	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering triangle fan, " << outData.size() << " vertices." << tcu::TestLog::EndMessage;
878	for (int vtxNdx = 0; vtxNdx < (int)outData.size(); ++vtxNdx)
879	{
880		m_testCtx.getLog()
881			<< tcu::TestLog::Message
882			<< "\t" << outData[vtxNdx]
883			<< tcu::TestLog::EndMessage;
884	}
885}
886
887class LinesCase : public BaseLineCase
888{
889public:
890			LinesCase		(Context& context, const char* name, const char* desc, PrimitiveWideness wideness);
891
892	void	generateLines	(int iteration, std::vector<tcu::Vec4>& outData, std::vector<LineSceneSpec::SceneLine>& outLines);
893};
894
895LinesCase::LinesCase (Context& context, const char* name, const char* desc, PrimitiveWideness wideness)
896	: BaseLineCase(context, name, desc, GL_LINES, wideness)
897{
898}
899
900void LinesCase::generateLines (int iteration, std::vector<tcu::Vec4>& outData, std::vector<LineSceneSpec::SceneLine>& outLines)
901{
902	outData.resize(6);
903
904	switch (iteration)
905	{
906		case 0:
907			// \note: these values are chosen arbitrarily
908			outData[0] = tcu::Vec4( 0.01f,  0.0f, 0.0f, 1.0f);
909			outData[1] = tcu::Vec4( 0.5f,   0.2f, 0.0f, 1.0f);
910			outData[2] = tcu::Vec4( 0.46f,  0.3f, 0.0f, 1.0f);
911			outData[3] = tcu::Vec4(-0.3f,   0.2f, 0.0f, 1.0f);
912			outData[4] = tcu::Vec4(-1.5f,  -0.4f, 0.0f, 1.0f);
913			outData[5] = tcu::Vec4( 0.1f,   0.5f, 0.0f, 1.0f);
914			break;
915
916		case 1:
917			outData[0] = tcu::Vec4(-0.499f, 0.128f, 0.0f, 1.0f);
918			outData[1] = tcu::Vec4(-0.501f,  -0.3f, 0.0f, 1.0f);
919			outData[2] = tcu::Vec4(  0.11f,  -0.2f, 0.0f, 1.0f);
920			outData[3] = tcu::Vec4(  0.11f,   0.2f, 0.0f, 1.0f);
921			outData[4] = tcu::Vec4(  0.88f,   0.9f, 0.0f, 1.0f);
922			outData[5] = tcu::Vec4(  0.18f,  -0.2f, 0.0f, 1.0f);
923			break;
924
925		case 2:
926			outData[0] = tcu::Vec4( -0.9f, -0.3f, 0.0f, 1.0f);
927			outData[1] = tcu::Vec4(  1.1f, -0.9f, 0.0f, 1.0f);
928			outData[2] = tcu::Vec4(  0.7f, -0.1f, 0.0f, 1.0f);
929			outData[3] = tcu::Vec4( 0.11f,  0.2f, 0.0f, 1.0f);
930			outData[4] = tcu::Vec4( 0.88f,  0.7f, 0.0f, 1.0f);
931			outData[5] = tcu::Vec4(  0.8f, -0.7f, 0.0f, 1.0f);
932			break;
933	}
934
935	outLines.resize(3);
936	outLines[0].positions[0] = outData[0];
937	outLines[0].positions[1] = outData[1];
938	outLines[1].positions[0] = outData[2];
939	outLines[1].positions[1] = outData[3];
940	outLines[2].positions[0] = outData[4];
941	outLines[2].positions[1] = outData[5];
942
943	// log
944	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering " << outLines.size() << " lines(s): (width = " << m_lineWidth << ")" << tcu::TestLog::EndMessage;
945	for (int lineNdx = 0; lineNdx < (int)outLines.size(); ++lineNdx)
946	{
947		m_testCtx.getLog()
948			<< tcu::TestLog::Message
949			<< "Line " << (lineNdx+1) << ":"
950			<< "\n\t" << outLines[lineNdx].positions[0]
951			<< "\n\t" << outLines[lineNdx].positions[1]
952			<< tcu::TestLog::EndMessage;
953	}
954}
955
956class LineStripCase : public BaseLineCase
957{
958public:
959			LineStripCase	(Context& context, const char* name, const char* desc, PrimitiveWideness wideness);
960
961	void	generateLines	(int iteration, std::vector<tcu::Vec4>& outData, std::vector<LineSceneSpec::SceneLine>& outLines);
962};
963
964LineStripCase::LineStripCase (Context& context, const char* name, const char* desc, PrimitiveWideness wideness)
965	: BaseLineCase(context, name, desc, GL_LINE_STRIP, wideness)
966{
967}
968
969void LineStripCase::generateLines (int iteration, std::vector<tcu::Vec4>& outData, std::vector<LineSceneSpec::SceneLine>& outLines)
970{
971	outData.resize(4);
972
973	switch (iteration)
974	{
975		case 0:
976			// \note: these values are chosen arbitrarily
977			outData[0] = tcu::Vec4( 0.01f,  0.0f, 0.0f, 1.0f);
978			outData[1] = tcu::Vec4( 0.5f,   0.2f, 0.0f, 1.0f);
979			outData[2] = tcu::Vec4( 0.46f,  0.3f, 0.0f, 1.0f);
980			outData[3] = tcu::Vec4(-0.5f,   0.2f, 0.0f, 1.0f);
981			break;
982
983		case 1:
984			outData[0] = tcu::Vec4(-0.499f, 0.128f, 0.0f, 1.0f);
985			outData[1] = tcu::Vec4(-0.501f,  -0.3f, 0.0f, 1.0f);
986			outData[2] = tcu::Vec4(  0.11f,  -0.2f, 0.0f, 1.0f);
987			outData[3] = tcu::Vec4(  0.11f,   0.2f, 0.0f, 1.0f);
988			break;
989
990		case 2:
991			outData[0] = tcu::Vec4( -0.9f, -0.3f, 0.0f, 1.0f);
992			outData[1] = tcu::Vec4(  1.1f, -0.9f, 0.0f, 1.0f);
993			outData[2] = tcu::Vec4(  0.7f, -0.1f, 0.0f, 1.0f);
994			outData[3] = tcu::Vec4( 0.11f,  0.2f, 0.0f, 1.0f);
995			break;
996	}
997
998	outLines.resize(3);
999	outLines[0].positions[0] = outData[0];
1000	outLines[0].positions[1] = outData[1];
1001	outLines[1].positions[0] = outData[1];
1002	outLines[1].positions[1] = outData[2];
1003	outLines[2].positions[0] = outData[2];
1004	outLines[2].positions[1] = outData[3];
1005
1006	// log
1007	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering line strip, width = " << m_lineWidth << ", " << outData.size() << " vertices." << tcu::TestLog::EndMessage;
1008	for (int vtxNdx = 0; vtxNdx < (int)outData.size(); ++vtxNdx)
1009	{
1010		m_testCtx.getLog()
1011			<< tcu::TestLog::Message
1012			<< "\t" << outData[vtxNdx]
1013			<< tcu::TestLog::EndMessage;
1014	}
1015}
1016
1017class LineLoopCase : public BaseLineCase
1018{
1019public:
1020			LineLoopCase	(Context& context, const char* name, const char* desc, PrimitiveWideness wideness);
1021
1022	void	generateLines	(int iteration, std::vector<tcu::Vec4>& outData, std::vector<LineSceneSpec::SceneLine>& outLines);
1023};
1024
1025LineLoopCase::LineLoopCase (Context& context, const char* name, const char* desc, PrimitiveWideness wideness)
1026	: BaseLineCase(context, name, desc, GL_LINE_LOOP, wideness)
1027{
1028}
1029
1030void LineLoopCase::generateLines (int iteration, std::vector<tcu::Vec4>& outData, std::vector<LineSceneSpec::SceneLine>& outLines)
1031{
1032	outData.resize(4);
1033
1034	switch (iteration)
1035	{
1036		case 0:
1037			// \note: these values are chosen arbitrarily
1038			outData[0] = tcu::Vec4( 0.01f,  0.0f, 0.0f, 1.0f);
1039			outData[1] = tcu::Vec4( 0.5f,   0.2f, 0.0f, 1.0f);
1040			outData[2] = tcu::Vec4( 0.46f,  0.3f, 0.0f, 1.0f);
1041			outData[3] = tcu::Vec4(-0.5f,   0.2f, 0.0f, 1.0f);
1042			break;
1043
1044		case 1:
1045			outData[0] = tcu::Vec4(-0.499f, 0.128f, 0.0f, 1.0f);
1046			outData[1] = tcu::Vec4(-0.501f,  -0.3f, 0.0f, 1.0f);
1047			outData[2] = tcu::Vec4(  0.11f,  -0.2f, 0.0f, 1.0f);
1048			outData[3] = tcu::Vec4(  0.11f,   0.2f, 0.0f, 1.0f);
1049			break;
1050
1051		case 2:
1052			outData[0] = tcu::Vec4( -0.9f, -0.3f, 0.0f, 1.0f);
1053			outData[1] = tcu::Vec4(  1.1f, -0.9f, 0.0f, 1.0f);
1054			outData[2] = tcu::Vec4(  0.7f, -0.1f, 0.0f, 1.0f);
1055			outData[3] = tcu::Vec4( 0.11f,  0.2f, 0.0f, 1.0f);
1056			break;
1057	}
1058
1059	outLines.resize(4);
1060	outLines[0].positions[0] = outData[0];
1061	outLines[0].positions[1] = outData[1];
1062	outLines[1].positions[0] = outData[1];
1063	outLines[1].positions[1] = outData[2];
1064	outLines[2].positions[0] = outData[2];
1065	outLines[2].positions[1] = outData[3];
1066	outLines[3].positions[0] = outData[3];
1067	outLines[3].positions[1] = outData[0];
1068
1069	// log
1070	m_testCtx.getLog() << tcu::TestLog::Message << "Rendering line loop, width = " << m_lineWidth << ", " << outData.size() << " vertices." << tcu::TestLog::EndMessage;
1071	for (int vtxNdx = 0; vtxNdx < (int)outData.size(); ++vtxNdx)
1072	{
1073		m_testCtx.getLog()
1074			<< tcu::TestLog::Message
1075			<< "\t" << outData[vtxNdx]
1076			<< tcu::TestLog::EndMessage;
1077	}
1078}
1079
1080class FillRuleCase : public BaseRenderingCase
1081{
1082public:
1083	enum FillRuleCaseType
1084	{
1085		FILLRULECASE_BASIC = 0,
1086		FILLRULECASE_REVERSED,
1087		FILLRULECASE_CLIPPED_FULL,
1088		FILLRULECASE_CLIPPED_PARTIAL,
1089		FILLRULECASE_PROJECTED,
1090
1091		FILLRULECASE_LAST
1092	};
1093
1094							FillRuleCase		(Context& ctx, const char* name, const char* desc, FillRuleCaseType type);
1095							~FillRuleCase		(void);
1096	IterateResult			iterate				(void);
1097
1098private:
1099	int						getRenderSize		(FillRuleCase::FillRuleCaseType type) const;
1100	int						getNumIterations	(FillRuleCase::FillRuleCaseType type) const;
1101	void					generateTriangles	(int iteration, std::vector<tcu::Vec4>& outData) const;
1102
1103	const FillRuleCaseType	m_caseType;
1104	int						m_iteration;
1105	const int				m_iterationCount;
1106	bool					m_allIterationsPassed;
1107
1108};
1109
1110FillRuleCase::FillRuleCase (Context& ctx, const char* name, const char* desc, FillRuleCaseType type)
1111	: BaseRenderingCase		(ctx, name, desc, getRenderSize(type))
1112	, m_caseType			(type)
1113	, m_iteration			(0)
1114	, m_iterationCount		(getNumIterations(type))
1115	, m_allIterationsPassed	(true)
1116{
1117	DE_ASSERT(type < FILLRULECASE_LAST);
1118}
1119
1120FillRuleCase::~FillRuleCase (void)
1121{
1122	deinit();
1123}
1124
1125FillRuleCase::IterateResult FillRuleCase::iterate (void)
1126{
1127	const std::string						iterationDescription	= "Test iteration " + de::toString(m_iteration+1) + " / " + de::toString(m_iterationCount);
1128	const tcu::ScopedLogSection				section					(m_testCtx.getLog(), iterationDescription, iterationDescription);
1129	const int								thresholdRed			= 1 << (8 - m_context.getRenderTarget().getPixelFormat().redBits);
1130	const int								thresholdGreen			= 1 << (8 - m_context.getRenderTarget().getPixelFormat().greenBits);
1131	const int								thresholdBlue			= 1 << (8 - m_context.getRenderTarget().getPixelFormat().blueBits);
1132	tcu::Surface							resultImage				(m_renderSize, m_renderSize);
1133	std::vector<tcu::Vec4>					drawBuffer;
1134	bool									imageShown				= false;
1135
1136	generateTriangles(m_iteration, drawBuffer);
1137
1138	// draw image
1139	{
1140		const glw::Functions&			gl				= m_context.getRenderContext().getFunctions();
1141		const std::vector<tcu::Vec4>	colorBuffer		(drawBuffer.size(), tcu::Vec4(0.5f, 0.5f, 0.5f, 1.0f));
1142
1143		m_testCtx.getLog() << tcu::TestLog::Message << "Drawing gray triangles with shared edges.\nEnabling additive blending to detect overlapping fragments." << tcu::TestLog::EndMessage;
1144
1145		gl.enable(GL_BLEND);
1146		gl.blendEquation(GL_FUNC_ADD);
1147		gl.blendFunc(GL_ONE, GL_ONE);
1148		drawPrimitives(resultImage, drawBuffer, colorBuffer, GL_TRIANGLES);
1149	}
1150
1151	// verify no overdraw
1152	{
1153		const tcu::RGBA	triangleColor	= tcu::RGBA(127, 127, 127, 255);
1154		bool			overdraw		= false;
1155
1156		m_testCtx.getLog() << tcu::TestLog::Message << "Verifying result." << tcu::TestLog::EndMessage;
1157
1158		for (int y = 0; y < resultImage.getHeight(); ++y)
1159		for (int x = 0; x < resultImage.getWidth();  ++x)
1160		{
1161			const tcu::RGBA color = resultImage.getPixel(x, y);
1162
1163			// color values are greater than triangle color? Allow lower values for multisampled edges and background.
1164			if ((color.getRed()   - triangleColor.getRed())   > thresholdRed   ||
1165				(color.getGreen() - triangleColor.getGreen()) > thresholdGreen ||
1166				(color.getBlue()  - triangleColor.getBlue())  > thresholdBlue)
1167				overdraw = true;
1168		}
1169
1170		// results
1171		if (!overdraw)
1172			m_testCtx.getLog() << tcu::TestLog::Message << "No overlapping fragments detected." << tcu::TestLog::EndMessage;
1173		else
1174		{
1175			m_testCtx.getLog()	<< tcu::TestLog::Message << "Overlapping fragments detected, image is not valid." << tcu::TestLog::EndMessage;
1176			m_testCtx.getLog()	<< tcu::TestLog::ImageSet("Result of rendering", "Result of rendering")
1177								<< tcu::TestLog::Image("Result", "Result", resultImage)
1178								<< tcu::TestLog::EndImageSet;
1179
1180			imageShown = true;
1181			m_allIterationsPassed = false;
1182		}
1183	}
1184
1185	// verify no missing fragments in the full viewport case
1186	if (m_caseType == FILLRULECASE_CLIPPED_FULL)
1187	{
1188		bool missingFragments = false;
1189
1190		m_testCtx.getLog() << tcu::TestLog::Message << "Searching missing fragments." << tcu::TestLog::EndMessage;
1191
1192		for (int y = 0; y < resultImage.getHeight(); ++y)
1193		for (int x = 0; x < resultImage.getWidth();  ++x)
1194		{
1195			const tcu::RGBA color = resultImage.getPixel(x, y);
1196
1197			// black? (background)
1198			if (color.getRed()   <= thresholdRed   ||
1199				color.getGreen() <= thresholdGreen ||
1200				color.getBlue()  <= thresholdBlue)
1201				missingFragments = true;
1202		}
1203
1204		// results
1205		if (!missingFragments)
1206			m_testCtx.getLog() << tcu::TestLog::Message << "No missing fragments detected." << tcu::TestLog::EndMessage;
1207		else
1208		{
1209			m_testCtx.getLog()	<< tcu::TestLog::Message << "Missing fragments detected, image is not valid." << tcu::TestLog::EndMessage;
1210
1211			if (!imageShown)
1212			{
1213				m_testCtx.getLog()	<< tcu::TestLog::ImageSet("Result of rendering", "Result of rendering")
1214									<< tcu::TestLog::Image("Result", "Result", resultImage)
1215									<< tcu::TestLog::EndImageSet;
1216			}
1217
1218			m_allIterationsPassed = false;
1219		}
1220	}
1221
1222	// result
1223	if (++m_iteration == m_iterationCount)
1224	{
1225		if (m_allIterationsPassed)
1226			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1227		else
1228			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Found invalid pixels");
1229
1230		return STOP;
1231	}
1232	else
1233		return CONTINUE;
1234}
1235
1236int FillRuleCase::getRenderSize (FillRuleCase::FillRuleCaseType type) const
1237{
1238	if (type == FILLRULECASE_CLIPPED_FULL || type == FILLRULECASE_CLIPPED_PARTIAL)
1239		return 64;
1240	else
1241		return 256;
1242}
1243
1244int FillRuleCase::getNumIterations (FillRuleCase::FillRuleCaseType type) const
1245{
1246	if (type == FILLRULECASE_CLIPPED_FULL || type == FILLRULECASE_CLIPPED_PARTIAL)
1247		return 15;
1248	else
1249		return 2;
1250}
1251
1252void FillRuleCase::generateTriangles (int iteration, std::vector<tcu::Vec4>& outData) const
1253{
1254	switch (m_caseType)
1255	{
1256		case FILLRULECASE_BASIC:
1257		case FILLRULECASE_REVERSED:
1258		case FILLRULECASE_PROJECTED:
1259		{
1260			const int	numRows		= 4;
1261			const int	numColumns	= 4;
1262			const float	quadSide	= 0.15f;
1263			de::Random	rnd			(0xabcd);
1264
1265			outData.resize(6 * numRows * numColumns);
1266
1267			for (int col = 0; col < numColumns; ++col)
1268			for (int row = 0; row < numRows;    ++row)
1269			{
1270				const tcu::Vec2 center		= tcu::Vec2(((float)row + 0.5f) / (float)numRows * 2.0f - 1.0f, ((float)col + 0.5f) / (float)numColumns * 2.0f - 1.0f);
1271				const float		rotation	= float(iteration * numColumns * numRows + col * numRows + row) / (float)(m_iterationCount * numColumns * numRows) * DE_PI / 2.0f;
1272				const tcu::Vec2 sideH		= quadSide * tcu::Vec2(deFloatCos(rotation), deFloatSin(rotation));
1273				const tcu::Vec2 sideV		= tcu::Vec2(sideH.y(), -sideH.x());
1274				const tcu::Vec2 quad[4]		=
1275				{
1276					center + sideH + sideV,
1277					center + sideH - sideV,
1278					center - sideH - sideV,
1279					center - sideH + sideV,
1280				};
1281
1282				if (m_caseType == FILLRULECASE_BASIC)
1283				{
1284					outData[6 * (col * numRows + row) + 0] = tcu::Vec4(quad[0].x(), quad[0].y(), 0.0f, 1.0f);
1285					outData[6 * (col * numRows + row) + 1] = tcu::Vec4(quad[1].x(), quad[1].y(), 0.0f, 1.0f);
1286					outData[6 * (col * numRows + row) + 2] = tcu::Vec4(quad[2].x(), quad[2].y(), 0.0f, 1.0f);
1287					outData[6 * (col * numRows + row) + 3] = tcu::Vec4(quad[2].x(), quad[2].y(), 0.0f, 1.0f);
1288					outData[6 * (col * numRows + row) + 4] = tcu::Vec4(quad[0].x(), quad[0].y(), 0.0f, 1.0f);
1289					outData[6 * (col * numRows + row) + 5] = tcu::Vec4(quad[3].x(), quad[3].y(), 0.0f, 1.0f);
1290				}
1291				else if (m_caseType == FILLRULECASE_REVERSED)
1292				{
1293					outData[6 * (col * numRows + row) + 0] = tcu::Vec4(quad[0].x(), quad[0].y(), 0.0f, 1.0f);
1294					outData[6 * (col * numRows + row) + 1] = tcu::Vec4(quad[1].x(), quad[1].y(), 0.0f, 1.0f);
1295					outData[6 * (col * numRows + row) + 2] = tcu::Vec4(quad[2].x(), quad[2].y(), 0.0f, 1.0f);
1296					outData[6 * (col * numRows + row) + 3] = tcu::Vec4(quad[0].x(), quad[0].y(), 0.0f, 1.0f);
1297					outData[6 * (col * numRows + row) + 4] = tcu::Vec4(quad[2].x(), quad[2].y(), 0.0f, 1.0f);
1298					outData[6 * (col * numRows + row) + 5] = tcu::Vec4(quad[3].x(), quad[3].y(), 0.0f, 1.0f);
1299				}
1300				else if (m_caseType == FILLRULECASE_PROJECTED)
1301				{
1302					const float w0 = rnd.getFloat(0.1f, 4.0f);
1303					const float w1 = rnd.getFloat(0.1f, 4.0f);
1304					const float w2 = rnd.getFloat(0.1f, 4.0f);
1305					const float w3 = rnd.getFloat(0.1f, 4.0f);
1306
1307					outData[6 * (col * numRows + row) + 0] = tcu::Vec4(quad[0].x() * w0, quad[0].y() * w0, 0.0f, w0);
1308					outData[6 * (col * numRows + row) + 1] = tcu::Vec4(quad[1].x() * w1, quad[1].y() * w1, 0.0f, w1);
1309					outData[6 * (col * numRows + row) + 2] = tcu::Vec4(quad[2].x() * w2, quad[2].y() * w2, 0.0f, w2);
1310					outData[6 * (col * numRows + row) + 3] = tcu::Vec4(quad[2].x() * w2, quad[2].y() * w2, 0.0f, w2);
1311					outData[6 * (col * numRows + row) + 4] = tcu::Vec4(quad[0].x() * w0, quad[0].y() * w0, 0.0f, w0);
1312					outData[6 * (col * numRows + row) + 5] = tcu::Vec4(quad[3].x() * w3, quad[3].y() * w3, 0.0f, w3);
1313				}
1314				else
1315					DE_ASSERT(DE_FALSE);
1316			}
1317
1318			break;
1319		}
1320
1321		case FILLRULECASE_CLIPPED_PARTIAL:
1322		case FILLRULECASE_CLIPPED_FULL:
1323		{
1324			const float		quadSide	= (m_caseType == FILLRULECASE_CLIPPED_PARTIAL) ? (1.0f) : (2.0f);
1325			const tcu::Vec2 center		= (m_caseType == FILLRULECASE_CLIPPED_PARTIAL) ? (tcu::Vec2(0.5f, 0.5f)) : (tcu::Vec2(0.0f, 0.0f));
1326			const float		rotation	= (float)(iteration) / (float)(m_iterationCount - 1) * DE_PI / 2.0f;
1327			const tcu::Vec2 sideH		= quadSide * tcu::Vec2(deFloatCos(rotation), deFloatSin(rotation));
1328			const tcu::Vec2 sideV		= tcu::Vec2(sideH.y(), -sideH.x());
1329			const tcu::Vec2 quad[4]		=
1330			{
1331				center + sideH + sideV,
1332				center + sideH - sideV,
1333				center - sideH - sideV,
1334				center - sideH + sideV,
1335			};
1336
1337			outData.resize(6);
1338			outData[0] = tcu::Vec4(quad[0].x(), quad[0].y(), 0.0f, 1.0f);
1339			outData[1] = tcu::Vec4(quad[1].x(), quad[1].y(), 0.0f, 1.0f);
1340			outData[2] = tcu::Vec4(quad[2].x(), quad[2].y(), 0.0f, 1.0f);
1341			outData[3] = tcu::Vec4(quad[2].x(), quad[2].y(), 0.0f, 1.0f);
1342			outData[4] = tcu::Vec4(quad[0].x(), quad[0].y(), 0.0f, 1.0f);
1343			outData[5] = tcu::Vec4(quad[3].x(), quad[3].y(), 0.0f, 1.0f);
1344			break;
1345		}
1346
1347		default:
1348			DE_ASSERT(DE_FALSE);
1349	}
1350}
1351
1352class CullingTest : public BaseRenderingCase
1353{
1354public:
1355						CullingTest			(Context& ctx, const char* name, const char* desc, glw::GLenum cullMode, glw::GLenum primitive, glw::GLenum faceOrder);
1356						~CullingTest		(void);
1357	IterateResult		iterate				(void);
1358
1359private:
1360	void				generateVertices	(std::vector<tcu::Vec4>& outData) const;
1361	void				extractTriangles	(std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles, const std::vector<tcu::Vec4>& vertices) const;
1362	bool				triangleOrder		(const tcu::Vec4& v0, const tcu::Vec4& v1, const tcu::Vec4& v2) const;
1363
1364	const glw::GLenum	m_cullMode;
1365	const glw::GLenum	m_primitive;
1366	const glw::GLenum	m_faceOrder;
1367};
1368
1369CullingTest::CullingTest (Context& ctx, const char* name, const char* desc, glw::GLenum cullMode, glw::GLenum primitive, glw::GLenum faceOrder)
1370	: BaseRenderingCase	(ctx, name, desc)
1371	, m_cullMode		(cullMode)
1372	, m_primitive		(primitive)
1373	, m_faceOrder		(faceOrder)
1374{
1375}
1376
1377CullingTest::~CullingTest (void)
1378{
1379}
1380
1381CullingTest::IterateResult CullingTest::iterate (void)
1382{
1383	tcu::Surface									resultImage(m_renderSize, m_renderSize);
1384	std::vector<tcu::Vec4>							drawBuffer;
1385	std::vector<TriangleSceneSpec::SceneTriangle>	triangles;
1386
1387	// generate scene
1388	generateVertices(drawBuffer);
1389	extractTriangles(triangles, drawBuffer);
1390
1391	// draw image
1392	{
1393		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1394
1395		gl.enable(GL_CULL_FACE);
1396		gl.cullFace(m_cullMode);
1397		gl.frontFace(m_faceOrder);
1398
1399		m_testCtx.getLog() << tcu::TestLog::Message << "Setting front face to " << glu::getWindingName(m_faceOrder) << tcu::TestLog::EndMessage;
1400		m_testCtx.getLog() << tcu::TestLog::Message << "Setting cull face to " << glu::getFaceName(m_cullMode) << tcu::TestLog::EndMessage;
1401		m_testCtx.getLog() << tcu::TestLog::Message << "Drawing test pattern (" << glu::getPrimitiveTypeName(m_primitive) << ")" << tcu::TestLog::EndMessage;
1402
1403		drawPrimitives(resultImage, drawBuffer, m_primitive);
1404	}
1405
1406	// compare
1407	{
1408		RasterizationArguments	args;
1409		TriangleSceneSpec		scene;
1410
1411		args.numSamples		= m_numSamples;
1412		args.subpixelBits	= m_subpixelBits;
1413		args.redBits		= m_context.getRenderTarget().getPixelFormat().redBits;
1414		args.greenBits		= m_context.getRenderTarget().getPixelFormat().greenBits;
1415		args.blueBits		= m_context.getRenderTarget().getPixelFormat().blueBits;
1416
1417		scene.triangles.swap(triangles);
1418
1419		if (verifyTriangleGroupRasterization(resultImage, scene, args, m_testCtx.getLog(), tcu::VERIFICATIONMODE_WEAK))
1420			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1421		else
1422			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Incorrect rendering");
1423	}
1424
1425	return STOP;
1426}
1427
1428void CullingTest::generateVertices (std::vector<tcu::Vec4>& outData) const
1429{
1430	de::Random rnd(543210);
1431
1432	outData.resize(6);
1433	for (int vtxNdx = 0; vtxNdx < (int)outData.size(); ++vtxNdx)
1434	{
1435		outData[vtxNdx].x() = rnd.getFloat(-0.9f, 0.9f);
1436		outData[vtxNdx].y() = rnd.getFloat(-0.9f, 0.9f);
1437		outData[vtxNdx].z() = 0.0f;
1438		outData[vtxNdx].w() = 1.0f;
1439	}
1440}
1441
1442void CullingTest::extractTriangles (std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles, const std::vector<tcu::Vec4>& vertices) const
1443{
1444	const bool cullDirection = (m_cullMode == GL_FRONT) ^ (m_faceOrder == GL_CCW);
1445
1446	// No triangles
1447	if (m_cullMode == GL_FRONT_AND_BACK)
1448		return;
1449
1450	switch (m_primitive)
1451	{
1452		case GL_TRIANGLES:
1453		{
1454			for (int vtxNdx = 0; vtxNdx < (int)vertices.size() - 2; vtxNdx += 3)
1455			{
1456				const tcu::Vec4& v0 = vertices[vtxNdx + 0];
1457				const tcu::Vec4& v1 = vertices[vtxNdx + 1];
1458				const tcu::Vec4& v2 = vertices[vtxNdx + 2];
1459
1460				if (triangleOrder(v0, v1, v2) != cullDirection)
1461				{
1462					TriangleSceneSpec::SceneTriangle tri;
1463					tri.positions[0] = v0;	tri.sharedEdge[0] = false;
1464					tri.positions[1] = v1;	tri.sharedEdge[1] = false;
1465					tri.positions[2] = v2;	tri.sharedEdge[2] = false;
1466
1467					outTriangles.push_back(tri);
1468				}
1469			}
1470			break;
1471		}
1472
1473		case GL_TRIANGLE_STRIP:
1474		{
1475			for (int vtxNdx = 0; vtxNdx < (int)vertices.size() - 2; ++vtxNdx)
1476			{
1477				const tcu::Vec4& v0 = vertices[vtxNdx + 0];
1478				const tcu::Vec4& v1 = vertices[vtxNdx + 1];
1479				const tcu::Vec4& v2 = vertices[vtxNdx + 2];
1480
1481				if (triangleOrder(v0, v1, v2) != (cullDirection ^ (vtxNdx % 2 != 0)))
1482				{
1483					TriangleSceneSpec::SceneTriangle tri;
1484					tri.positions[0] = v0;	tri.sharedEdge[0] = false;
1485					tri.positions[1] = v1;	tri.sharedEdge[1] = false;
1486					tri.positions[2] = v2;	tri.sharedEdge[2] = false;
1487
1488					outTriangles.push_back(tri);
1489				}
1490			}
1491			break;
1492		}
1493
1494		case GL_TRIANGLE_FAN:
1495		{
1496			for (int vtxNdx = 1; vtxNdx < (int)vertices.size() - 1; ++vtxNdx)
1497			{
1498				const tcu::Vec4& v0 = vertices[0];
1499				const tcu::Vec4& v1 = vertices[vtxNdx + 0];
1500				const tcu::Vec4& v2 = vertices[vtxNdx + 1];
1501
1502				if (triangleOrder(v0, v1, v2) != cullDirection)
1503				{
1504					TriangleSceneSpec::SceneTriangle tri;
1505					tri.positions[0] = v0;	tri.sharedEdge[0] = false;
1506					tri.positions[1] = v1;	tri.sharedEdge[1] = false;
1507					tri.positions[2] = v2;	tri.sharedEdge[2] = false;
1508
1509					outTriangles.push_back(tri);
1510				}
1511			}
1512			break;
1513		}
1514
1515		default:
1516			DE_ASSERT(false);
1517	}
1518}
1519
1520bool CullingTest::triangleOrder (const tcu::Vec4& v0, const tcu::Vec4& v1, const tcu::Vec4& v2) const
1521{
1522	const tcu::Vec2 s0 = v0.swizzle(0, 1) / v0.w();
1523	const tcu::Vec2 s1 = v1.swizzle(0, 1) / v1.w();
1524	const tcu::Vec2 s2 = v2.swizzle(0, 1) / v2.w();
1525
1526	// cross
1527	return ((s1.x() - s0.x()) * (s2.y() - s0.y()) - (s2.x() - s0.x()) * (s1.y() - s0.y())) < 0;
1528}
1529
1530class TriangleInterpolationTest : public BaseRenderingCase
1531{
1532public:
1533						TriangleInterpolationTest	(Context& ctx, const char* name, const char* desc, glw::GLenum primitive, int flags);
1534						~TriangleInterpolationTest	(void);
1535	IterateResult		iterate						(void);
1536
1537private:
1538	void				generateVertices			(int iteration, std::vector<tcu::Vec4>& outVertices, std::vector<tcu::Vec4>& outColors) const;
1539	void				extractTriangles			(std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles, const std::vector<tcu::Vec4>& vertices, const std::vector<tcu::Vec4>& colors) const;
1540
1541	const glw::GLenum	m_primitive;
1542	const bool			m_projective;
1543	const int			m_iterationCount;
1544
1545	int					m_iteration;
1546	bool				m_allIterationsPassed;
1547};
1548
1549TriangleInterpolationTest::TriangleInterpolationTest (Context& ctx, const char* name, const char* desc, glw::GLenum primitive, int flags)
1550	: BaseRenderingCase		(ctx, name, desc)
1551	, m_primitive			(primitive)
1552	, m_projective			((flags & INTERPOLATIONFLAGS_PROJECTED) != 0)
1553	, m_iterationCount		(3)
1554	, m_iteration			(0)
1555	, m_allIterationsPassed	(true)
1556{
1557}
1558
1559TriangleInterpolationTest::~TriangleInterpolationTest (void)
1560{
1561	deinit();
1562}
1563
1564TriangleInterpolationTest::IterateResult TriangleInterpolationTest::iterate (void)
1565{
1566	const std::string								iterationDescription	= "Test iteration " + de::toString(m_iteration+1) + " / " + de::toString(m_iterationCount);
1567	const tcu::ScopedLogSection						section					(m_testCtx.getLog(), "Iteration" + de::toString(m_iteration+1), iterationDescription);
1568	tcu::Surface									resultImage				(m_renderSize, m_renderSize);
1569	std::vector<tcu::Vec4>							drawBuffer;
1570	std::vector<tcu::Vec4>							colorBuffer;
1571	std::vector<TriangleSceneSpec::SceneTriangle>	triangles;
1572
1573	// generate scene
1574	generateVertices(m_iteration, drawBuffer, colorBuffer);
1575	extractTriangles(triangles, drawBuffer, colorBuffer);
1576
1577	// log
1578	{
1579		m_testCtx.getLog() << tcu::TestLog::Message << "Generated vertices:" << tcu::TestLog::EndMessage;
1580		for (int vtxNdx = 0; vtxNdx < (int)drawBuffer.size(); ++vtxNdx)
1581			m_testCtx.getLog() << tcu::TestLog::Message << "\t" << drawBuffer[vtxNdx] << ",\tcolor= " << colorBuffer[vtxNdx] << tcu::TestLog::EndMessage;
1582	}
1583
1584	// draw image
1585	drawPrimitives(resultImage, drawBuffer, colorBuffer, m_primitive);
1586
1587	// compare
1588	{
1589		RasterizationArguments	args;
1590		TriangleSceneSpec		scene;
1591
1592		args.numSamples		= m_numSamples;
1593		args.subpixelBits	= m_subpixelBits;
1594		args.redBits		= m_context.getRenderTarget().getPixelFormat().redBits;
1595		args.greenBits		= m_context.getRenderTarget().getPixelFormat().greenBits;
1596		args.blueBits		= m_context.getRenderTarget().getPixelFormat().blueBits;
1597
1598		scene.triangles.swap(triangles);
1599
1600		if (!verifyTriangleGroupInterpolation(resultImage, scene, args, m_testCtx.getLog()))
1601			m_allIterationsPassed = false;
1602	}
1603
1604	// result
1605	if (++m_iteration == m_iterationCount)
1606	{
1607		if (m_allIterationsPassed)
1608			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1609		else
1610			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Found invalid pixel values");
1611
1612		return STOP;
1613	}
1614	else
1615		return CONTINUE;
1616}
1617
1618void TriangleInterpolationTest::generateVertices (int iteration, std::vector<tcu::Vec4>& outVertices, std::vector<tcu::Vec4>& outColors) const
1619{
1620	// use only red, green and blue
1621	const tcu::Vec4 colors[] =
1622	{
1623		tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f),
1624		tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f),
1625		tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f),
1626	};
1627
1628	de::Random rnd(123 + iteration * 1000 + (int)m_primitive);
1629
1630	outVertices.resize(6);
1631	outColors.resize(6);
1632
1633	for (int vtxNdx = 0; vtxNdx < (int)outVertices.size(); ++vtxNdx)
1634	{
1635		outVertices[vtxNdx].x() = rnd.getFloat(-0.9f, 0.9f);
1636		outVertices[vtxNdx].y() = rnd.getFloat(-0.9f, 0.9f);
1637		outVertices[vtxNdx].z() = 0.0f;
1638
1639		if (!m_projective)
1640			outVertices[vtxNdx].w() = 1.0f;
1641		else
1642		{
1643			const float w = rnd.getFloat(0.2f, 4.0f);
1644
1645			outVertices[vtxNdx].x() *= w;
1646			outVertices[vtxNdx].y() *= w;
1647			outVertices[vtxNdx].z() *= w;
1648			outVertices[vtxNdx].w() = w;
1649		}
1650
1651		outColors[vtxNdx] = colors[vtxNdx % DE_LENGTH_OF_ARRAY(colors)];
1652	}
1653}
1654
1655void TriangleInterpolationTest::extractTriangles (std::vector<TriangleSceneSpec::SceneTriangle>& outTriangles, const std::vector<tcu::Vec4>& vertices, const std::vector<tcu::Vec4>& colors) const
1656{
1657	switch (m_primitive)
1658	{
1659		case GL_TRIANGLES:
1660		{
1661			for (int vtxNdx = 0; vtxNdx < (int)vertices.size() - 2; vtxNdx += 3)
1662			{
1663				TriangleSceneSpec::SceneTriangle tri;
1664				tri.positions[0]	= vertices[vtxNdx + 0];
1665				tri.positions[1]	= vertices[vtxNdx + 1];
1666				tri.positions[2]	= vertices[vtxNdx + 2];
1667				tri.sharedEdge[0]	= false;
1668				tri.sharedEdge[1]	= false;
1669				tri.sharedEdge[2]	= false;
1670
1671				tri.colors[0] = colors[vtxNdx + 0];
1672				tri.colors[1] = colors[vtxNdx + 1];
1673				tri.colors[2] = colors[vtxNdx + 2];
1674
1675				outTriangles.push_back(tri);
1676			}
1677			break;
1678		}
1679
1680		case GL_TRIANGLE_STRIP:
1681		{
1682			for (int vtxNdx = 0; vtxNdx < (int)vertices.size() - 2; ++vtxNdx)
1683			{
1684				TriangleSceneSpec::SceneTriangle tri;
1685				tri.positions[0]	= vertices[vtxNdx + 0];
1686				tri.positions[1]	= vertices[vtxNdx + 1];
1687				tri.positions[2]	= vertices[vtxNdx + 2];
1688				tri.sharedEdge[0]	= false;
1689				tri.sharedEdge[1]	= false;
1690				tri.sharedEdge[2]	= false;
1691
1692				tri.colors[0] = colors[vtxNdx + 0];
1693				tri.colors[1] = colors[vtxNdx + 1];
1694				tri.colors[2] = colors[vtxNdx + 2];
1695
1696				outTriangles.push_back(tri);
1697			}
1698			break;
1699		}
1700
1701		case GL_TRIANGLE_FAN:
1702		{
1703			for (int vtxNdx = 1; vtxNdx < (int)vertices.size() - 1; ++vtxNdx)
1704			{
1705				TriangleSceneSpec::SceneTriangle tri;
1706				tri.positions[0]	= vertices[0];
1707				tri.positions[1]	= vertices[vtxNdx + 0];
1708				tri.positions[2]	= vertices[vtxNdx + 1];
1709				tri.sharedEdge[0]	= false;
1710				tri.sharedEdge[1]	= false;
1711				tri.sharedEdge[2]	= false;
1712
1713				tri.colors[0] = colors[0];
1714				tri.colors[1] = colors[vtxNdx + 0];
1715				tri.colors[2] = colors[vtxNdx + 1];
1716
1717				outTriangles.push_back(tri);
1718			}
1719			break;
1720		}
1721
1722		default:
1723			DE_ASSERT(false);
1724	}
1725}
1726
1727class LineInterpolationTest : public BaseRenderingCase
1728{
1729public:
1730							LineInterpolationTest	(Context& ctx, const char* name, const char* desc, glw::GLenum primitive, int flags, float lineWidth);
1731							~LineInterpolationTest	(void);
1732	IterateResult			iterate					(void);
1733
1734private:
1735	void					generateVertices		(int iteration, std::vector<tcu::Vec4>& outVertices, std::vector<tcu::Vec4>& outColors) const;
1736	void					extractLines			(std::vector<LineSceneSpec::SceneLine>& outLines, const std::vector<tcu::Vec4>& vertices, const std::vector<tcu::Vec4>& colors) const;
1737
1738	const glw::GLenum		m_primitive;
1739	const bool				m_projective;
1740	const int				m_iterationCount;
1741
1742	int						m_iteration;
1743	tcu::ResultCollector	m_result;
1744};
1745
1746LineInterpolationTest::LineInterpolationTest (Context& ctx, const char* name, const char* desc, glw::GLenum primitive, int flags, float lineWidth)
1747	: BaseRenderingCase		(ctx, name, desc)
1748	, m_primitive			(primitive)
1749	, m_projective			((flags & INTERPOLATIONFLAGS_PROJECTED) != 0)
1750	, m_iterationCount		(3)
1751	, m_iteration			(0)
1752{
1753	m_lineWidth = lineWidth;
1754}
1755
1756LineInterpolationTest::~LineInterpolationTest (void)
1757{
1758	deinit();
1759}
1760
1761LineInterpolationTest::IterateResult LineInterpolationTest::iterate (void)
1762{
1763	const std::string						iterationDescription	= "Test iteration " + de::toString(m_iteration+1) + " / " + de::toString(m_iterationCount);
1764	const tcu::ScopedLogSection				section					(m_testCtx.getLog(), "Iteration" + de::toString(m_iteration+1), iterationDescription);
1765	tcu::Surface							resultImage				(m_renderSize, m_renderSize);
1766	std::vector<tcu::Vec4>					drawBuffer;
1767	std::vector<tcu::Vec4>					colorBuffer;
1768	std::vector<LineSceneSpec::SceneLine>	lines;
1769
1770	// generate scene
1771	generateVertices(m_iteration, drawBuffer, colorBuffer);
1772	extractLines(lines, drawBuffer, colorBuffer);
1773
1774	// log
1775	{
1776		m_testCtx.getLog() << tcu::TestLog::Message << "Generated vertices:" << tcu::TestLog::EndMessage;
1777		for (int vtxNdx = 0; vtxNdx < (int)drawBuffer.size(); ++vtxNdx)
1778			m_testCtx.getLog() << tcu::TestLog::Message << "\t" << drawBuffer[vtxNdx] << ",\tcolor= " << colorBuffer[vtxNdx] << tcu::TestLog::EndMessage;
1779	}
1780
1781	// draw image
1782	drawPrimitives(resultImage, drawBuffer, colorBuffer, m_primitive);
1783
1784	// compare
1785	{
1786		RasterizationArguments	args;
1787		LineSceneSpec			scene;
1788		LineInterpolationMethod	iterationResult;
1789
1790		args.numSamples		= m_numSamples;
1791		args.subpixelBits	= m_subpixelBits;
1792		args.redBits		= m_context.getRenderTarget().getPixelFormat().redBits;
1793		args.greenBits		= m_context.getRenderTarget().getPixelFormat().greenBits;
1794		args.blueBits		= m_context.getRenderTarget().getPixelFormat().blueBits;
1795
1796		scene.lines.swap(lines);
1797		scene.lineWidth = m_lineWidth;
1798
1799		iterationResult = verifyLineGroupInterpolation(resultImage, scene, args, m_testCtx.getLog());
1800		switch (iterationResult)
1801		{
1802			case tcu::LINEINTERPOLATION_STRICTLY_CORRECT:
1803				// line interpolation matches the specification
1804				m_result.addResult(QP_TEST_RESULT_PASS, "Pass");
1805				break;
1806
1807			case tcu::LINEINTERPOLATION_PROJECTED:
1808				// line interpolation weights are otherwise correct, but they are projected onto major axis
1809				m_testCtx.getLog()	<< tcu::TestLog::Message
1810									<< "Interpolation was calculated using coordinates projected onto major axis. "
1811									   "This method does not produce the same values as the non-projecting method defined in the specification."
1812									<< tcu::TestLog::EndMessage;
1813				m_result.addResult(QP_TEST_RESULT_QUALITY_WARNING, "Interpolation was calculated using projected coordinateds");
1814				break;
1815
1816			case tcu::LINEINTERPOLATION_INCORRECT:
1817				if (scene.lineWidth != 1.0f && m_numSamples > 1)
1818				{
1819					// multisampled wide lines might not be supported
1820					m_result.addResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, "Interpolation of multisampled wide lines failed");
1821				}
1822				else
1823				{
1824					// line interpolation is incorrect
1825					m_result.addResult(QP_TEST_RESULT_FAIL, "Found invalid pixel values");
1826				}
1827				break;
1828
1829			default:
1830				DE_ASSERT(false);
1831				break;
1832		}
1833	}
1834
1835	// result
1836	if (++m_iteration == m_iterationCount)
1837	{
1838		m_result.setTestContextResult(m_testCtx);
1839		return STOP;
1840	}
1841	else
1842		return CONTINUE;
1843}
1844
1845void LineInterpolationTest::generateVertices (int iteration, std::vector<tcu::Vec4>& outVertices, std::vector<tcu::Vec4>& outColors) const
1846{
1847	// use only red, green and blue
1848	const tcu::Vec4 colors[] =
1849	{
1850		tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f),
1851		tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f),
1852		tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f),
1853	};
1854
1855	de::Random rnd(123 + iteration * 1000 + (int)m_primitive);
1856
1857	outVertices.resize(6);
1858	outColors.resize(6);
1859
1860	for (int vtxNdx = 0; vtxNdx < (int)outVertices.size(); ++vtxNdx)
1861	{
1862		outVertices[vtxNdx].x() = rnd.getFloat(-0.9f, 0.9f);
1863		outVertices[vtxNdx].y() = rnd.getFloat(-0.9f, 0.9f);
1864		outVertices[vtxNdx].z() = 0.0f;
1865
1866		if (!m_projective)
1867			outVertices[vtxNdx].w() = 1.0f;
1868		else
1869		{
1870			const float w = rnd.getFloat(0.2f, 4.0f);
1871
1872			outVertices[vtxNdx].x() *= w;
1873			outVertices[vtxNdx].y() *= w;
1874			outVertices[vtxNdx].z() *= w;
1875			outVertices[vtxNdx].w() = w;
1876		}
1877
1878		outColors[vtxNdx] = colors[vtxNdx % DE_LENGTH_OF_ARRAY(colors)];
1879	}
1880}
1881
1882void LineInterpolationTest::extractLines (std::vector<LineSceneSpec::SceneLine>& outLines, const std::vector<tcu::Vec4>& vertices, const std::vector<tcu::Vec4>& colors) const
1883{
1884	switch (m_primitive)
1885	{
1886		case GL_LINES:
1887		{
1888			for (int vtxNdx = 0; vtxNdx < (int)vertices.size() - 1; vtxNdx += 2)
1889			{
1890				LineSceneSpec::SceneLine line;
1891				line.positions[0] = vertices[vtxNdx + 0];
1892				line.positions[1] = vertices[vtxNdx + 1];
1893
1894				line.colors[0] = colors[vtxNdx + 0];
1895				line.colors[1] = colors[vtxNdx + 1];
1896
1897				outLines.push_back(line);
1898			}
1899			break;
1900		}
1901
1902		case GL_LINE_STRIP:
1903		{
1904			for (int vtxNdx = 0; vtxNdx < (int)vertices.size() - 1; ++vtxNdx)
1905			{
1906				LineSceneSpec::SceneLine line;
1907				line.positions[0] = vertices[vtxNdx + 0];
1908				line.positions[1] = vertices[vtxNdx + 1];
1909
1910				line.colors[0] = colors[vtxNdx + 0];
1911				line.colors[1] = colors[vtxNdx + 1];
1912
1913				outLines.push_back(line);
1914			}
1915			break;
1916		}
1917
1918		case GL_LINE_LOOP:
1919		{
1920			for (int vtxNdx = 0; vtxNdx < (int)vertices.size(); ++vtxNdx)
1921			{
1922				LineSceneSpec::SceneLine line;
1923				line.positions[0] = vertices[(vtxNdx + 0) % (int)vertices.size()];
1924				line.positions[1] = vertices[(vtxNdx + 1) % (int)vertices.size()];
1925
1926				line.colors[0] = colors[(vtxNdx + 0) % (int)vertices.size()];
1927				line.colors[1] = colors[(vtxNdx + 1) % (int)vertices.size()];
1928
1929				outLines.push_back(line);
1930			}
1931			break;
1932		}
1933
1934		default:
1935			DE_ASSERT(false);
1936	}
1937}
1938
1939} // anonymous
1940
1941RasterizationTests::RasterizationTests (Context& context)
1942	: TestCaseGroup(context, "rasterization", "Rasterization Tests")
1943{
1944}
1945
1946RasterizationTests::~RasterizationTests (void)
1947{
1948}
1949
1950void RasterizationTests::init (void)
1951{
1952	// .primitives
1953	{
1954		tcu::TestCaseGroup* const primitives = new tcu::TestCaseGroup(m_testCtx, "primitives", "Primitive rasterization");
1955
1956		addChild(primitives);
1957
1958		primitives->addChild(new TrianglesCase		(m_context, "triangles",		"Render primitives as GL_TRIANGLES, verify rasterization result"));
1959		primitives->addChild(new TriangleStripCase	(m_context, "triangle_strip",	"Render primitives as GL_TRIANGLE_STRIP, verify rasterization result"));
1960		primitives->addChild(new TriangleFanCase	(m_context, "triangle_fan",		"Render primitives as GL_TRIANGLE_FAN, verify rasterization result"));
1961		primitives->addChild(new LinesCase			(m_context, "lines",			"Render primitives as GL_LINES, verify rasterization result",							PRIMITIVEWIDENESS_NARROW));
1962		primitives->addChild(new LineStripCase		(m_context, "line_strip",		"Render primitives as GL_LINE_STRIP, verify rasterization result",						PRIMITIVEWIDENESS_NARROW));
1963		primitives->addChild(new LineLoopCase		(m_context, "line_loop",		"Render primitives as GL_LINE_LOOP, verify rasterization result",						PRIMITIVEWIDENESS_NARROW));
1964		primitives->addChild(new LinesCase			(m_context, "lines_wide",		"Render primitives as GL_LINES with wide lines, verify rasterization result",			PRIMITIVEWIDENESS_WIDE));
1965		primitives->addChild(new LineStripCase		(m_context, "line_strip_wide",	"Render primitives as GL_LINE_STRIP with wide lines, verify rasterization result",		PRIMITIVEWIDENESS_WIDE));
1966		primitives->addChild(new LineLoopCase		(m_context, "line_loop_wide",	"Render primitives as GL_LINE_LOOP with wide lines, verify rasterization result",		PRIMITIVEWIDENESS_WIDE));
1967		primitives->addChild(new PointCase			(m_context, "points",			"Render primitives as GL_POINTS, verify rasterization result",							PRIMITIVEWIDENESS_WIDE));
1968	}
1969
1970	// .limits
1971	{
1972		tcu::TestCaseGroup* const limits = new tcu::TestCaseGroup(m_testCtx, "limits", "Primitive width limits");
1973
1974		addChild(limits);
1975
1976		limits->addChild(new PointSizeClampedTest(m_context, "points", "gl_PointSize is clamped to ALIASED_POINT_SIZE_RANGE"));
1977	}
1978
1979	// .fill_rules
1980	{
1981		tcu::TestCaseGroup* const fillRules = new tcu::TestCaseGroup(m_testCtx, "fill_rules", "Primitive fill rules");
1982
1983		addChild(fillRules);
1984
1985		fillRules->addChild(new FillRuleCase(m_context,	"basic_quad",			"Verify fill rules",	FillRuleCase::FILLRULECASE_BASIC));
1986		fillRules->addChild(new FillRuleCase(m_context,	"basic_quad_reverse",	"Verify fill rules",	FillRuleCase::FILLRULECASE_REVERSED));
1987		fillRules->addChild(new FillRuleCase(m_context,	"clipped_full",			"Verify fill rules",	FillRuleCase::FILLRULECASE_CLIPPED_FULL));
1988		fillRules->addChild(new FillRuleCase(m_context,	"clipped_partly",		"Verify fill rules",	FillRuleCase::FILLRULECASE_CLIPPED_PARTIAL));
1989		fillRules->addChild(new FillRuleCase(m_context,	"projected",			"Verify fill rules",	FillRuleCase::FILLRULECASE_PROJECTED));
1990	}
1991
1992	// .culling
1993	{
1994		static const struct CullMode
1995		{
1996			glw::GLenum	mode;
1997			const char*	prefix;
1998		} cullModes[] =
1999		{
2000			{ GL_FRONT,				"front_"	},
2001			{ GL_BACK,				"back_"		},
2002			{ GL_FRONT_AND_BACK,	"both_"		},
2003		};
2004		static const struct PrimitiveType
2005		{
2006			glw::GLenum	type;
2007			const char*	name;
2008		} primitiveTypes[] =
2009		{
2010			{ GL_TRIANGLES,			"triangles"			},
2011			{ GL_TRIANGLE_STRIP,	"triangle_strip"	},
2012			{ GL_TRIANGLE_FAN,		"triangle_fan"		},
2013		};
2014		static const struct FrontFaceOrder
2015		{
2016			glw::GLenum	mode;
2017			const char*	postfix;
2018		} frontOrders[] =
2019		{
2020			{ GL_CCW,	""			},
2021			{ GL_CW,	"_reverse"	},
2022		};
2023
2024		tcu::TestCaseGroup* const culling = new tcu::TestCaseGroup(m_testCtx, "culling", "Culling");
2025
2026		addChild(culling);
2027
2028		for (int cullModeNdx   = 0; cullModeNdx   < DE_LENGTH_OF_ARRAY(cullModes);      ++cullModeNdx)
2029		for (int primitiveNdx  = 0; primitiveNdx  < DE_LENGTH_OF_ARRAY(primitiveTypes); ++primitiveNdx)
2030		for (int frontOrderNdx = 0; frontOrderNdx < DE_LENGTH_OF_ARRAY(frontOrders);    ++frontOrderNdx)
2031		{
2032			const std::string name = std::string(cullModes[cullModeNdx].prefix) + primitiveTypes[primitiveNdx].name + frontOrders[frontOrderNdx].postfix;
2033
2034			culling->addChild(new CullingTest(m_context, name.c_str(), "Test primitive culling.", cullModes[cullModeNdx].mode, primitiveTypes[primitiveNdx].type, frontOrders[frontOrderNdx].mode));
2035		}
2036	}
2037
2038	// .interpolation
2039	{
2040		tcu::TestCaseGroup* const interpolation = new tcu::TestCaseGroup(m_testCtx, "interpolation", "Test interpolation");
2041
2042		addChild(interpolation);
2043
2044		// .basic
2045		{
2046			tcu::TestCaseGroup* const basic = new tcu::TestCaseGroup(m_testCtx, "basic", "Non-projective interpolation");
2047
2048			interpolation->addChild(basic);
2049
2050			basic->addChild(new TriangleInterpolationTest		(m_context, "triangles",		"Verify triangle interpolation",		GL_TRIANGLES,		INTERPOLATIONFLAGS_NONE));
2051			basic->addChild(new TriangleInterpolationTest		(m_context, "triangle_strip",	"Verify triangle strip interpolation",	GL_TRIANGLE_STRIP,	INTERPOLATIONFLAGS_NONE));
2052			basic->addChild(new TriangleInterpolationTest		(m_context, "triangle_fan",		"Verify triangle fan interpolation",	GL_TRIANGLE_FAN,	INTERPOLATIONFLAGS_NONE));
2053			basic->addChild(new LineInterpolationTest			(m_context, "lines",			"Verify line interpolation",			GL_LINES,			INTERPOLATIONFLAGS_NONE,	1.0f));
2054			basic->addChild(new LineInterpolationTest			(m_context, "line_strip",		"Verify line strip interpolation",		GL_LINE_STRIP,		INTERPOLATIONFLAGS_NONE,	1.0f));
2055			basic->addChild(new LineInterpolationTest			(m_context, "line_loop",		"Verify line loop interpolation",		GL_LINE_LOOP,		INTERPOLATIONFLAGS_NONE,	1.0f));
2056			basic->addChild(new LineInterpolationTest			(m_context, "lines_wide",		"Verify wide line interpolation",		GL_LINES,			INTERPOLATIONFLAGS_NONE,	5.0f));
2057			basic->addChild(new LineInterpolationTest			(m_context, "line_strip_wide",	"Verify wide line strip interpolation",	GL_LINE_STRIP,		INTERPOLATIONFLAGS_NONE,	5.0f));
2058			basic->addChild(new LineInterpolationTest			(m_context, "line_loop_wide",	"Verify wide line loop interpolation",	GL_LINE_LOOP,		INTERPOLATIONFLAGS_NONE,	5.0f));
2059		}
2060
2061		// .projected
2062		{
2063			tcu::TestCaseGroup* const projected = new tcu::TestCaseGroup(m_testCtx, "projected", "Projective interpolation");
2064
2065			interpolation->addChild(projected);
2066
2067			projected->addChild(new TriangleInterpolationTest	(m_context, "triangles",		"Verify triangle interpolation",		GL_TRIANGLES,		INTERPOLATIONFLAGS_PROJECTED));
2068			projected->addChild(new TriangleInterpolationTest	(m_context, "triangle_strip",	"Verify triangle strip interpolation",	GL_TRIANGLE_STRIP,	INTERPOLATIONFLAGS_PROJECTED));
2069			projected->addChild(new TriangleInterpolationTest	(m_context, "triangle_fan",		"Verify triangle fan interpolation",	GL_TRIANGLE_FAN,	INTERPOLATIONFLAGS_PROJECTED));
2070			projected->addChild(new LineInterpolationTest		(m_context, "lines",			"Verify line interpolation",			GL_LINES,			INTERPOLATIONFLAGS_PROJECTED,	1.0f));
2071			projected->addChild(new LineInterpolationTest		(m_context, "line_strip",		"Verify line strip interpolation",		GL_LINE_STRIP,		INTERPOLATIONFLAGS_PROJECTED,	1.0f));
2072			projected->addChild(new LineInterpolationTest		(m_context, "line_loop",		"Verify line loop interpolation",		GL_LINE_LOOP,		INTERPOLATIONFLAGS_PROJECTED,	1.0f));
2073			projected->addChild(new LineInterpolationTest		(m_context, "lines_wide",		"Verify wide line interpolation",		GL_LINES,			INTERPOLATIONFLAGS_PROJECTED,	5.0f));
2074			projected->addChild(new LineInterpolationTest		(m_context, "line_strip_wide",	"Verify wide line strip interpolation",	GL_LINE_STRIP,		INTERPOLATIONFLAGS_PROJECTED,	5.0f));
2075			projected->addChild(new LineInterpolationTest		(m_context, "line_loop_wide",	"Verify wide line loop interpolation",	GL_LINE_LOOP,		INTERPOLATIONFLAGS_PROJECTED,	5.0f));
2076		}
2077	}
2078}
2079
2080} // Functional
2081} // gles2
2082} // deqp
2083