/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES 3.0 Module
 * -------------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Rasterizer discard tests.
 *//*--------------------------------------------------------------------*/

#include "es3fRasterizerDiscardTests.hpp"

#include "tcuTestLog.hpp"
#include "tcuVector.hpp"
#include "tcuSurface.hpp"
#include "tcuRenderTarget.hpp"
#include "gluShaderProgram.hpp"
#include "gluPixelTransfer.hpp"
#include "deRandom.hpp"
#include "deStringUtil.hpp"
#include "deString.h"

#include "glw.h"

using tcu::Vec4;
using tcu::TestLog;

namespace deqp
{
namespace gles3
{
namespace Functional
{

static const int	NUM_CASE_ITERATIONS = 1;
static const Vec4	FAIL_COLOR_RED		= Vec4(1.0f, 0.0f, 0.0f, 1.0f);
static const Vec4	PASS_COLOR_BLUE		= Vec4(0.0f, 0.0f, 0.5f, 1.0f);
static const Vec4	BLACK_COLOR			= Vec4(0.0f, 0.0f, 0.0f, 1.0f);
static const float	FAIL_DEPTH			= 0.0f;
static const int	FAIL_STENCIL		= 1;
static const float	UNIT_SQUARE[16] =
{
	 1.0f,  1.0f, 0.05f, 1.0f,
	 1.0f, -1.0f, 0.05f, 1.0f,
	-1.0f,  1.0f, 0.05f, 1.0f,
	-1.0f, -1.0f, 0.05f, 1.0f
};

enum CaseType
{
	CASE_WRITE_DEPTH,
	CASE_WRITE_STENCIL,
	CASE_CLEAR_COLOR,
	CASE_CLEAR_DEPTH,
	CASE_CLEAR_STENCIL
};

enum CaseOptions
{
	CASEOPTION_FBO		= (1 << 0),
	CASEOPTION_SCISSOR	= (1 << 1)
};

class RasterizerDiscardCase : public TestCase
{
public:
								RasterizerDiscardCase	(Context& context, const char* name, const char* description, int numPrimitives, CaseType caseType, deUint32 caseOptions, deUint32 drawMode = GL_TRIANGLES);
								~RasterizerDiscardCase	(void);

	void						init					(void);
	void						deinit					(void);
	IterateResult				iterate					(void);

private:
								RasterizerDiscardCase	(const RasterizerDiscardCase& other);
	RasterizerDiscardCase&		operator=				(const RasterizerDiscardCase& other);

	void						setupFramebufferObject	(void);
	void						deleteFramebufferObject	(void);

	int							m_numPrimitives;
	CaseType					m_caseType;
	deUint32					m_caseOptions;
	deUint32					m_drawMode;

