void ShaderAtomicCounterOpsTestBase::ShaderPipeline::create(deqp::Context& context)
{
	glu::ProgramSources sources;
	for (unsigned int i = 0; i < glu::SHADERTYPE_COMPUTE; ++i)
	{
		if (!m_shaders[i].empty())
		{
			sources.sources[i].push_back(m_shaders[i]);
		}
	}
	m_program = new glu::ShaderProgram(context.getRenderContext(), sources);

	if (!m_program->isOk())
	{
		TCU_FAIL("Shader compilation failed");
	}

	glu::ProgramSources sourcesCompute;
	sourcesCompute.sources[glu::SHADERTYPE_COMPUTE].push_back(m_shaders[glu::SHADERTYPE_COMPUTE]);
	m_programCompute = new glu::ShaderProgram(context.getRenderContext(), sourcesCompute);

	if (!m_programCompute->isOk())
	{
		TCU_FAIL("Shader compilation failed");
	}
}
Exemplo n.º 2
0
void VarTokenizer::advance (void)
{
	DE_ASSERT(m_token != TOKEN_END);

	m_tokenStart	+= m_tokenLen;
	m_token			 = TOKEN_LAST;
	m_tokenLen		 = 1;

	if (m_str[m_tokenStart] == '[')
		m_token = TOKEN_LEFT_BRACKET;
	else if (m_str[m_tokenStart] == ']')
		m_token = TOKEN_RIGHT_BRACKET;
	else if (m_str[m_tokenStart] == 0)
		m_token = TOKEN_END;
	else if (m_str[m_tokenStart] == '.')
		m_token = TOKEN_PERIOD;
	else if (isNum(m_str[m_tokenStart]))
	{
		m_token = TOKEN_NUMBER;
		while (isNum(m_str[m_tokenStart+m_tokenLen]))
			m_tokenLen += 1;
	}
	else if (isIdentifierChar(m_str[m_tokenStart]))
	{
		m_token = TOKEN_IDENTIFIER;
		while (isIdentifierChar(m_str[m_tokenStart+m_tokenLen]))
			m_tokenLen += 1;
	}
	else
		TCU_FAIL("Unexpected character");
}
/* Determines depth internalformat that can be used for a draw framebuffer.
 * The result is stored in m_depth_internal_format and m_depth_type.
 **/
void TextureCubeMapArrayColorDepthAttachmentsTest::determineSupportedDepthFormat()
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	/* Start with 16-bit depth internalformat */
	m_depth_internal_format = GL_DEPTH_COMPONENT16;
	m_depth_type			= GL_UNSIGNED_SHORT;

	while (true)
	{
		/* Create color and depth texture objectss */
		generateAndConfigureTextureObjects(8,	  /* texture_width */
										   1,	  /* n_cubemaps */
										   false); /* should_generate_mutable_textures */

		/* Set framebuffer attachments up */
		configureNonLayeredFramebufferAttachment(m_color_texture_id, 0 /* layer */, true /* is_color_attachment */,
												 true /* should_update_draw_framebuffer */);
		configureNonLayeredFramebufferAttachment(m_depth_texture_id, 0 /* layer */, false /* is_color_attachment */,
												 true /* should_update_draw_framebuffer */);

		/* Check framebuffer status */
		const glw::GLenum framebuffer_status = gl.checkFramebufferStatus(GL_DRAW_FRAMEBUFFER);

		if (GL_FRAMEBUFFER_COMPLETE == framebuffer_status)
		{
			return;
		}

		/* Current format does not work too well, try another one */
		switch (m_depth_internal_format)
		{
		case GL_DEPTH_COMPONENT16:
		{
			m_depth_internal_format = GL_DEPTH_COMPONENT24;
			m_depth_type			= GL_UNSIGNED_INT;

			break;
		}

		case GL_DEPTH_COMPONENT24:
		{
			m_depth_internal_format = GL_DEPTH_COMPONENT32F;
			m_depth_type			= GL_FLOAT;

			break;
		}

		case GL_DEPTH_COMPONENT32F:
		{
			throw tcu::NotSupportedError("Implementation does not support any known depth format");
		}

		default:
		{
			TCU_FAIL("Unrecognized depth internalformat");
		}
		} /* switch (m_depth_internal_format) */
	}	 /* while (true) */
}
Exemplo n.º 4
0
void ShaderBallotBaseTestCase::createShaderPrograms(ShaderPipeline& pipeline)
{
	glu::ProgramSources sourcesRender;

	for (unsigned int i = 0; i < glu::SHADERTYPE_COMPUTE; ++i)
	{
		glu::ShaderType shaderType = (glu::ShaderType)i;

		std::map<std::string, std::string>::const_iterator mapIter;
		for (mapIter = pipeline.getSpecializationMap().begin(); mapIter != pipeline.getSpecializationMap().end();
			 mapIter++)
			m_specializationMap[mapIter->first] = mapIter->second;

		std::string shader =
			specializeShader(pipeline.getShaderPartsCount(shaderType), pipeline.getShaderParts(shaderType));
		sourcesRender.sources[i].push_back(shader);
	}

	glu::ShaderProgram* programRender = new glu::ShaderProgram(m_context.getRenderContext(), sourcesRender);

	if (!programRender->isOk())
	{
		TCU_FAIL("Shader compilation failed");
	}

	glu::ProgramSources sourcesCompute;

	m_specializationMap.insert(pipeline.getSpecializationMap().begin(), pipeline.getSpecializationMap().end());
	std::string shaderCompute = specializeShader(pipeline.getShaderPartsCount(glu::SHADERTYPE_COMPUTE),
												 pipeline.getShaderParts(glu::SHADERTYPE_COMPUTE));
	sourcesCompute.sources[glu::SHADERTYPE_COMPUTE].push_back(shaderCompute);

	glu::ShaderProgram* programCompute = new glu::ShaderProgram(m_context.getRenderContext(), sourcesCompute);

	if (!programCompute->isOk())
	{
		TCU_FAIL("Shader compilation failed");
	}

	pipeline.setShaderPrograms(programRender, programCompute);
}
Exemplo n.º 5
0
/** Check Framebuffer Status.
 *   Throws a TestError exception, should the framebuffer be found incomplete.
 *
 *  @param framebuffer - GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER or GL_FRAMEBUFFER
 *
 */
