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