	glu::ShaderProgram*			m_program;
	deUint32					m_fbo;
	deUint32					m_colorBuf;
	deUint32					m_depthStencilBuf;
	int							m_iterNdx;
	de::Random					m_rnd;
};

RasterizerDiscardCase::RasterizerDiscardCase (Context& context, const char* name, const char* description, int numPrimitives, CaseType caseType, deUint32 caseOptions, deUint32 drawMode)
	: TestCase				(context, name, description)
	, m_numPrimitives		(numPrimitives)
	, m_caseType			(caseType)
	, m_caseOptions			(caseOptions)
	, m_drawMode			(drawMode)
	, m_program				(DE_NULL)
	, m_fbo					(0)
	, m_colorBuf			(0)
	, m_depthStencilBuf		(0)
	, m_iterNdx				(0)
	, m_rnd					(deStringHash(name))
{
}

RasterizerDiscardCase::~RasterizerDiscardCase (void)
{
	RasterizerDiscardCase::deinit();
}

static void generateVertices (std::vector<float>& dst, int numPrimitives, de::Random& rnd, deUint32 drawMode)
{
	int numVertices;

	switch (drawMode)
	{
		case GL_POINTS:			numVertices = numPrimitives;	break;
		case GL_LINES:			numVertices = 2*numPrimitives;	break;
		case GL_LINE_STRIP:		numVertices = numPrimitives+1;	break;
		case GL_LINE_LOOP:		numVertices = numPrimitives+2;	break;
		case GL_TRIANGLES:		numVertices = 3*numPrimitives;	break;
		case GL_TRIANGLE_STRIP:	numVertices = numPrimitives+2;	break;
		case GL_TRIANGLE_FAN:	numVertices = numPrimitives+2;	break;
		default:
			DE_ASSERT(false);
			numVertices = 0;
	}

	dst.resize(numVertices * 4);

	for (int i = 0; i < numVertices; i++)
	{
		dst[i*4    ] = rnd.getFloat(-1.0f, 1.0f);	// x
		dst[i*4 + 1] = rnd.getFloat(-1.0f, 1.0f);	// y
		dst[i*4 + 2] = rnd.getFloat( 0.1f, 0.9f);	// z
		dst[i*4 + 3] = 1.0f;						// w
	}
}

void RasterizerDiscardCase::setupFramebufferObject (void)
{
	int width  = m_context.getRenderTarget().getWidth();
	int height = m_context.getRenderTarget().getHeight();

	// Create framebuffer object

	glGenFramebuffers	(1, &m_fbo);				// FBO
	glGenTextures		(1, &m_colorBuf);			// Color attachment
	glGenRenderbuffers	(1, &m_depthStencilBuf);	// Depth and stencil attachments

	// Create color texture

	glBindTexture	(GL_TEXTURE_2D, m_colorBuf);
	glTexParameteri	(GL_TEXTURE_2D,	GL_TEXTURE_WRAP_S,		GL_CLAMP_TO_EDGE);
	glTexParameteri	(GL_TEXTURE_2D,	GL_TEXTURE_WRAP_T,		GL_CLAMP_TO_EDGE);
	glTexParameteri	(GL_TEXTURE_2D,	GL_TEXTURE_MIN_FILTER,	GL_LINEAR);
	glTexParameteri	(GL_TEXTURE_2D,	GL_TEXTURE_MAG_FILTER,	GL_LINEAR);
	glTexImage2D	(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);

	// Create depth and stencil buffers

	glBindRenderbuffer	  (GL_RENDERBUFFER, m_depthStencilBuf);
	glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);

	// Attach texture and buffers to FBO

	glBindFramebuffer		  (GL_FRAMEBUFFER, m_fbo);
	glFramebufferTexture2D	  (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuf, 0);
	glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuf);
	glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuf);

	deUint32 fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);

	if (fboStatus == GL_FRAMEBUFFER_UNSUPPORTED)
		throw tcu::NotSupportedError("Framebuffer unsupported", "", __FILE__, __LINE__);
	else if (fboStatus != GL_FRAMEBUFFER_COMPLETE)
		throw tcu::TestError("Failed to create framebuffer object", "", __FILE__, __LINE__);
}

void RasterizerDiscardCase::deleteFramebufferObject (void)
{
	glDeleteTextures		(1, &m_colorBuf);			// Color attachment
	glDeleteRenderbuffers	(1, &m_depthStencilBuf);	// Depth and stencil attachments
	glDeleteFramebuffers	(1, &m_fbo);				// FBO
}

void RasterizerDiscardCase::init (void)
{
	const char*	vertShaderSource =
				"#version 300 es\n"
				"layout(location = 0) in mediump vec4 a_position;\n"
				"\n"
				"void main (void)\n"
				"{\n"
				"	gl_Position = a_position;\n"
				"}\n";

	const char* fragShaderSource =
				"#version 300 es\n"
				"layout(location = 0) out mediump vec4 dEQP_FragColor;\n"
				"uniform mediump vec4 u_color;\n"
				"\n"
				"void main (void)\n"
				"{\n"
				"	mediump float depth_gradient = gl_FragCoord.z;\n"
				"	mediump float bias = 0.1;\n"
				"	dEQP_FragColor = vec4(u_color.xyz * (depth_gradient + bias), 1.0);\n"
				"}\n";

	DE_ASSERT(!m_program);
	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertShaderSource, fragShaderSource));

	if (!m_program->isOk())
	{
		m_testCtx.getLog() << *m_program;
		TCU_FAIL("Failed to compile shader program");
	}

	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); // Initialize test result to pass.
	GLU_CHECK_MSG ("Case initialization finished");
}