void TestCaseBase::checkFramebufferStatus(glw::GLenum framebuffer) const
{
	/* Get GL entry points */
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	glw::GLenum framebuffer_status = gl.checkFramebufferStatus(framebuffer);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Error getting framebuffer status!");

	if (GL_FRAMEBUFFER_COMPLETE != framebuffer_status)
	{
		switch (framebuffer_status)
		{
		case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
		{
			TCU_FAIL("Framebuffer incomplete, status: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
		}

		case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
		{
			TCU_FAIL("Framebuffer incomplete, status: GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS");
		}

		case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
		{
			TCU_FAIL("Framebuffer incomplete, status: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
		}

		case GL_FRAMEBUFFER_UNSUPPORTED:
		{
			TCU_FAIL("Framebuffer incomplete, status: Error: GL_FRAMEBUFFER_UNSUPPORTED");
		}

		default:
		{
			TCU_FAIL("Framebuffer incomplete, status not recognized");
		}
		} /* switch (framebuffer_status) */
	}	 /* if (GL_FRAMEBUFFER_COMPLETE != framebuffer_status) */
}
Exemplo n.º 6
0
/** Retrieves compilation OR linking info log for a shader/program object with GLES id
 *  @param id.
 *
 *  @param is_compilation_info_log true if @param id is a GLES id of a shader object;
 *                                 false if it represents a program object.
 *  @param id                      GLES id of a shader OR a program object to
 *                                 retrieve info log for.
 *
 *  @return String instance containing the log..
 **/
std::string TestCaseBase::getInfoLog(LOG_TYPE log_type, glw::GLuint id)
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	glw::GLint n_characters = 0;
	/* Retrieve amount of characters needed to store the info log (terminator-inclusive) */
	switch (log_type)
	{
	case LT_SHADER_OBJECT:
		gl.getShaderiv(id, GL_INFO_LOG_LENGTH, &n_characters);
		break;
	case LT_PROGRAM_OBJECT:
		gl.getProgramiv(id, GL_INFO_LOG_LENGTH, &n_characters);
		break;
	case LT_PIPELINE_OBJECT:
		gl.getProgramPipelineiv(id, GL_INFO_LOG_LENGTH, &n_characters);
		break;
	default:
		TCU_FAIL("Invalid parameter");
	}

	/* Check if everything is fine so far */
	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not query info log length!");

	/* Allocate buffer */
	std::vector<char> result_vec(n_characters + 1);

	/* Retrieve the info log */
	switch (log_type)
	{
	case LT_SHADER_OBJECT:
		gl.getShaderInfoLog(id, n_characters + 1, 0, &result_vec[0]);
		break;
	case LT_PROGRAM_OBJECT:
		gl.getProgramInfoLog(id, n_characters + 1, 0, &result_vec[0]);
		break;
	case LT_PIPELINE_OBJECT:
		gl.getProgramPipelineInfoLog(id, n_characters + 1, 0, &result_vec[0]);
		break;
	}

	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not retrieve info log!");

	return std::string(&result_vec[0]);
}
/** Set contents of texture
 *
 * @param gl              GL functions
 * @param target          Texture target
 * @param level           Mipmap level
 * @param internal_format Format of data
 * @param width           Width of texture
 * @param height          Height of texture
 * @param depth           Depth of texture
 * @param format          Format of data
 * @param type            Type of data
 * @param data            Buffer with image data
 **/
void texImage(const Functions& gl, GLenum target, GLint level, GLenum internal_format, GLuint width, GLuint height,
			  GLuint depth, GLenum format, GLenum type, const GLvoid* data)
{
	switch (target)
	{
	case GL_TEXTURE_2D:
		gl.texImage2D(target, level, internal_format, width, height, 0 /* border */, format, type, data);
		GLU_EXPECT_NO_ERROR(gl.getError(), "texImage");
		break;
	case GL_TEXTURE_2D_ARRAY:
		gl.texImage3D(target, level, internal_format, width, height, depth, 0 /* border */, format, type, data);
		GLU_EXPECT_NO_ERROR(gl.getError(), "texImage");
		break;
	default:
		TCU_FAIL("Invalid enum");
		break;
	}
}
/** Set contents of texture
 *
 * @param gl              GL functions
 * @param target          Texture target
 * @param level           Mipmap level
 * @param x               X offset
 * @param y               Y offset
 * @param z               Z offset
 * @param width           Width of texture
 * @param height          Height of texture
 * @param depth           Depth of texture
 * @param format          Format of data
 * @param type            Type of data
 * @param pixels          Buffer with image data
 **/
void subImage(const Functions& gl, GLenum target, GLint level, GLint x, GLint y, GLint z, GLsizei width, GLsizei height,
			  GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels)
{
	switch (target)
	{
	case GL_TEXTURE_2D:
		gl.texSubImage2D(target, level, x, y, width, height, format, type, pixels);
		GLU_EXPECT_NO_ERROR(gl.getError(), "TexSubImage2D");
		break;
	case GL_TEXTURE_2D_ARRAY:
		gl.texSubImage3D(target, level, x, y, z, width, height, depth, format, type, pixels);
		GLU_EXPECT_NO_ERROR(gl.getError(), "TexSubImage3D");
		break;
	default:
		TCU_FAIL("Invalid enum");
		break;
	}
}
/** Executes test iteration.
 *
 *  @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
 */
tcu::TestNode::IterateResult MultisampleTextureSampleMaskiGettersTest::iterate()
{
	/* Get GL_MAX_SAMPLE_MASK_WORDS value */
	const glw::Functions& gl							 = m_context.getRenderContext().getFunctions();
	glw::GLint			  gl_max_sample_mask_words_value = 0;

	gl.getIntegerv(GL_MAX_SAMPLE_MASK_WORDS, &gl_max_sample_mask_words_value);

	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to retrieve GL_MAX_SAMPLE_MASK_WORDS value");

	/* Iterate over valid index & mask values */
	const glw::GLuint  valid_masks[] = { 0, 0xFFFFFFFF, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF };
	const unsigned int n_valid_masks = sizeof(valid_masks) / sizeof(valid_masks[0]);

	for (int index = 0; index < gl_max_sample_mask_words_value; ++index)
	{
		for (unsigned int n_mask = 0; n_mask < n_valid_masks; ++n_mask)
		{
			glw::GLint mask = valid_masks[n_mask];

			/* Make sure a valid glSampleMaski() call does not result in an error */
			gl.sampleMaski(index, mask);

			GLU_EXPECT_NO_ERROR(gl.getError(), "A valid glSampleMaski() call resulted in an error");

			/* Check the property value as reported by implementation */
			glw::GLint int_value = -1;

			gl.getIntegeri_v(GL_SAMPLE_MASK_VALUE, index, &int_value);
			GLU_EXPECT_NO_ERROR(gl.getError(), "A valid glGetIntegeri_v() call resulted in an error");

			if (int_value != mask)
			{
				TCU_FAIL("Invalid sample mask reported");
			}
		} /* for (all masks) */
	}	 /* for (all valid index argument values) */

	/* Test case passed */
	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");

	return STOP;
}
/** Executes test iteration.
 *
 *  @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
 */
tcu::TestNode::IterateResult MultisampleTextureSampleMaskiIndexEqualToGLMaxSampleMaskWordsTest::iterate()
{
	/* Get GL_MAX_SAMPLE_MASK_WORDS value */
	const glw::Functions& gl							 = m_context.getRenderContext().getFunctions();
	glw::GLint			  gl_max_sample_mask_words_value = 0;

	gl.getIntegerv(GL_MAX_SAMPLE_MASK_WORDS, &gl_max_sample_mask_words_value);

	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to retrieve GL_MAX_SAMPLE_MASK_WORDS value");

	/* Issue call with valid parameters, but invalid index equal to GL_MAX_SAMPLE_MASK_WORDS value */
	gl.sampleMaski(gl_max_sample_mask_words_value, 0);

	if (gl.getError() != GL_INVALID_VALUE)
	{
		TCU_FAIL("Invalid error code reported");
	}

	/* Test case passed */
	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");

	return STOP;
}
Exemplo n.º 11
0
void CompareData::exec (Thread& thread)
{
	bool result = true;
	DE_ASSERT(m_a->getSize() == m_b->getSize());

	thread.newMessage() << "Begin -- CompareData" << Message::End;

	for (int byteNdx = 0; byteNdx < (int)m_a->getSize(); byteNdx++)
	{
		if (m_a->getData()[byteNdx] != m_b->getData()[byteNdx])
		{
			result = false;
			thread.newMessage() << "CompareData failed at offset :" << byteNdx << Message::End;
			break;
		}
	}

	if (result)
		thread.newMessage() << "CompareData passed" << Message::End;
	else
		TCU_FAIL("Data comparision failed");

	thread.newMessage() << "End -- CompareData" << Message::End;
}
Exemplo n.º 12
0
void parseTypePath (const char* nameWithPath, const VarType& type, TypeComponentVector& path)
{
	VarTokenizer tokenizer(nameWithPath);

	if (tokenizer.getToken() == VarTokenizer::TOKEN_IDENTIFIER)
		tokenizer.advance();

	path.clear();
	while (tokenizer.getToken() != VarTokenizer::TOKEN_END)
	{
		VarType curType = getVarType(type, path);

		if (tokenizer.getToken() == VarTokenizer::TOKEN_PERIOD)
		{
			tokenizer.advance();
			TCU_CHECK(tokenizer.getToken() == VarTokenizer::TOKEN_IDENTIFIER);
			TCU_CHECK_MSG(curType.isStructType(), "Invalid field selector");

			// Find member.
			std::string		memberName	= tokenizer.getIdentifier();
			int				ndx			= 0;
			for (; ndx < curType.getStructPtr()->getNumMembers(); ndx++)
			{
				if (memberName == curType.getStructPtr()->getMember(ndx).getName())
					break;
			}
			TCU_CHECK_MSG(ndx < curType.getStructPtr()->getNumMembers(), "Member not found in type");

			path.push_back(VarTypeComponent(VarTypeComponent::STRUCT_MEMBER, ndx));
			tokenizer.advance();
		}
		else if (tokenizer.getToken() == VarTokenizer::TOKEN_LEFT_BRACKET)
		{
			tokenizer.advance();
			TCU_CHECK(tokenizer.getToken() == VarTokenizer::TOKEN_NUMBER);

			int ndx = tokenizer.getNumber();

			if (curType.isArrayType())
			{
				TCU_CHECK(de::inBounds(ndx, 0, curType.getArraySize()));
				path.push_back(VarTypeComponent(VarTypeComponent::ARRAY_ELEMENT, ndx));
			}
			else if (curType.isBasicType() && isDataTypeMatrix(curType.getBasicType()))
			{
				TCU_CHECK(de::inBounds(ndx, 0, getDataTypeMatrixNumColumns(curType.getBasicType())));
				path.push_back(VarTypeComponent(VarTypeComponent::MATRIX_COLUMN, ndx));
			}
			else if (curType.isBasicType() && isDataTypeVector(curType.getBasicType()))
			{
				TCU_CHECK(de::inBounds(ndx, 0, getDataTypeScalarSize(curType.getBasicType())));
				path.push_back(VarTypeComponent(VarTypeComponent::VECTOR_COMPONENT, ndx));
			}
			else
				TCU_FAIL("Invalid subscript");

			tokenizer.advance();
			TCU_CHECK(tokenizer.getToken() == VarTokenizer::TOKEN_RIGHT_BRACKET);
			tokenizer.advance();
		}
		else
			TCU_FAIL("Unexpected token");
	}
}
/** Initializes ES objects necessary to run the test. */
void TessellationShaderQuadsDegenerateCase::initTest()
{
	/* Skip if required extensions are not supported. */
	if (!m_is_tessellation_shader_supported)
	{
		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
	}

	/* Initialize Utils instance */
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	m_utils = new TessellationShaderUtils(gl, this);

	/* Initialize vertex array object */
	gl.genVertexArrays(1, &m_vao_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");

	gl.bindVertexArray(m_vao_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");

	/* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */
	glw::GLint gl_max_tess_gen_level_value = 0;

	gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname");

	/* Initialize all test runs */
	const glw::GLint   tess_levels[] = { -gl_max_tess_gen_level_value / 2, -1, 1 };
	const unsigned int n_tess_levels = sizeof(tess_levels) / sizeof(tess_levels[0]);

	const _tessellation_shader_vertex_spacing vs_modes[] = {
		/* NOTE: We do not check "fractional even" vertex spacing since it will always
		 *       clamp to 2 which is out of scope for this test.
		 */
		TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT,
		TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD
	};
	const unsigned int n_vs_modes = sizeof(vs_modes) / sizeof(vs_modes[0]);

	/* Iterate through all vertex spacing modes */
	bool has_failed = false;

	for (unsigned int n_vs_mode = 0; n_vs_mode < n_vs_modes; ++n_vs_mode)
	{
		_tessellation_shader_vertex_spacing vs_mode = vs_modes[n_vs_mode];

		/* Iterate through all values that should be used for irrelevant tessellation levels */
		for (unsigned int n_tess_level = 0; n_tess_level < n_tess_levels; ++n_tess_level)
		{
			const glw::GLint tess_level = tess_levels[n_tess_level];

			/* Set up the run descriptor.
			 *
			 * Round outer tesellation levels to 1 if necessary, since otherwise no geometry will
			 * be generated.
			 **/
			_run run;

			run.inner[0]	   = (float)tess_level;
			run.inner[1]	   = (float)tess_level;
			run.outer[0]	   = (float)((tess_level < 0) ? 1 : tess_level);
			run.outer[1]	   = (float)((tess_level < 0) ? 1 : tess_level);
			run.outer[2]	   = (float)((tess_level < 0) ? 1 : tess_level);
			run.outer[3]	   = (float)((tess_level < 0) ? 1 : tess_level);
			run.vertex_spacing = vs_mode;

			/* Retrieve vertex data for both passes */
			run.n_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
				TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, run.inner, run.outer, run.vertex_spacing,
				false); /* is_point_mode_enabled */

			if (run.n_vertices == 0)
			{
				std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);

				m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: "
															   "inner tess levels:"
															   "["
								   << run.inner[0] << ", " << run.inner[1] << "]"
																			  ", outer tess levels:"
																			  "["
								   << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
								   << run.outer[3] << "]"
													  ", primitive mode: quads, "
													  "vertex spacing: "
								   << vs_mode_string << tcu::TestLog::EndMessage;

				has_failed = true;
			}
			else
			{
				/* Retrieve the data buffers */
				run.data = m_utils->getDataGeneratedByTessellator(run.inner, false, /* is_point_mode_enabled */
																  TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS,
																  TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
																  run.vertex_spacing, run.outer);
			}

			/* Store the run data */
			m_runs.push_back(run);
		} /* for (all tessellation levels) */
	}	 /* for (all vertex spacing modes) */

	if (has_failed)
	{
		TCU_FAIL("Zero vertices were generated by tessellator for at least one run which is not "
				 "a correct behavior");
	}
}
/* Initializes all ES objects needed to run the test */
void TextureCubeMapArrayColorDepthAttachmentsTest::initTest()
{
	const glw::GLchar*	depth_calculation_code = DE_NULL;
	const glw::Functions& gl					 = m_context.getRenderContext().getFunctions();

	/* Check if EXT_texture_cube_map_array extension is supported */
	if (true != m_is_texture_cube_map_array_supported)
	{
		throw tcu::NotSupportedError(TEXTURE_CUBE_MAP_ARRAY_EXTENSION_NOT_SUPPORTED);
	}

	/* This test should only run if EXT_geometry_shader is supported */
	if (true != m_is_geometry_shader_extension_supported)
	{
		throw tcu::NotSupportedError(GEOMETRY_SHADER_EXTENSION_NOT_SUPPORTED);
	}

	/* Generate and bind VAO */
	gl.genVertexArrays(1, &m_vao_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");

	gl.bindVertexArray(m_vao_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");

	/* Create a framebuffer object */
	gl.genFramebuffers(1, &m_framebuffer_object_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "genFramebuffers");

	/* Determine which depth format can be used as a depth attachment without
	 * making the FBO incomplete */
	determineSupportedDepthFormat();

	/* Decide which code snippet to use for depth value calculation */
	switch (m_depth_internal_format)
	{
	case GL_DEPTH_COMPONENT16:
	{
		depth_calculation_code = "-1.0 + float(2 * layer) / float(0xffff)";

		break;
	}

	case GL_DEPTH_COMPONENT24:
	{
		depth_calculation_code = "-1.0 + float(2 * layer) / float(0xffffff)";

		break;
	}

	case GL_DEPTH_COMPONENT32F:
	{
		depth_calculation_code = "-1.0 + float(2 * layer) / 256.0";

		break;
	}

	default:
	{
		TCU_FAIL("Unrecognized depth internal format");
	}
	} /* switch (m_depth_internal_format) */

	/* Create shader objects */
	m_fragment_shader_id			 = gl.createShader(GL_FRAGMENT_SHADER);
	m_layered_geometry_shader_id	 = gl.createShader(m_glExtTokens.GEOMETRY_SHADER);
	m_non_layered_geometry_shader_id = gl.createShader(m_glExtTokens.GEOMETRY_SHADER);
	m_vertex_shader_id				 = gl.createShader(GL_VERTEX_SHADER);

	GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call(s) failed.");

	/* Create program objects */
	m_layered_program_id	 = gl.createProgram();
	m_non_layered_program_id = gl.createProgram();

	GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call(s) failed");

	/* Build up an array of snippets making up bodies of two geometry shaders
	 * we'll be using for the test.
	 */
	const glw::GLchar* const layered_geometry_shader_parts[] = { m_geometry_shader_code_preamble,
																 m_geometry_shader_code_layered,
																 "    float depth = ", depth_calculation_code,
																 m_geometry_shader_code_body };

	const glw::GLchar* const non_layered_geometry_shader_parts[] = { m_geometry_shader_code_preamble,
																	 m_geometry_shader_code_non_layered,
																	 "    float depth = ", depth_calculation_code,
																	 m_geometry_shader_code_body };

	const glw::GLuint n_layered_geometry_shader_parts =
		sizeof(layered_geometry_shader_parts) / sizeof(layered_geometry_shader_parts[0]);
	const glw::GLuint n_non_layered_geometry_shader_parts =
		sizeof(non_layered_geometry_shader_parts) / sizeof(non_layered_geometry_shader_parts[0]);

	/* Build both programs */
	if (!buildProgram(m_layered_program_id, m_fragment_shader_id, 1, &m_fragment_shader_code,
					  m_layered_geometry_shader_id, n_layered_geometry_shader_parts, layered_geometry_shader_parts,
					  m_vertex_shader_id, 1, &m_vertex_shader_code))
	{
		TCU_FAIL("Could not build layered-case program object");
	}

	if (!buildProgram(m_non_layered_program_id, m_fragment_shader_id, 1, &m_fragment_shader_code,
					  m_non_layered_geometry_shader_id, n_non_layered_geometry_shader_parts,
					  non_layered_geometry_shader_parts, m_vertex_shader_id, 1, &m_vertex_shader_code))
	{
		TCU_FAIL("Could not build non-layered-case program object");
	}

	/* Get location of "uni_layer" uniform */
	m_non_layered_program_id_uni_layer_uniform_location = gl.getUniformLocation(m_non_layered_program_id, "uni_layer");

	if ((-1 == m_non_layered_program_id_uni_layer_uniform_location) || (GL_NO_ERROR != gl.getError()))
	{
		TCU_FAIL("Could not retrieve location of uni_layer uniform for non-layered program");
	}
}
/** Initializes ES objects necessary to run the test. */
void TessellationShaderQuadsInnerTessellationLevelRounding::initTest()
{
	/* Skip if required extensions are not supported. */
	if (!m_is_tessellation_shader_supported)
	{
		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
	}

	/* Initialize Utils instance */
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	m_utils = new TessellationShaderUtils(gl, this);

	/* Initialize vertex array object */
	gl.genVertexArrays(1, &m_vao_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");

	gl.bindVertexArray(m_vao_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");

	/* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */
	glw::GLint gl_max_tess_gen_level_value = 0;

	gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname");

	/* Initialize all test runs */
	const glw::GLint tess_levels[] = { 2, gl_max_tess_gen_level_value / 2, gl_max_tess_gen_level_value };

	const glw::GLint tess_levels_odd[] = { 2 + 1, gl_max_tess_gen_level_value / 2 + 1,
										   gl_max_tess_gen_level_value - 1 };
	const unsigned int n_tess_levels = sizeof(tess_levels) / sizeof(tess_levels[0]);

	const _tessellation_shader_vertex_spacing vs_modes[] = { TESSELLATION_SHADER_VERTEX_SPACING_EQUAL,
															 TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN,
															 TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD };
	const unsigned int n_vs_modes = sizeof(vs_modes) / sizeof(vs_modes[0]);

	/* Iterate through all vertex spacing modes */
	for (unsigned int n_vs_mode = 0; n_vs_mode < n_vs_modes; ++n_vs_mode)
	{
		_tessellation_shader_vertex_spacing vs_mode = vs_modes[n_vs_mode];

		/* Iterate through all values that should be used for irrelevant tessellation levels */
		for (unsigned int n_tess_level = 0; n_tess_level < n_tess_levels; ++n_tess_level)
		{
			/* We need to test two cases in this test:
			 *
			 * a) inner[0] is set to 1 for set A and, for set B, to the value we're expecting the level to
			 *    round, given the vertex spacing mode. inner[1] is irrelevant.
			 * b) inner[0] is irrelevant. inner[1] is set to 1 for set A and, for set B, to the value we're
			 *    expecting the level to round, given the vertex spacing mode.
			 */
			for (unsigned int n_inner_tess_level_combination = 0;
				 n_inner_tess_level_combination < 2; /* inner[0], inner[1] */
				 ++n_inner_tess_level_combination)
			{
				/* Set up the run descriptor */
				glw::GLint tess_level = (vs_mode == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD) ?
											tess_levels_odd[n_tess_level] :
											tess_levels[n_tess_level];
				_run run;

				/* Determine inner tessellation level values for two cases */
				switch (n_inner_tess_level_combination)
				{
				case 0:
				{
					run.set1_inner[0] = 1.0f;
					run.set1_inner[1] = (glw::GLfloat)tess_level;
					run.set2_inner[1] = (glw::GLfloat)tess_level;

					TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
						vs_mode, run.set1_inner[0] + 1.0f /* epsilon */, gl_max_tess_gen_level_value,
						DE_NULL, /* out_clamped */
						run.set2_inner);
					break;
				} /* case 0: */

				case 1:
				{
					run.set1_inner[0] = (glw::GLfloat)tess_level;
					run.set1_inner[1] = 1.0f;
					run.set2_inner[0] = (glw::GLfloat)tess_level;

					TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
						vs_mode, run.set1_inner[1] + 1.0f /* epsilon */, gl_max_tess_gen_level_value,
						DE_NULL, /* out_clamped */
						run.set2_inner + 1);
					break;
				} /* case 1: */

				default:
					TCU_FAIL("Invalid inner tessellation level combination index");
				} /* switch (n_inner_tess_level_combination) */

				/* Configure outer tessellation level values */
				run.set1_outer[0] = (glw::GLfloat)tess_level;
				run.set2_outer[0] = (glw::GLfloat)tess_level;
				run.set1_outer[1] = (glw::GLfloat)tess_level;
				run.set2_outer[1] = (glw::GLfloat)tess_level;
				run.set1_outer[2] = (glw::GLfloat)tess_level;
				run.set2_outer[2] = (glw::GLfloat)tess_level;
				run.set1_outer[3] = (glw::GLfloat)tess_level;
				run.set2_outer[3] = (glw::GLfloat)tess_level;

				/* Set up remaining run properties */
				run.vertex_spacing = vs_mode;

				/* Retrieve vertex data for both passes */
				glw::GLint n_set1_vertices = 0;
				glw::GLint n_set2_vertices = 0;

				n_set1_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
					TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, run.set1_inner, run.set1_outer, run.vertex_spacing,
					false); /* is_point_mode_enabled */
				n_set2_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
					TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, run.set2_inner, run.set2_outer, run.vertex_spacing,
					false); /* is_point_mode_enabled */

				if (n_set1_vertices == 0)
				{
					std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);

					m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: "
																   "inner tess levels:"
																   "["
									   << run.set1_inner[0] << ", " << run.set1_inner[1] << "]"
																							", outer tess levels:"
																							"["
									   << run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2]
									   << ", " << run.set1_outer[3] << "]"
																	   ", primitive mode: quads, "
																	   "vertex spacing: "
									   << vs_mode_string << tcu::TestLog::EndMessage;

					TCU_FAIL("Zero vertices were generated by tessellator for first test pass");
				}

				if (n_set2_vertices == 0)
				{
					std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);

					m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: "
																   "inner tess levels:"
																   "["
									   << run.set2_inner[0] << ", " << run.set2_inner[1] << "]"
																							", outer tess levels:"
																							"["
									   << run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2]
									   << ", " << run.set2_outer[3] << "]"
																	   ", primitive mode: quads, "
																	   "vertex spacing: "
									   << vs_mode_string << tcu::TestLog::EndMessage;

					TCU_FAIL("Zero vertices were generated by tessellator for second test pass");
				}

				if (n_set1_vertices != n_set2_vertices)
				{
					std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);

					m_testCtx.getLog() << tcu::TestLog::Message
									   << "Amount of vertices generated by the tessellator differs"
										  " for the following inner/outer configs: "
										  "inner tess levels:"
										  "["
									   << run.set1_inner[0] << ", " << run.set1_inner[1] << "]"
																							", outer tess levels:"
																							"["
									   << run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2]
									   << ", " << run.set1_outer[3] << "]"
																	   " and inner tess levels:"
																	   "["
									   << run.set2_inner[0] << ", " << run.set2_inner[1] << "]"
																							", outer tess levels:"
																							"["
									   << run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2]
									   << ", " << run.set2_outer[3] << "]"
																	   ", primitive mode: quads, vertex spacing: "
									   << vs_mode_string << tcu::TestLog::EndMessage;

					TCU_FAIL("Amount of vertices generated by tessellator differs between base and references passes");
				}

				/* Store the amount of vertices in run properties */
				run.n_vertices = n_set1_vertices;

				/* Retrieve the data buffers */
				run.set1_data = m_utils->getDataGeneratedByTessellator(
					run.set1_inner, false, /* is_point_mode_enabled */
					TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
					run.vertex_spacing, run.set1_outer);
				run.set2_data = m_utils->getDataGeneratedByTessellator(
					run.set2_inner, false, /* is_point_mode_enabled */
					TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
					run.vertex_spacing, run.set2_outer);

				/* Store the run data */
				m_runs.push_back(run);
			} /* for (all inner tess level combinations) */
		}	 /* for (all sets) */
	}		  /* for (all vertex spacing modes) */
}
/** Executes the test.
 *
 *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
 *
 *  Note the function throws exception should an error occur!
 *
 *  @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
 **/
tcu::TestNode::IterateResult TessellationShaderQuadsInnerTessellationLevelRounding::iterate(void)
{
	/* Do not execute if required extensions are not supported. */
	if (!m_is_tessellation_shader_supported)
	{
		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
	}

	/* Initialize the test */
	initTest();

	/* Iterate through all runs */

	for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++)
	{
		const _run& run = *run_iterator;

		if (run.vertex_spacing != TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD)
		{
			/* Make sure the vertex data generated for two passes matches */
			const glw::GLint n_triangles = run.n_vertices / 3 /* vertices per triangle */;

			std::vector<bool> triangleMatched;
			triangleMatched.assign(n_triangles, false);

			for (int n_triangle = 0; n_triangle < n_triangles; ++n_triangle)
			{
				const float* triangle_a = (const float*)(&run.set1_data[0]) +
										  n_triangle * 3 /* vertices */
											  * 3;		 /* components */
				/* Look up matching triangle */
				bool triangleFound = false;

				for (int n_triangle_b = 0; n_triangle_b < n_triangles; ++n_triangle_b)
				{
					if (!triangleMatched[n_triangle_b])
					{
						const float* triangle_b = (const float*)(&run.set2_data[0]) +
												  n_triangle_b * 3 /* vertices */
													  * 3;		   /* components */

						if (TessellationShaderUtils::isTriangleDefined(triangle_a, triangle_b))
						{
							triangleMatched[n_triangle_b] = true;
							triangleFound				  = true;
							break;
						}
					}
				}

				if (!triangleFound)
				{
					std::string vs_mode_string =
						TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing);

					m_testCtx.getLog() << tcu::TestLog::Message
									   << "The following triangle, generated in the first pass, was not "
										  "generated in the second one. "
										  "First pass' configuration: inner tess levels:"
										  "["
									   << run.set1_inner[0] << ", " << run.set1_inner[1] << "]"
																							", outer tess levels:"
																							"["
									   << run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2]
									   << ", " << run.set1_outer[3]
									   << "]"
										  "; second pass' configuration: inner tess levels:"
										  "["
									   << run.set2_inner[0] << ", " << run.set2_inner[1] << "]"
																							", outer tess levels:"
																							"["
									   << run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2]
									   << ", " << run.set2_outer[3] << "]"
																	   ", primitive mode: quads, vertex spacing: "
									   << vs_mode_string << tcu::TestLog::EndMessage;

					TCU_FAIL("A triangle from first pass' data set was not found in second pass' data set.");
				}
			} /* for (all vertices) */
		}	 /* if (run.vertex_spacing != TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD) */
		else
		{
			/* For Fractional Odd vertex spacing, we cannot apply the above methodology because of the fact
			 * two of the inner edges (the ones subdivided with tessellation level of 1.0) will have been
			 * subdivided differently between two runs, causing the vertex positions to shift, ultimately
			 * leading to isTriangleDefined() failure.
			 *
			 * Hence, we need to take a bit different approach for this case. If you look at a visualization
			 * of a tessellated quad, for which one of the inner tess levels has been set to 1 (let's assume
			 * inner[0] for the sake of discussion), and then looked at a list of points that were generated by the
			 * tessellator, you'll notice that even though it looks like we only have one span of triangles
			 * horizontally, there are actually three. The two outermost spans on each side (the "outer
			 * tessellation regions") are actually degenerate, because they have epsilon spacing allocated to
			 * them.
			 *
			 * Using the theory above, we look for a so-called "marker triangle". In the inner[0] = 1.0 case,
			 * it's one of the two triangles, one edge of which spans horizontally across the whole domain, and
			 * the other two edges touch the outermost edge of the quad. In the inner[1] = 1.0 case, Xs are flipped
			 * with Ys, but the general idea stays the same.
			 *
			 * So for fractional odd vertex spacing, this test verifies that the two marker triangles capping the
			 * opposite ends of the inner quad tessellation region exist.
			 */

			/* Convert arrayed triangle-based representation to a vector storing unique tessellation
			 * coordinates.
			 */
			std::vector<_vec2> set1_tess_coordinates =
				getUniqueTessCoordinatesFromVertexDataSet((const float*)(&run.set1_data[0]), run.n_vertices);

			/* Extract and sort the coordinate components we have from
			 * the minimum to the maximum */
			std::vector<float> set1_tess_coordinates_x_sorted;
			std::vector<float> set1_tess_coordinates_y_sorted;

			for (std::vector<_vec2>::iterator coordinate_iterator = set1_tess_coordinates.begin();
				 coordinate_iterator != set1_tess_coordinates.end(); coordinate_iterator++)
			{
				const _vec2& coordinate = *coordinate_iterator;

				if (std::find(set1_tess_coordinates_x_sorted.begin(), set1_tess_coordinates_x_sorted.end(),
							  coordinate.x) == set1_tess_coordinates_x_sorted.end())
				{
					set1_tess_coordinates_x_sorted.push_back(coordinate.x);
				}

				if (std::find(set1_tess_coordinates_y_sorted.begin(), set1_tess_coordinates_y_sorted.end(),
							  coordinate.y) == set1_tess_coordinates_y_sorted.end())
				{
					set1_tess_coordinates_y_sorted.push_back(coordinate.y);
				}
			} /* for (all tessellation coordinates) */

			std::sort(set1_tess_coordinates_x_sorted.begin(), set1_tess_coordinates_x_sorted.end());
			std::sort(set1_tess_coordinates_y_sorted.begin(), set1_tess_coordinates_y_sorted.end());

			/* Sanity checks */
			DE_ASSERT(set1_tess_coordinates_x_sorted.size() > 2);
			DE_ASSERT(set1_tess_coordinates_y_sorted.size() > 2);

			/* NOTE: This code could have been merged for both cases at the expense of code readability. */
			if (run.set1_inner[0] == 1.0f)
			{
				/* Look for the second horizontal line segment, starting from top and bottom */
				const float second_y_from_top = set1_tess_coordinates_y_sorted[1];
				const float second_y_from_bottom =
					set1_tess_coordinates_y_sorted[set1_tess_coordinates_y_sorted.size() - 2];

				/* In this particular case, there should be exactly one triangle spanning
				 * from U=0 to U=1 at both these heights, with the third coordinate located
				 * at the "outer" edge.
				 */
				for (int n = 0; n < 2 /* cases */; ++n)
				{
					float y1_y2 = 0.0f;
					float y3	= 0.0f;

					if (n == 0)
					{
						y1_y2 = second_y_from_bottom;
						y3	= 1.0f;
					}
					else
					{
						y1_y2 = second_y_from_top;
						y3	= 0.0f;
					}

					/* Try to find the triangle */
					bool has_found_triangle = false;

					DE_ASSERT((run.n_vertices % 3) == 0);

					for (unsigned int n_triangle = 0; n_triangle < run.n_vertices / 3; ++n_triangle)
					{
						const float* vertex1_data = (const float*)(&run.set1_data[0]) +
													3		/* vertices */
														* 3 /* components */
														* n_triangle;
						const float* vertex2_data = vertex1_data + 3 /* components */;
						const float* vertex3_data = vertex2_data + 3 /* components */;

						/* Make sure at least two Y coordinates are equal to y1_y2. */
						const float* y1_vertex_data = DE_NULL;
						const float* y2_vertex_data = DE_NULL;
						const float* y3_vertex_data = DE_NULL;

						if (vertex1_data[1] == y1_y2)
						{
							if (vertex2_data[1] == y1_y2 && vertex3_data[1] == y3)
							{
								y1_vertex_data = vertex1_data;
								y2_vertex_data = vertex2_data;
								y3_vertex_data = vertex3_data;
							}
							else if (vertex2_data[1] == y3 && vertex3_data[1] == y1_y2)
							{
								y1_vertex_data = vertex1_data;
								y2_vertex_data = vertex3_data;
								y3_vertex_data = vertex2_data;
							}
						}
						else if (vertex2_data[1] == y1_y2 && vertex3_data[1] == y1_y2 && vertex1_data[1] == y3)
						{
							y1_vertex_data = vertex2_data;
							y2_vertex_data = vertex3_data;
							y3_vertex_data = vertex1_data;
						}

						if (y1_vertex_data != DE_NULL && y2_vertex_data != DE_NULL && y3_vertex_data != DE_NULL)
						{
							/* Vertex 1 and 2 should span across whole domain horizontally */
							if ((y1_vertex_data[0] == 0.0f && y2_vertex_data[0] == 1.0f) ||
								(y1_vertex_data[0] == 1.0f && y2_vertex_data[0] == 0.0f))
							{
								has_found_triangle = true;

								break;
							}
						}
					} /* for (all triangles) */

					if (!has_found_triangle)
					{
						TCU_FAIL("Could not find a marker triangle");
					}
				} /* for (both cases) */
			}	 /* if (run.set1_inner[0] == 1.0f) */
			else
			{
				DE_ASSERT(run.set1_inner[1] == 1.0f);

				/* Look for the second vertical line segment, starting from left and right */
				const float second_x_from_left = set1_tess_coordinates_x_sorted[1];
				const float second_x_from_right =
					set1_tess_coordinates_x_sorted[set1_tess_coordinates_x_sorted.size() - 2];

				/* In this particular case, there should be exactly one triangle spanning
				 * from V=0 to V=1 at both these widths, with the third coordinate located
				 * at the "outer" edge.
				 */
				for (int n = 0; n < 2 /* cases */; ++n)
				{
					float x1_x2 = 0.0f;
					float x3	= 0.0f;

					if (n == 0)
					{
						x1_x2 = second_x_from_right;
						x3	= 1.0f;
					}
					else
					{
						x1_x2 = second_x_from_left;
						x3	= 0.0f;
					}

					/* Try to find the triangle */
					bool has_found_triangle = false;

					DE_ASSERT((run.n_vertices % 3) == 0);

					for (unsigned int n_triangle = 0; n_triangle < run.n_vertices / 3; ++n_triangle)
					{
						const float* vertex1_data =
							(const float*)(&run.set1_data[0]) + 3 /* vertices */ * 3 /* components */ * n_triangle;
						const float* vertex2_data = vertex1_data + 3 /* components */;
						const float* vertex3_data = vertex2_data + 3 /* components */;

						/* Make sure at least two X coordinates are equal to x1_x2. */
						const float* x1_vertex_data = DE_NULL;
						const float* x2_vertex_data = DE_NULL;
						const float* x3_vertex_data = DE_NULL;

						if (vertex1_data[0] == x1_x2)
						{
							if (vertex2_data[0] == x1_x2 && vertex3_data[0] == x3)
							{
								x1_vertex_data = vertex1_data;
								x2_vertex_data = vertex2_data;
								x3_vertex_data = vertex3_data;
							}
							else if (vertex2_data[0] == x3 && vertex3_data[0] == x1_x2)
							{
								x1_vertex_data = vertex1_data;
								x2_vertex_data = vertex3_data;
								x3_vertex_data = vertex2_data;
							}
						}
						else if (vertex2_data[0] == x1_x2 && vertex3_data[0] == x1_x2 && vertex1_data[0] == x3)
						{
							x1_vertex_data = vertex2_data;
							x2_vertex_data = vertex3_data;
							x3_vertex_data = vertex1_data;
						}

						if (x1_vertex_data != DE_NULL && x2_vertex_data != DE_NULL && x3_vertex_data != DE_NULL)
						{
							/* Vertex 1 and 2 should span across whole domain vertically */
							if ((x1_vertex_data[1] == 0.0f && x2_vertex_data[1] == 1.0f) ||
								(x1_vertex_data[1] == 1.0f && x2_vertex_data[1] == 0.0f))
							{
								has_found_triangle = true;

								break;
							}
						}
					} /* for (all triangles) */

					if (!has_found_triangle)
					{
						TCU_FAIL("Could not find a marker triangle (implies invalid quad tessellation for the case "
								 "considered)");
					}
				} /* for (both cases) */
			}
		}
	} /* for (all runs) */

	/* All done */
	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
	return STOP;
}
/** Verifies layered rendering works correctly.
 *
 * @param texture_size               Resolution of texture
 * @param should_use_mutable_texture true if an immutable texture should be used for
 *                                   the invocation; false if mutable.
 **/