void RasterizerDiscardCase::deinit (void)
{
	deleteFramebufferObject();
	delete m_program;
	m_program = DE_NULL;
}

RasterizerDiscardCase::IterateResult RasterizerDiscardCase::iterate (void)
{
	TestLog&					log				= m_testCtx.getLog();
	const tcu::RenderTarget&	renderTarget	= m_context.getRenderTarget();
	deUint32					colorUnif		= glGetUniformLocation(m_program->getProgram(), "u_color");
	bool						failColorFound	= false;
	bool						passColorFound	= false;
	std::vector<float>			vertices;

	std::string header = "Case iteration " + de::toString(m_iterNdx+1) + " / " + de::toString(NUM_CASE_ITERATIONS);
	log << TestLog::Section(header, header);

	DE_ASSERT (m_program);

	// Create and bind FBO if needed

	if (m_caseOptions & CASEOPTION_FBO)
	{
		try
		{
			setupFramebufferObject();
		}
		catch (tcu::NotSupportedError& e)
		{
			log << TestLog::Message << "ERROR: " << e.what() << "." << TestLog::EndMessage << TestLog::EndSection;
			m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported");
			return STOP;
		}
		catch (tcu::InternalError& e)
		{
			log << TestLog::Message << "ERROR: " << e.what() << "." << TestLog::EndMessage << TestLog::EndSection;
			m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error");
			return STOP;
		}
	}

	if (m_caseOptions & CASEOPTION_SCISSOR)
	{
		glEnable (GL_SCISSOR_TEST);
		glScissor(0, 0, renderTarget.getWidth(), renderTarget.getHeight());
		log << TestLog::Message << "Scissor test enabled: glScissor(0, 0, " << renderTarget.getWidth() << ", " << renderTarget.getHeight() << ")" << TestLog::EndMessage;
	}

	glUseProgram	(m_program->getProgram());

	glEnable		(GL_DEPTH_TEST);
	glDepthRangef	(0.0f, 1.0f);
	glDepthFunc		(GL_LEQUAL);

	glEnable		(GL_STENCIL_TEST);
	glStencilFunc	(GL_NOTEQUAL, 1, 0xFF);
	glStencilOp		(GL_REPLACE, GL_KEEP, GL_KEEP);

	glClearColor	(PASS_COLOR_BLUE.x(), PASS_COLOR_BLUE.y(), PASS_COLOR_BLUE.z(), PASS_COLOR_BLUE.w());
	glClearDepthf	(1.0f);
	glClearStencil	(0);
	glClear			(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

	// Generate vertices

	glEnableVertexAttribArray (0);
	generateVertices		  (vertices, m_numPrimitives, m_rnd, m_drawMode);
	glVertexAttribPointer	  (0, 4, GL_FLOAT, GL_FALSE, 0, &vertices[0]);

	// Clear color to black for depth and stencil clear cases

	if (m_caseType == CASE_CLEAR_DEPTH || m_caseType == CASE_CLEAR_STENCIL)
	{
		glClearColor	(BLACK_COLOR.x(), BLACK_COLOR.y(), BLACK_COLOR.z(), BLACK_COLOR.w());
		glClear			(GL_COLOR_BUFFER_BIT);
	}

	// Set fail values for color, depth and stencil

	glUniform4f		(colorUnif, FAIL_COLOR_RED.x(), FAIL_COLOR_RED.y(), FAIL_COLOR_RED.z(), FAIL_COLOR_RED.w());
	glClearColor	(FAIL_COLOR_RED.x(), FAIL_COLOR_RED.y(), FAIL_COLOR_RED.z(), FAIL_COLOR_RED.w());
	glClearDepthf	(FAIL_DEPTH);
	glClearStencil	(FAIL_STENCIL);

	// Enable rasterizer discard

	glEnable		(GL_RASTERIZER_DISCARD);
	GLU_CHECK_MSG	("Rasterizer discard enabled");

	// Do to-be-discarded primitive draws and buffer clears

	switch (m_caseType)
	{
		case CASE_WRITE_DEPTH:			glDrawArrays(m_drawMode, 0, (int)vertices.size() / 4);																	break;
		case CASE_WRITE_STENCIL:		glDrawArrays(m_drawMode, 0, (int)vertices.size() / 4);																	break;
		case CASE_CLEAR_COLOR:			(m_caseOptions & CASEOPTION_FBO) ? glClearBufferfv(GL_COLOR, 0, &FAIL_COLOR_RED[0])	: glClear(GL_COLOR_BUFFER_BIT);		break;
		case CASE_CLEAR_DEPTH:			(m_caseOptions & CASEOPTION_FBO) ? glClearBufferfv(GL_DEPTH, 0, &FAIL_DEPTH)		: glClear(GL_DEPTH_BUFFER_BIT);		break;
		case CASE_CLEAR_STENCIL:		(m_caseOptions & CASEOPTION_FBO) ? glClearBufferiv(GL_STENCIL, 0, &FAIL_STENCIL)	: glClear(GL_STENCIL_BUFFER_BIT);	break;
		default:						DE_ASSERT(false);
	}

	// Disable rasterizer discard

	glDisable		(GL_RASTERIZER_DISCARD);
	GLU_CHECK_MSG	("Rasterizer discard disabled");

	if (m_caseType == CASE_WRITE_STENCIL)
	{
		if ((m_caseOptions & CASEOPTION_FBO) || m_context.getRenderTarget().getStencilBits() > 0)
		{
			// Draw a full-screen square that colors all pixels red if they have stencil value 1.

			glVertexAttribPointer (0, 4, GL_FLOAT, GL_FALSE, 0, &UNIT_SQUARE[0]);
			glStencilFunc		  (GL_EQUAL, 1, 0xFF);
			glDrawArrays		  (GL_TRIANGLE_STRIP, 0, 4);
		}
		// \note If no stencil buffers are present and test is rendering to default framebuffer, test will always pass.
	}
	else if (m_caseType == CASE_CLEAR_DEPTH || m_caseType == CASE_CLEAR_STENCIL)
	{
		// Draw pass-indicating primitives for depth and stencil clear cases

		glUniform4f	 (colorUnif, PASS_COLOR_BLUE.x(), PASS_COLOR_BLUE.y(), PASS_COLOR_BLUE.z(), PASS_COLOR_BLUE.w());
		glDrawArrays (m_drawMode, 0, (int)vertices.size() / 4);
	}

	glFinish  ();
	glDisable (GL_STENCIL_TEST);
	glDisable (GL_DEPTH_TEST);
	glDisable (GL_SCISSOR_TEST);

	// Read and check pixel data

	tcu::Surface pixels(renderTarget.getWidth(), renderTarget.getHeight());
	glu::readPixels(m_context.getRenderContext(), 0, 0, pixels.getAccess());

	{
		int width = pixels.getWidth();
		int height = pixels.getHeight();

		for (int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; x++)
			{
				if (pixels.getPixel(x,y).getBlue() != 0)
					passColorFound = true;

				if (pixels.getPixel(x,y).getRed() != 0)
				{
					failColorFound = true;
					break;
				}
			}
			if (failColorFound) break;
		}
	}

	// Delete FBO if created

	if (m_caseOptions & CASEOPTION_FBO)
		deleteFramebufferObject();

	// Evaluate test result

	bool testOk = passColorFound && !failColorFound;

	if (!testOk)
		log << TestLog::Image ("Result image", "Result image", pixels);
	log << TestLog::Message << "Test result: " << (testOk ? "Passed!" : "Failed!") << TestLog::EndMessage;

	if (!testOk)
	{
		log << TestLog::Message << "Primitive or buffer clear was not discarded." << TestLog::EndMessage << TestLog::EndSection;
		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
		return STOP;
	}

	log << TestLog::Message << "Primitive or buffer clear was discarded correctly." << TestLog::EndMessage << TestLog::EndSection;

	return (++m_iterNdx < NUM_CASE_ITERATIONS) ? CONTINUE : STOP;
}