void TextureCubeMapArrayColorDepthAttachmentsTest::testNonLayeredRendering(const _texture_size& texture_size,
																		   bool should_use_mutable_texture)
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	/* Activate a program object that renders in a non-layered fashion */
	gl.useProgram(m_non_layered_program_id);

	GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");

	/* Create relevant textures */
	generateAndConfigureTextureObjects(texture_size.m_size, texture_size.m_n_cubemaps, should_use_mutable_texture);

	/* Iterate over all layer-faces */
	for (glw::GLuint n_layer_face = 0; n_layer_face < texture_size.m_n_cubemaps * 6 /* layer-faces per cube-map */;
		 ++n_layer_face)
	{
		/* Set up non-layered framebuffer attachments */
		configureNonLayeredFramebufferAttachment(m_color_texture_id, n_layer_face, true /* is_color_attachment */);
		configureNonLayeredFramebufferAttachment(m_depth_texture_id, n_layer_face, false /* is_color_attachment */);

		/* Update value assigned to "uni_layer" uniform */
		gl.uniform1i(m_non_layered_program_id_uni_layer_uniform_location, n_layer_face);
		GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform1i() call failed.");

		/* Execute a draw call */
		draw(texture_size);

		/* Restore default framebuffer attachments */
		configureNonLayeredFramebufferAttachment(0 /* texture_id */, 0 /* n_layer */,
												 true /* should_use_as_color_attachment */);
		configureNonLayeredFramebufferAttachment(0 /* texture_id */, 0 /* n_layer */,
												 false /* should_use_as_color_attachment */);

		/* Remove draw framebuffer binding */
		gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() call failed.");

		/* Verify the results. First, make sure the read framebuffer binding is configured
		 * accordingly.
		 */
		gl.bindFramebuffer(GL_READ_FRAMEBUFFER, m_framebuffer_object_id);
		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() call failed.");

		/* Configure read framebuffer attachments to point to the layer of our current interest */
		configureNonLayeredFramebufferAttachment(m_color_texture_id, n_layer_face,
												 true,   /* should_use_as_color_attachment */
												 false); /* should_update_draw_framebuffer */
		configureNonLayeredFramebufferAttachment(m_depth_texture_id, n_layer_face,
												 false,  /* should_use_as_color_attachment */
												 false); /* should_update_draw_framebuffer */

		/* Verify contents of color and depth attachments */
		bool is_color_data_ok = verifyColorData(texture_size, n_layer_face);
		bool is_depth_data_ok = false;

		if (!glu::isContextTypeES(m_context.getRenderContext().getType()))
		{
			switch (m_depth_internal_format)
			{
			case GL_DEPTH_COMPONENT16:
			{
				is_depth_data_ok = verifyDepth16Data(texture_size, n_layer_face);

				break;
			}

			case GL_DEPTH_COMPONENT24:
			{
				is_depth_data_ok = verifyDepth24Data(texture_size, n_layer_face);

				break;
			}

			case GL_DEPTH_COMPONENT32F:
			{
				is_depth_data_ok = verifyDepth32FData(texture_size, n_layer_face);

				break;
			}

			default:
			{
				TCU_FAIL("Unrecognized depth internalformat");
			}
			} /* switch (m_depth_internal_format) */
		}
		else
		{
			is_depth_data_ok = true;
		}

		/* Any errors? Increment relevant counters */
		if (false == is_color_data_ok)
		{
			m_n_invalid_color_checks++;
		}

		if (false == is_depth_data_ok)
		{
			m_n_invalid_depth_checks++;
		}
	} /* for (all layer-faces) */
}
/** Executes the test.
 *
 *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
 *
 *  Note the function throws exception should an error occur!
 *
 *  @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
 **/
tcu::TestNode::IterateResult TessellationShaderQuadsDegenerateCase::iterate(void)
{
	/* Do not execute if required extensions are not supported. */
	if (!m_is_tessellation_shader_supported)
	{
		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
	}

	/* Initialize the test */
	initTest();

	/* Iterate through all runs */

	/* The test fails if any of the runs did not generate exactly 6 coordinates */
	for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++)
	{
		const _run& run = *run_iterator;

		if (run.n_vertices != (2 /* triangles */ * 3 /* vertices */))
		{
			std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing);

			m_testCtx.getLog() << tcu::TestLog::Message << "Invalid number of coordinates (" << run.n_vertices
							   << ", instead of 6)"
								  " was generated for the following tessellation configuration: "
								  "primitive mode:quads, "
								  "vertex spacing mode:"
							   << vs_mode_string << " inner tessellation levels:" << run.inner[0] << ", "
							   << run.inner[1] << " outer tessellation levels:" << run.outer[0] << ", " << run.outer[1]
							   << ", " << run.outer[2] << ", " << run.outer[3] << tcu::TestLog::EndMessage;

			TCU_FAIL("Invalid number of coordinates was generated for at least one run");
		}
	} /* for (all runs) */

	/* All runs should generate exactly the same set of triangles.
	 *
	 * Note: we must not assume any specific vertex ordering, so we cannot
	 *       just do a plain memcmp() here.
	 */
	const _run& base_run = *m_runs.begin();

	for (unsigned int n_triangle = 0; n_triangle < base_run.n_vertices / 3 /* vertices per triangle */; n_triangle++)
	{
		const float* base_triangle_data = (const float*)(&base_run.data[0]) +
										  3		  /* vertices per triangle */
											  * 3 /* components */
											  * n_triangle;

		for (_runs_const_iterator ref_run_iterator = m_runs.begin() + 1; ref_run_iterator != m_runs.end();
			 ref_run_iterator++)
		{
			const _run& ref_run = *ref_run_iterator;

			const float* ref_triangle_data1 = (const float*)(&ref_run.data[0]);
			const float* ref_triangle_data2 =
				(const float*)(&ref_run.data[0]) + 3 /* vertices per triangle */ * 3 /* components */;

			if (!TessellationShaderUtils::isTriangleDefined(base_triangle_data, ref_triangle_data1) &&
				!TessellationShaderUtils::isTriangleDefined(base_triangle_data, ref_triangle_data2))
			{
				std::string base_vs_mode_string =
					TessellationShaderUtils::getESTokenForVertexSpacingMode(base_run.vertex_spacing);
				std::string ref_vs_mode_string =
					TessellationShaderUtils::getESTokenForVertexSpacingMode(ref_run.vertex_spacing);

				m_testCtx.getLog() << tcu::TestLog::Message
								   << "Reference run does not contain a triangle found in a base run"
									  " generated for the following tessellation configuration: "
									  "primitive mode:quads, "
									  "base vertex spacing mode:"
								   << base_vs_mode_string << " base inner tessellation levels:" << base_run.inner[0]
								   << ", " << base_run.inner[1]
								   << " base outer tessellation levels:" << base_run.outer[0] << ", "
								   << base_run.outer[1] << ", " << base_run.outer[2] << ", " << base_run.outer[3]
								   << "reference vertex spacing mode:" << ref_vs_mode_string
								   << " reference inner tessellation levels:" << ref_run.inner[0] << ", "
								   << ref_run.inner[1] << " reference outer tessellation levels:" << ref_run.outer[0]
								   << ", " << ref_run.outer[1] << ", " << ref_run.outer[2] << ", " << ref_run.outer[3]
								   << tcu::TestLog::EndMessage;

				TCU_FAIL("Reference run does not contain a triangle found in a base run");
			}
		} /* for (all reference runs) */
	}	 /* for (all triangles) */

	/* All done */
	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
	return STOP;
}
/** @brief Function builds test's GLSL program.
 *         If succeded, the program will be set to be used.
 *
 *  @note The function may throw if unexpected error has occured.
 */