RasterizerDiscardTests::RasterizerDiscardTests (Context& context)
	: TestCaseGroup(context, "rasterizer_discard", "Rasterizer Discard Tests")
{
}

RasterizerDiscardTests::~RasterizerDiscardTests (void)
{
}

void RasterizerDiscardTests::init (void)
{
	tcu::TestCaseGroup* basic	= new tcu::TestCaseGroup(m_testCtx, "basic",	"Rasterizer discard test for default framebuffer");
	tcu::TestCaseGroup*	scissor	= new tcu::TestCaseGroup(m_testCtx, "scissor",	"Rasterizer discard test for default framebuffer with scissor test enabled");
	tcu::TestCaseGroup*	fbo		= new tcu::TestCaseGroup(m_testCtx, "fbo",		"Rasterizer discard test for framebuffer object");

	addChild(basic);
	addChild(scissor);
	addChild(fbo);

	// Default framebuffer cases

	basic->addChild(new RasterizerDiscardCase(m_context, "write_depth_points",				"points",			4, CASE_WRITE_DEPTH,	0, GL_POINTS));
	basic->addChild(new RasterizerDiscardCase(m_context, "write_depth_lines",				"lines",			4, CASE_WRITE_DEPTH,	0, GL_LINES));
	basic->addChild(new RasterizerDiscardCase(m_context, "write_depth_line_strip",			"line_strip",		4, CASE_WRITE_DEPTH,	0, GL_LINE_STRIP));
	basic->addChild(new RasterizerDiscardCase(m_context, "write_depth_line_loop",			"line_loop",		4, CASE_WRITE_DEPTH,	0, GL_LINE_LOOP));
	basic->addChild(new RasterizerDiscardCase(m_context, "write_depth_triangles",			"triangles",		4, CASE_WRITE_DEPTH,	0, GL_TRIANGLES));
	basic->addChild(new RasterizerDiscardCase(m_context, "write_depth_triangle_strip",		"triangle_strip",	4, CASE_WRITE_DEPTH,	0, GL_TRIANGLE_STRIP));
	basic->addChild(new RasterizerDiscardCase(m_context, "write_depth_triangle_fan",		"triangle_fan",		4, CASE_WRITE_DEPTH,	0, GL_TRIANGLE_FAN));

	basic->addChild(new RasterizerDiscardCase(m_context, "write_stencil_points",			"points",			4, CASE_WRITE_STENCIL,	0, GL_POINTS));
	basic->addChild(new RasterizerDiscardCase(m_context, "write_stencil_lines",				"lines",			4, CASE_WRITE_STENCIL,	0, GL_LINES));
	basic->addChild(new RasterizerDiscardCase(m_context, "write_stencil_line_strip",		"line_strip",		4, CASE_WRITE_STENCIL,	0, GL_LINE_STRIP));
	basic->addChild(new RasterizerDiscardCase(m_context, "write_stencil_line_loop",			"line_loop",		4, CASE_WRITE_STENCIL,	0, GL_LINE_LOOP));
	basic->addChild(new RasterizerDiscardCase(m_context, "write_stencil_triangles",			"triangles",		4, CASE_WRITE_STENCIL,	0, GL_TRIANGLES));
	basic->addChild(new RasterizerDiscardCase(m_context, "write_stencil_triangle_strip",	"triangle_strip",	4, CASE_WRITE_STENCIL,	0, GL_TRIANGLE_STRIP));
	basic->addChild(new RasterizerDiscardCase(m_context, "write_stencil_triangle_fan",		"triangle_fan",		4, CASE_WRITE_STENCIL,	0, GL_TRIANGLE_FAN));

	basic->addChild(new RasterizerDiscardCase(m_context, "clear_color",						"clear_color",		4, CASE_CLEAR_COLOR,	0));
	basic->addChild(new RasterizerDiscardCase(m_context, "clear_depth",						"clear_depth",		4, CASE_CLEAR_DEPTH,	0));
	basic->addChild(new RasterizerDiscardCase(m_context, "clear_stencil",					"clear_stencil",	4, CASE_CLEAR_STENCIL,	0));

	// Default framebuffer cases with scissor test enabled

	scissor->addChild(new RasterizerDiscardCase(m_context, "write_depth_points",			"points",			4, CASE_WRITE_DEPTH,	CASEOPTION_SCISSOR, GL_POINTS));
	scissor->addChild(new RasterizerDiscardCase(m_context, "write_depth_lines",				"lines",			4, CASE_WRITE_DEPTH,	CASEOPTION_SCISSOR, GL_LINES));
	scissor->addChild(new RasterizerDiscardCase(m_context, "write_depth_line_strip",		"line_strip",		4, CASE_WRITE_DEPTH,	CASEOPTION_SCISSOR, GL_LINE_STRIP));
	scissor->addChild(new RasterizerDiscardCase(m_context, "write_depth_line_loop",			"line_loop",		4, CASE_WRITE_DEPTH,	CASEOPTION_SCISSOR, GL_LINE_LOOP));
	scissor->addChild(new RasterizerDiscardCase(m_context, "write_depth_triangles",			"triangles",		4, CASE_WRITE_DEPTH,	CASEOPTION_SCISSOR, GL_TRIANGLES));
	scissor->addChild(new RasterizerDiscardCase(m_context, "write_depth_triangle_strip",	"triangle_strip",	4, CASE_WRITE_DEPTH,	CASEOPTION_SCISSOR, GL_TRIANGLE_STRIP));
	scissor->addChild(new RasterizerDiscardCase(m_context, "write_depth_triangle_fan",		"triangle_fan",		4, CASE_WRITE_DEPTH,	CASEOPTION_SCISSOR, GL_TRIANGLE_FAN));

	scissor->addChild(new RasterizerDiscardCase(m_context, "write_stencil_points",			"points",			4, CASE_WRITE_STENCIL,	CASEOPTION_SCISSOR, GL_POINTS));
	scissor->addChild(new RasterizerDiscardCase(m_context, "write_stencil_lines",			"lines",			4, CASE_WRITE_STENCIL,	CASEOPTION_SCISSOR, GL_LINES));
	scissor->addChild(new RasterizerDiscardCase(m_context, "write_stencil_line_strip",		"line_strip",		4, CASE_WRITE_STENCIL,	CASEOPTION_SCISSOR, GL_LINE_STRIP));
	scissor->addChild(new RasterizerDiscardCase(m_context, "write_stencil_line_loop",		"line_loop",		4, CASE_WRITE_STENCIL,	CASEOPTION_SCISSOR, GL_LINE_LOOP));
	scissor->addChild(new RasterizerDiscardCase(m_context, "write_stencil_triangles",		"triangles",		4, CASE_WRITE_STENCIL,	CASEOPTION_SCISSOR, GL_TRIANGLES));
	scissor->addChild(new RasterizerDiscardCase(m_context, "write_stencil_triangle_strip",	"triangle_strip",	4, CASE_WRITE_STENCIL,	CASEOPTION_SCISSOR, GL_TRIANGLE_STRIP));
	scissor->addChild(new RasterizerDiscardCase(m_context, "write_stencil_triangle_fan",	"triangle_fan",		4, CASE_WRITE_STENCIL,	CASEOPTION_SCISSOR, GL_TRIANGLE_FAN));

	scissor->addChild(new RasterizerDiscardCase(m_context, "clear_color",					"clear_color",		4, CASE_CLEAR_COLOR,	CASEOPTION_SCISSOR));
	scissor->addChild(new RasterizerDiscardCase(m_context, "clear_depth",					"clear_depth",		4, CASE_CLEAR_DEPTH,	CASEOPTION_SCISSOR));
	scissor->addChild(new RasterizerDiscardCase(m_context, "clear_stencil",					"clear_stencil",	4, CASE_CLEAR_STENCIL,	CASEOPTION_SCISSOR));

	// FBO cases

	fbo->addChild(new RasterizerDiscardCase(m_context, "write_depth_points",			"points",			4, CASE_WRITE_DEPTH,	CASEOPTION_FBO, GL_POINTS));
	fbo->addChild(new RasterizerDiscardCase(m_context, "write_depth_lines",				"lines",			4, CASE_WRITE_DEPTH,	CASEOPTION_FBO, GL_LINES));
	fbo->addChild(new RasterizerDiscardCase(m_context, "write_depth_line_strip",		"line_strip",		4, CASE_WRITE_DEPTH,	CASEOPTION_FBO, GL_LINE_STRIP));
	fbo->addChild(new RasterizerDiscardCase(m_context, "write_depth_line_loop",			"line_loop",		4, CASE_WRITE_DEPTH,	CASEOPTION_FBO, GL_LINE_LOOP));
	fbo->addChild(new RasterizerDiscardCase(m_context, "write_depth_triangles",			"triangles",		4, CASE_WRITE_DEPTH,	CASEOPTION_FBO, GL_TRIANGLES));
	fbo->addChild(new RasterizerDiscardCase(m_context, "write_depth_triangle_strip",	"triangle_strip",	4, CASE_WRITE_DEPTH,	CASEOPTION_FBO, GL_TRIANGLE_STRIP));
	fbo->addChild(new RasterizerDiscardCase(m_context, "write_depth_triangle_fan",		"triangle_fan",		4, CASE_WRITE_DEPTH,	CASEOPTION_FBO, GL_TRIANGLE_FAN));

	fbo->addChild(new RasterizerDiscardCase(m_context, "write_stencil_points",			"points",			4, CASE_WRITE_STENCIL,	CASEOPTION_FBO, GL_POINTS));
	fbo->addChild(new RasterizerDiscardCase(m_context, "write_stencil_lines",			"lines",			4, CASE_WRITE_STENCIL,	CASEOPTION_FBO, GL_LINES));
	fbo->addChild(new RasterizerDiscardCase(m_context, "write_stencil_line_strip",		"line_strip",		4, CASE_WRITE_STENCIL,	CASEOPTION_FBO, GL_LINE_STRIP));
	fbo->addChild(new RasterizerDiscardCase(m_context, "write_stencil_line_loop",		"line_loop",		4, CASE_WRITE_STENCIL,	CASEOPTION_FBO, GL_LINE_LOOP));
	fbo->addChild(new RasterizerDiscardCase(m_context, "write_stencil_triangles",		"triangles",		4, CASE_WRITE_STENCIL,	CASEOPTION_FBO, GL_TRIANGLES));
	fbo->addChild(new RasterizerDiscardCase(m_context, "write_stencil_triangle_strip",	"triangle_strip",	4, CASE_WRITE_STENCIL,	CASEOPTION_FBO, GL_TRIANGLE_STRIP));
	fbo->addChild(new RasterizerDiscardCase(m_context, "write_stencil_triangle_fan",	"triangle_fan",		4, CASE_WRITE_STENCIL,	CASEOPTION_FBO, GL_TRIANGLE_FAN));

	fbo->addChild(new RasterizerDiscardCase(m_context, "clear_color",					"clear_color",		4, CASE_CLEAR_COLOR,	CASEOPTION_FBO));
	fbo->addChild(new RasterizerDiscardCase(m_context, "clear_depth",					"clear_depth",		4, CASE_CLEAR_DEPTH,	CASEOPTION_FBO));
	fbo->addChild(new RasterizerDiscardCase(m_context, "clear_stencil",					"clear_stencil",	4, CASE_CLEAR_STENCIL,	CASEOPTION_FBO));
}

} // Functional
} // gles3
} // deqp