void TextureCubeMapArrayETC2Support::prepareProgram()
{
	/* Shortcut for GL functionality */
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	struct Shader
	{
		glw::GLchar const* const source;
		glw::GLenum const		 type;
		glw::GLuint				 id;
	} shader[] = { { s_vertex_shader, GL_VERTEX_SHADER, 0 }, { s_fragment_shader, GL_FRAGMENT_SHADER, 0 } };

	bool			  programPreparationFailed = false;
	glw::GLuint const shader_count			   = sizeof(shader) / sizeof(shader[0]);

	/* Create program. */
	m_program = gl.createProgram();
	GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram call failed.");

	/* Shader compilation. */
	for (glw::GLuint i = 0; i < shader_count; ++i)
	{
		if (DE_NULL != shader[i].source)
		{
			shader[i].id = gl.createShader(shader[i].type);

			GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader call failed.");

			gl.attachShader(m_program, shader[i].id);

			GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader call failed.");

			glu::ContextType contextType = m_context.getRenderContext().getType();
			glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(contextType);
			std::string		 shaderSource(glu::getGLSLVersionDeclaration(glslVersion));
			shaderSource += shader[i].source;
			const char* source = shaderSource.c_str();

			gl.shaderSource(shader[i].id, 1, &source, NULL);

			GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource call failed.");

			gl.compileShader(shader[i].id);

			GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader call failed.");

			glw::GLint status = GL_FALSE;

			gl.getShaderiv(shader[i].id, GL_COMPILE_STATUS, &status);
			GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed.");

			if (GL_FALSE == status)
			{
				glw::GLint log_size = 0;
				gl.getShaderiv(shader[i].id, GL_INFO_LOG_LENGTH, &log_size);
				GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed.");

				glw::GLchar* log_text = new glw::GLchar[log_size];

				gl.getShaderInfoLog(shader[i].id, log_size, NULL, &log_text[0]);

				m_context.getTestContext().getLog() << tcu::TestLog::Message << "Shader compilation error, log:\n"
													<< log_text << "\n"
													<< "Shader source code:\n"
													<< shaderSource << "\n"
													<< tcu::TestLog::EndMessage;

				delete[] log_text;

				GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderInfoLog call failed.");

				programPreparationFailed = true;
				break;
			}
		}
	}

	if (programPreparationFailed)
	{
		if (m_program)
		{
			gl.deleteProgram(m_program);
			m_program = 0;
		}
	}
	else
	{
		/* Link. */
		gl.linkProgram(m_program);

		GLU_EXPECT_NO_ERROR(gl.getError(), "glLlinkProgram call failed.");

		glw::GLint status = GL_FALSE;

		gl.getProgramiv(m_program, GL_LINK_STATUS, &status);

		if (GL_TRUE == status)
		{
			for (glw::GLuint i = 0; i < shader_count; ++i)
			{
				if (shader[i].id)
				{
					gl.detachShader(m_program, shader[i].id);

					GLU_EXPECT_NO_ERROR(gl.getError(), "glDetachShader call failed.");
				}
			}
		}
		else
		{
			glw::GLint log_size = 0;

			gl.getProgramiv(m_program, GL_INFO_LOG_LENGTH, &log_size);

			GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv call failed.");

			glw::GLchar* log_text = new glw::GLchar[log_size];

			gl.getProgramInfoLog(m_program, log_size, NULL, &log_text[0]);

			m_context.getTestContext().getLog() << tcu::TestLog::Message << "Program linkage has failed due to:\n"
												<< log_text << "\n"
												<< tcu::TestLog::EndMessage;

			delete[] log_text;

			GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInfoLog call failed.");

			programPreparationFailed = true;
		}
	}

	for (glw::GLuint i = 0; i < shader_count; ++i)
	{
		if (0 != shader[i].id)
		{
			gl.deleteShader(shader[i].id);
			shader[i].id = 0;
		}
	}

	if (m_program)
	{
		glw::GLint textureSampler = gl.getUniformLocation(m_program, "texture_sampler");
		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformLocation call failed.");
		gl.useProgram(m_program);
		GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed.");
		gl.uniform1i(textureSampler, 0);
		GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform1i call failed.");
	}
	else
		TCU_FAIL("Failed to prepare program");
}
Exemplo n.º 20
0
void ShaderBallotBaseTestCase::ShaderPipeline::executeComputeShader(deqp::Context& context)
{
	const glw::Functions& gl = context.getRenderContext().getFunctions();

	const glu::Texture outputTexture(context.getRenderContext());

	gl.useProgram(m_programCompute->getProgram());

	// output image
	gl.bindTexture(GL_TEXTURE_2D, *outputTexture);
	gl.texStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32UI, 16, 16);
	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Uploading image data failed");

	// bind image
	gl.bindImageTexture(1, *outputTexture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32UI);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Image setup failed");

	// dispatch compute
	gl.dispatchCompute(1, 1, 1);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute()");

	gl.memoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glMemoryBarrier()");

	// render output texture

	std::string vs = "#version 450 core\n"
					 "in highp vec2 position;\n"
					 "in vec2 inTexcoord;\n"
					 "out vec2 texcoord;\n"
					 "void main()\n"
					 "{\n"
					 "	texcoord = inTexcoord;\n"
					 "	gl_Position = vec4(position, 0.0, 1.0);\n"
					 "}\n";

	std::string fs = "#version 450 core\n"
					 "uniform sampler2D sampler;\n"
					 "in vec2 texcoord;\n"
					 "out vec4 color;\n"
					 "void main()\n"
					 "{\n"
					 "	color = texture(sampler, texcoord);\n"
					 "}\n";

	glu::ProgramSources sources;
	sources.sources[glu::SHADERTYPE_VERTEX].push_back(vs);
	sources.sources[glu::SHADERTYPE_FRAGMENT].push_back(fs);
	glu::ShaderProgram renderShader(context.getRenderContext(), sources);

	if (!m_programRender->isOk())
	{
		TCU_FAIL("Shader compilation failed");
	}

	gl.bindTexture(GL_TEXTURE_2D, *outputTexture);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture() call failed.");

	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

	gl.useProgram(renderShader.getProgram());

	gl.uniform1i(gl.getUniformLocation(renderShader.getProgram(), "sampler"), 0);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform1i failed");

	deUint16 const quadIndices[] = { 0, 1, 2, 2, 1, 3 };

	float const position[] = { -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f };

	float const texCoord[] = { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f };

	glu::VertexArrayBinding vertexArrays[] = { glu::va::Float("position", 2, 4, 0, position),
											   glu::va::Float("inTexcoord", 2, 4, 0, texCoord) };

	glu::draw(context.getRenderContext(), renderShader.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), vertexArrays,
			  glu::pr::TriangleStrip(DE_LENGTH_OF_ARRAY(quadIndices), quadIndices));

	GLU_EXPECT_NO_ERROR(gl.getError(), "glu::draw error");
}
/** Initializes all ES objects necessary to run a specific test pass.
 *
 *  @param test           Test descriptor to fill with IDs of initialized objects.
 *  @param vertex_spacing Vertex spacing mode to use for the run.
 **/
void TessellationShaderTrianglesDegenerateTriangle::initTestDescriptor(
	_test_descriptor& test, _tessellation_shader_vertex_spacing vertex_spacing)
{
	test.vertex_spacing = vertex_spacing;

	/* Set up a program object for the descriptor */
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	test.po_id = gl.createProgram();
	GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed");

	/* Set up a pass-specific tessellation evaluation shader object. */
	test.te_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() failed");

	/* Configure tessellation evaluation shader body */
	const char* te_template = "${VERSION}\n"
							  "\n"
							  "${TESSELLATION_SHADER_REQUIRE}\n"
							  "\n"
							  "layout (triangles, VERTEX_SPACING_MODE) in;\n"
							  "\n"
							  "out vec3 result_uvw;\n"
							  "\n"
							  "void main()\n"
							  "{\n"
							  "    gl_Position = gl_in[0].gl_Position;\n"
							  "    result_uvw  = gl_TessCoord;\n"
							  "}\n";

	const char* te_body_raw_ptr = DE_NULL;
	std::string te_body_string  = te_template;
	std::string vertex_spacing_mode_string;
	const char* vertex_spacing_token	   = "VERTEX_SPACING_MODE";
	std::size_t vertex_spacing_token_index = std::string::npos;

	switch (vertex_spacing)
	{
	case TESSELLATION_SHADER_VERTEX_SPACING_EQUAL:
	{
		vertex_spacing_mode_string = "equal_spacing";

		break;
	}

	case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD:
	{
		vertex_spacing_mode_string = "fractional_odd_spacing";

		break;
	}

	default:
	{
		TCU_FAIL("Invalid vertex spacing mode requested");
	}
	}

	while ((vertex_spacing_token_index = te_body_string.find(vertex_spacing_token)) != std::string::npos)
	{
		te_body_string = te_body_string.replace(vertex_spacing_token_index, strlen(vertex_spacing_token),
												vertex_spacing_mode_string);

		vertex_spacing_token_index = te_body_string.find(vertex_spacing_token);
	}

	te_body_raw_ptr = te_body_string.c_str();

	shaderSourceSpecialized(test.te_id, 1 /* count */, &te_body_raw_ptr);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation evaluation shader");

	/* Compile the tessellation evaluation shader */
	glw::GLint compile_status = GL_FALSE;

	gl.compileShader(test.te_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() failed for tessellation evaluation shader");

	gl.getShaderiv(test.te_id, GL_COMPILE_STATUS, &compile_status);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() failed for tessellation evaluation shader");

	if (compile_status != GL_TRUE)
	{
		TCU_FAIL("Tessellation evaluation shader compilation failed");
	}

	/* Attach all shader to the program object */
	gl.attachShader(test.po_id, m_fs_id);
	gl.attachShader(test.po_id, m_tc_id);
	gl.attachShader(test.po_id, test.te_id);
	gl.attachShader(test.po_id, m_vs_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() failed");

	/* Set up XFB */
	const char*		   varyings[] = { "result_uvw" };
	const unsigned int n_varyings = sizeof(varyings) / sizeof(varyings[0]);

	gl.transformFeedbackVaryings(test.po_id, n_varyings, varyings, GL_INTERLEAVED_ATTRIBS);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() failed");

	/* Link the program object */
	glw::GLint link_status = GL_FALSE;

	gl.linkProgram(test.po_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() failed");

	gl.getProgramiv(test.po_id, GL_LINK_STATUS, &link_status);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() failed");

	if (link_status != GL_TRUE)
	{
		TCU_FAIL("Program linking failed");
	}
}
/** Initializes ES objects necessary to run the test. */
void TessellationShaderTrianglesDegenerateTriangle::initTest()
{
	/* Skip if required extensions are not supported. */
	if (!m_is_tessellation_shader_supported)
	{
		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
	}

	/* Generate all test-wide objects needed for test execution */
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	gl.genVertexArrays(1, &m_vao_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");

	gl.bindVertexArray(m_vao_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");

	gl.genBuffers(1, &m_bo_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() failed");

	m_fs_id = gl.createShader(GL_FRAGMENT_SHADER);
	m_tc_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER);
	m_vs_id = gl.createShader(GL_VERTEX_SHADER);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() failed");

	/* Configure fragment shader body */
	const char* fs_body = "${VERSION}\n"
						  "\n"
						  "void main()\n"
						  "{\n"
						  "}\n";

	shaderSourceSpecialized(m_fs_id, 1 /* count */, &fs_body);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for fragment shader");

	/* Configure tessellation control shader body */
	const char* tc_body = "${VERSION}\n"
						  "\n"
						  "${TESSELLATION_SHADER_REQUIRE}\n"
						  "\n"
						  "layout (vertices=3) out;\n"
						  "\n"
						  "void main()\n"
						  "{\n"
						  "    gl_out [gl_InvocationID].gl_Position = gl_in[0].gl_Position;\n"
						  "\n"
						  "    gl_TessLevelInner[0] = 1.0;\n"
						  "    gl_TessLevelOuter[0] = 1.0;\n"
						  "    gl_TessLevelOuter[1] = 1.0;\n"
						  "    gl_TessLevelOuter[2] = 1.0;\n"
						  "}\n";

	shaderSourceSpecialized(m_tc_id, 1 /* count */, &tc_body);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for tessellation control shader");

	/* Configure vertex shader body */
	const char* vs_body = "${VERSION}\n"
						  "\n"
						  "void main()\n"
						  "{\n"
						  "    gl_Position = vec4(1.0, 0.0, 0.0, 1.0);\n"
						  "}\n";

	shaderSourceSpecialized(m_vs_id, 1 /* count */, &vs_body);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for vertex shader");

	/* Compile all the shaders */
	const glw::GLuint  shaders[] = { m_fs_id, m_tc_id, m_vs_id };
	const unsigned int n_shaders = sizeof(shaders) / sizeof(shaders[0]);

	for (unsigned int n_shader = 0; n_shader < n_shaders; ++n_shader)
	{
		glw::GLuint shader = shaders[n_shader];

		if (shader != 0)
		{
			glw::GLint compile_status = GL_FALSE;

			gl.compileShader(shader);
			GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() failed");

			gl.getShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
			GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() failed");

			if (compile_status != GL_TRUE)
			{
				TCU_FAIL("Shader compilation failed");
			}
		}
	} /* for (all shaders) */

	/* Initialize all test passes */
	_test_descriptor test_equal_spacing;
	_test_descriptor test_fractional_odd_spacing;

	initTestDescriptor(test_equal_spacing, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL);
	initTestDescriptor(test_fractional_odd_spacing, TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD);

	m_tests.push_back(test_equal_spacing);
	m_tests.push_back(test_fractional_odd_spacing);

	/* Set up buffer object storage */
	gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() failed");

	gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(float) * 3 /* components */ * 3 /* UVW sets */, NULL, /* data */
				  GL_STATIC_DRAW);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() failed");

	/* Bind the buffer object to indiced TF binding point */
	gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_bo_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() failed");
}
/** Executes the test.
 *
 *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
 *
 *  Note the function throws exception should an error occur!
 *
 *  @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
 **/
tcu::TestNode::IterateResult TessellationShaderTrianglesDegenerateTriangle::iterate(void)
{
	/* Do not execute if required extensions are not supported. */
	if (!m_is_tessellation_shader_supported)
	{
		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
	}

	/* Initialize ES test objects */
	initTest();

	/* We only need to use one vertex per so go for it */
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 1);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() failed for GL_PATCH_VERTICES_EXT pname");

	/* Iterate through all tests configured */
	for (_tests_const_iterator test_iterator = m_tests.begin(); test_iterator != m_tests.end(); test_iterator++)
	{
		const _test_descriptor& test = *test_iterator;

		/* Run the iteration */
		gl.useProgram(test.po_id);
		GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed");

		/* Draw the test geometry */
		gl.beginTransformFeedback(GL_TRIANGLES);
		GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback(GL_TRIANGLES) failed.");

		gl.drawArrays(m_glExtTokens.PATCHES, 0 /* first */, 1 /* count */);
		GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() failed");

		gl.endTransformFeedback();
		GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() failed");

		/* Map the BO with result data into user space */
		const float* triangle_vertex_data =
			(const float*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */
											sizeof(float) * 3 /* vec3 */ * 3 /* points */, GL_MAP_READ_BIT);

		GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() call failed");

		/* Make sure the triangle data is correct. Since we cannot rely on any specific order
		 * of the result vertices, raise corresponding flag for each of the expected vertices.
		 */
		const float epsilon					 = 1e-5f;
		bool		is_zero_zero_one_present = false;
		bool		is_zero_one_zero_present = false;
		bool		is_one_zero_zero_present = false;

		for (unsigned int n_vertex = 0; n_vertex < 3 /* vertices */; ++n_vertex)
		{
			const float* triangle_ptr = triangle_vertex_data + 3 * n_vertex;

			if (de::abs(triangle_ptr[0]) < epsilon && de::abs(triangle_ptr[1]) < epsilon &&
				de::abs(triangle_ptr[2] - 1.0f) < epsilon)
			{
				if (!is_zero_zero_one_present)
				{
					is_zero_zero_one_present = true;
				}
				else
				{
					TCU_FAIL("(0, 0, 1) vertex outputted more than once");
				}
			}
			else if (de::abs(triangle_ptr[0]) < epsilon && de::abs(triangle_ptr[1] - 1.0f) < epsilon &&
					 de::abs(triangle_ptr[2]) < epsilon)
			{
				if (!is_zero_one_zero_present)
				{
					is_zero_one_zero_present = true;
				}
				else
				{
					TCU_FAIL("(0, 1, 0) vertex outputted more than once");
				}
			}
			else if (de::abs(triangle_ptr[0] - 1.0f) < epsilon && de::abs(triangle_ptr[1]) < epsilon &&
					 de::abs(triangle_ptr[2]) < epsilon)
			{
				if (!is_one_zero_zero_present)
				{
					is_one_zero_zero_present = true;
				}
				else
				{
					TCU_FAIL("(1, 0, 0) vertex outputted more than once");
				}
			}
			else
			{
				m_testCtx.getLog() << tcu::TestLog::Message << "Unexpected vertex"
								   << " (" << triangle_vertex_data[0] << ", " << triangle_vertex_data[1] << ", "
								   << triangle_vertex_data[2] << ")"
								   << " encountered for a degenerate triangle." << tcu::TestLog::EndMessage;

				TCU_FAIL("Invalid vertex was generated by the tessellator");
			}
		} /* for (all vertices) */

		/* Unmap the BO */
		gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
		GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed");
	}

	/* All done */
	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
	return STOP;
}
/** Initializes ES objects necessary to run the test. */
void TessellationShaderTrianglesIdenticalTriangles::initTest()
{
	/* Skip if required extensions are not supported. */
	if (!m_is_tessellation_shader_supported)
	{
		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
	}

	/* Initialize Utils instance */
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	m_utils = new TessellationShaderUtils(gl, this);

	/* Initialize vertex array object */
	gl.genVertexArrays(1, &m_vao_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");

	gl.bindVertexArray(m_vao_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");

	/* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */
	glw::GLint gl_max_tess_gen_level_value = 0;

	gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname");

	/* Initialize all test runs */
	_tessellation_level_set_filter filter =
		(_tessellation_level_set_filter)((int)TESSELLATION_LEVEL_SET_FILTER_ALL_COMBINATIONS |
										 (int)TESSELLATION_LEVEL_SET_FILTER_EXCLUDE_NEGATIVE_BASE_VALUE);

	_tessellation_levels_set levels_sets = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode(
		TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, gl_max_tess_gen_level_value, filter);

	for (_tessellation_levels_set_const_iterator set_iterator = levels_sets.begin(); set_iterator != levels_sets.end();
		 set_iterator++)
	{
		_run						run;
		const _tessellation_levels& set = *set_iterator;

		memcpy(run.base_inner, set.inner, sizeof(run.base_inner));
		memcpy(run.base_outer, set.outer, sizeof(run.base_outer));

		run.reference_inner[0] = run.base_inner[0];
		run.reference_inner[1] = run.base_inner[1] * 0.25f;

		run.reference_outer[0] = run.base_outer[0];
		run.reference_outer[1] = run.base_outer[1];
		run.reference_outer[2] = run.base_outer[2];
		run.reference_outer[3] = run.base_outer[3] * 0.25f;

		/* Retrieve vertex data for both passes */
		glw::GLint n_base_vertices		= 0;
		glw::GLint n_reference_vertices = 0;

		n_base_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
			TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, run.base_inner, run.base_outer,
			TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, false);
		n_reference_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
			TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, run.reference_inner, run.reference_outer,
			TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, false); /* is_point_mode_enabled */

		if (n_base_vertices == 0)
		{
			m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: "
														   "inner tess levels:"
														   "["
							   << run.base_inner[0] << ", " << run.base_inner[1] << "]"
																					", outer tess levels:"
																					"["
							   << run.base_outer[0] << ", " << run.base_outer[1] << ", " << run.base_outer[2] << ", "
							   << run.base_outer[3] << "]"
													   ", primitive mode: triangles, vertex spacing: equal."
							   << tcu::TestLog::EndMessage;

			TCU_FAIL("Zero vertices were generated by tessellator for base test pass");
		}

		if (n_reference_vertices == 0)
		{
			m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: "
														   "inner tess levels:"
														   "["
							   << run.reference_inner[0] << ", " << run.reference_inner[1] << "]"
																							  ", outer tess levels:"
																							  "["
							   << run.reference_outer[0] << ", " << run.reference_outer[1] << ", "
							   << run.reference_outer[2] << ", " << run.reference_outer[3]
							   << "]"
								  ", primitive mode: triangles, vertex spacing: equal."
							   << tcu::TestLog::EndMessage;

			TCU_FAIL("Zero vertices were generated by tessellator for reference test pass");
		}

		if (n_base_vertices != n_reference_vertices)
		{
			m_testCtx.getLog() << tcu::TestLog::Message << "Amount of vertices generated by the tessellator differs"
														   " for the following inner/outer configs: "
														   "inner tess levels:"
														   "["
							   << run.base_inner[0] << ", " << run.base_inner[1] << "]"
																					", outer tess levels:"
																					"["
							   << run.base_outer[0] << ", " << run.base_outer[1] << ", " << run.base_outer[2] << ", "
							   << run.base_outer[3] << "]"
													   " and inner tess levels:"
													   "["
							   << run.reference_inner[0] << ", " << run.reference_inner[1] << "]"
																							  ", outer tess levels:"
																							  "["
							   << run.reference_outer[0] << ", " << run.reference_outer[1] << ", "
							   << run.reference_outer[2] << ", " << run.reference_outer[3]
							   << "]"
								  ", primitive mode: triangles, vertex spacing: equal."
							   << tcu::TestLog::EndMessage;

			TCU_FAIL("Amount of vertices generated by tessellator differs between base and references passes");
		}

		run.n_vertices = n_base_vertices;

		run.base_data = m_utils->getDataGeneratedByTessellator(
			run.base_inner, false, /* is_point_mode_enabled */
			TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
			TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, run.base_outer);
		run.reference_data = m_utils->getDataGeneratedByTessellator(
			run.reference_inner, false, /* is_point_mode_enabled */
			TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
			TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, run.reference_outer);

		/* Store the run data */
		m_runs.push_back(run);
	} /* for (all sets) */
}
/** Executes the test.
 *
 *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
 *
 *  Note the function throws exception should an error occur!
 *
 *  @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
 **/
tcu::TestNode::IterateResult TessellationShaderTrianglesIdenticalTriangles::iterate(void)
{
	/* Do not execute if required extensions are not supported. */
	if (!m_is_tessellation_shader_supported)
	{
		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
	}

	/* Initialize the test */
	initTest();

	/* Iterate through all runs */

	for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++)
	{
		const _run& run = *run_iterator;

		/* Make sure the vertex data generated for two passes matches */
		const glw::GLint n_triangles = run.n_vertices / 3 /* vertices per triangle */;

		for (int n_triangle = 0; n_triangle < n_triangles; ++n_triangle)
		{
			const float* triangle_a = (const float*)(&run.base_data[0]) +
									  n_triangle * 3 /* vertices */
										  * 3;		 /* components */
			const float* triangle_b = (const float*)(&run.reference_data[0]) +
									  n_triangle * 3 /* vertices */
										  * 3;		 /* components */

			if (!TessellationShaderUtils::isTriangleDefined(triangle_a, triangle_b))
			{
				m_testCtx.getLog() << tcu::TestLog::Message
								   << "The following triangle, generated in the first pass, was not "
									  "generated in the other. "
									  "First pass' configuration: inner tess levels:"
									  "["
								   << run.base_inner[0] << ", " << run.base_inner[1] << "]"
																						", outer tess levels:"
																						"["
								   << run.base_outer[0] << ", " << run.base_outer[1] << ", " << run.base_outer[2]
								   << ", " << run.base_outer[3] << "]"
																   "; second pass' configuration: inner tess levels:"
																   "["
								   << run.reference_inner[0] << ", " << run.reference_inner[1] << "]"
																								  ", outer tess levels:"
																								  "["
								   << run.reference_outer[0] << ", " << run.reference_outer[1] << ", "
								   << run.reference_outer[2] << ", " << run.reference_outer[3]
								   << "]"
									  ", primitive mode: triangles, vertex spacing: equal."
								   << tcu::TestLog::EndMessage;

				TCU_FAIL("A triangle from base vertex data set was not found in reference vertex data set.");
			}
		} /* for (all vertices) */
	}	 /* for (all runs) */

	/* All done */
	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
	return STOP;
}
/** Runs all test iterations needed to generate data for later verification. */
void TessellationShaderTrianglesInnerTessellationLevelRounding::runTestIterations()
{
	/* Skip if required extensions are not supported. */
	if (!m_is_tessellation_shader_supported)
	{
		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
	}

	/* Initialize Utils instance */
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	m_utils = new TessellationShaderUtils(gl, this);

	/* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */
	glw::GLint gl_max_tess_gen_level_value = 0;

	gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname");

	/* Initialize all test runs */
	const glw::GLint   tess_levels[] = { 2, gl_max_tess_gen_level_value / 2, gl_max_tess_gen_level_value };
	const unsigned int n_tess_levels = sizeof(tess_levels) / sizeof(tess_levels[0]);

	const _tessellation_shader_vertex_spacing vs_modes[] = { TESSELLATION_SHADER_VERTEX_SPACING_EQUAL,
															 TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN,
															 TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD };
	const unsigned int n_vs_modes = sizeof(vs_modes) / sizeof(vs_modes[0]);

	for (unsigned int n_vs_mode = 0; n_vs_mode < n_vs_modes; ++n_vs_mode)
	{
		_tessellation_shader_vertex_spacing vs_mode = vs_modes[n_vs_mode];

		for (unsigned int n_tess_level = 0; n_tess_level < n_tess_levels; ++n_tess_level)
		{
			/* Set up the run descriptor */
			glw::GLint tess_level = tess_levels[n_tess_level];
			_run	   run;

			run.set1_inner[0] = 1.0f;
			run.set1_inner[1] = 0.0f;
			run.set2_inner[1] = 0.0f;

			TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(vs_mode, 1.5f, gl_max_tess_gen_level_value,
																			DE_NULL, /* out_clamped */
																			run.set2_inner);
			run.set1_outer[0] = (glw::GLfloat)tess_level;
			run.set2_outer[0] = (glw::GLfloat)tess_level;
			run.set1_outer[1] = (glw::GLfloat)tess_level;
			run.set2_outer[1] = (glw::GLfloat)tess_level;
			run.set1_outer[2] = (glw::GLfloat)tess_level;
			run.set2_outer[2] = (glw::GLfloat)tess_level;
			run.set1_outer[3] = (glw::GLfloat)tess_level;
			run.set2_outer[3] = (glw::GLfloat)tess_level;

			run.vertex_spacing = vs_mode;

			/* Retrieve vertex data for both passes */
			glw::GLint n_set1_vertices = 0;
			glw::GLint n_set2_vertices = 0;

			n_set1_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
				TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, run.set1_inner, run.set1_outer, run.vertex_spacing,
				false); /* is_point_mode_enabled */
			n_set2_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
				TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES, run.set2_inner, run.set2_outer, run.vertex_spacing,
				false); /* is_point_mode_enabled */

			if (n_set1_vertices == 0)
			{
				std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);

				m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: "
															   "inner tess levels:"
															   "["
								   << run.set1_inner[0] << ", " << run.set1_inner[1] << "]"
																						", outer tess levels:"
																						"["
								   << run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2]
								   << ", " << run.set1_outer[3] << "]"
																   ", primitive mode: triangles, "
																   "vertex spacing: "
								   << vs_mode_string << tcu::TestLog::EndMessage;

				TCU_FAIL("Zero vertices were generated by tessellator for first test pass");
			}

			if (n_set2_vertices == 0)
			{
				std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);

				m_testCtx.getLog() << tcu::TestLog::Message << "No vertices were generated by tessellator for: "
															   "inner tess levels:"
															   "["
								   << run.set2_inner[0] << ", " << run.set2_inner[1] << "]"
																						", outer tess levels:"
																						"["
								   << run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2]
								   << ", " << run.set2_outer[3] << "]"
																   ", primitive mode: triangles, "
																   "vertex spacing: "
								   << vs_mode_string << tcu::TestLog::EndMessage;

				TCU_FAIL("Zero vertices were generated by tessellator for second test pass");
			}

			if (n_set1_vertices != n_set2_vertices)
			{
				std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);

				m_testCtx.getLog() << tcu::TestLog::Message << "Amount of vertices generated by the tessellator differs"
															   " for the following inner/outer configs: "
															   "inner tess levels:"
															   "["
								   << run.set1_inner[0] << ", " << run.set1_inner[1] << "]"
																						", outer tess levels:"
																						"["
								   << run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2]
								   << ", " << run.set1_outer[3] << "]"
																   " and inner tess levels:"
																   "["
								   << run.set2_inner[0] << ", " << run.set2_inner[1] << "]"
																						", outer tess levels:"
																						"["
								   << run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2]
								   << ", " << run.set2_outer[3] << "]"
																   ", primitive mode: triangles, vertex spacing: "
								   << vs_mode_string << tcu::TestLog::EndMessage;

				TCU_FAIL("Amount of vertices generated by tessellator differs between base and references passes");
			}

			run.n_vertices = n_set1_vertices;

			run.set1_data = m_utils->getDataGeneratedByTessellator(run.set1_inner, false, /* is_point_mode_enabled */
																   TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES,
																   TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
																   run.vertex_spacing, run.set1_outer);
			run.set2_data = m_utils->getDataGeneratedByTessellator(run.set2_inner, false, /* is_point_mode_enabled */
																   TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES,
																   TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
																   run.vertex_spacing, run.set2_outer);

			/* Store the run data */
			m_runs.push_back(run);
		} /* for (all sets) */
	}	 /* for (all vertex spacing modes) */
}
/** Executes the test.
 *
 *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
 *
 *  Note the function throws exception should an error occur!
 *
 *  @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
 **/
tcu::TestNode::IterateResult TessellationShaderTrianglesInnerTessellationLevelRounding::iterate(void)
{
	/* Do not execute if required extensions are not supported. */
	if (!m_is_tessellation_shader_supported)
	{
		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
	}

	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	/* Initialize vertex array object */
	gl.genVertexArrays(1, &m_vao_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");

	gl.bindVertexArray(m_vao_id);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");

	/* Initialize and run test iterations. In later part, we will verify the generated data. */
	runTestIterations();

	/* Iterate through all runs */

	for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++)
	{
		const _run& run = *run_iterator;

		if (run.vertex_spacing == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD &&
			de::abs(run.set1_inner[0] - run.set2_inner[0]) > 1e-5f &&
			de::min(run.set1_inner[0], run.set2_inner[0]) >= 1.0f)
		{
			/* In fractional_odd_spacing mode with inner level <f> >= 1.0f, the clamped
			 and rounded integer level <n> is at least 3.

			 These results in inner subdivision into at least <n>-2=1 segment and
			 two additional, typically shorter segments.

			 The length of these two additional segments relative to the others will
			 decrease monotonically with the value of <n>-<f>, so if different <f> levels
			 were used, we cannot proceed with matching the exact vertex data. */

			continue;
		}

		/* Make sure the vertex data generated for two passes matches */
		const glw::GLint n_triangles = run.n_vertices / 3 /* vertices per triangle */;

		for (int n_triangle = 0; n_triangle < n_triangles; ++n_triangle)
		{
			const float* triangle_a = (const float*)(&run.set1_data[0]) +
									  n_triangle * 3 /* vertices */
										  * 3;		 /* components */
			const float* triangle_b = (const float*)(&run.set2_data[0]) +
									  n_triangle * 3 /* vertices */
										  * 3;		 /* components */

			if (!TessellationShaderUtils::isTriangleDefined(triangle_a, triangle_b))
			{
				std::string vs_mode_string =
					TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing);

				m_testCtx.getLog() << tcu::TestLog::Message
								   << "The following triangle, generated in the first pass, was not "
									  "generated in the second one. "
									  "First pass' configuration: inner tess levels:"
									  "["
								   << run.set1_inner[0] << ", " << run.set1_inner[1] << "]"
																						", outer tess levels:"
																						"["
								   << run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2]
								   << ", " << run.set1_outer[3] << "]"
																   "; second pass' configuration: inner tess levels:"
																   "["
								   << run.set2_inner[0] << ", " << run.set2_inner[1] << "]"
																						", outer tess levels:"
																						"["
								   << run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2]
								   << ", " << run.set2_outer[3] << "]"
																   ", primitive mode: triangles, vertex spacing: "
								   << vs_mode_string << tcu::TestLog::EndMessage;

				TCU_FAIL("A triangle from first pass' data set was not found in second pass' data set.");
			}
		} /* for (all vertices) */
	}	 /* for (all runs) */

	/* All done */
	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
	return STOP;
}