/** Verify rendered polygon anisotropy.
 *
 *  @param gl  OpenGL functions wrapper
 *
 *  @return Returns points value. Less points means better anisotropy (smoother strips).
 */
GLuint TextureFilterAnisotropicDrawingTestCase::verifyScene(const glw::Functions& gl)
{
	std::vector<GLubyte> pixels;
	pixels.resize(32 * 8 * 4);

	gl.readPixels(0, 23, 32, 8, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
	GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels");

	GLuint sum = 0;

	GLubyte last	= 0;
	GLubyte current = 0;
	for (int j = 0; j < 8; ++j)
	{
		for (int i = 0; i < 32; ++i)
		{
			current = pixels[(i + j * 32) * 4];

			if (i > 0)
				sum += deAbs32((int)current - (int)last);

			last = current;
		}
	}

	return sum;
}
static std::vector<std::string> getExtensions (const glw::Functions& gl, ApiType apiType)
{
	using std::vector;
	using std::string;

	if (apiType.getProfile() == PROFILE_ES && apiType.getMajorVersion() == 2)
	{
		TCU_CHECK(gl.getString);

		const char*	extStr	= (const char*)gl.getString(GL_EXTENSIONS);
		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetString(GL_EXTENSIONS)");

		if (extStr)
			return de::splitString(extStr);
		else
			throw tcu::TestError("glGetString(GL_EXTENSIONS) returned null pointer", DE_NULL, __FILE__, __LINE__);
	}
	else
	{
		int				numExtensions	= 0;
		vector<string>	extensions;

		TCU_CHECK(gl.getIntegerv && gl.getStringi);

		gl.getIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv(GL_NUM_EXTENSIONS)");

		if (numExtensions > 0)
		{
			extensions.resize(numExtensions);

			for (int ndx = 0; ndx < numExtensions; ndx++)
			{
				const char* const ext = (const char*)gl.getStringi(GL_EXTENSIONS, ndx);
				GLU_EXPECT_NO_ERROR(gl.getError(), "glGetStringi(GL_EXTENSIONS)");

				if (ext)
					extensions[ndx] = ext;
				else
					throw tcu::TestError("glGetStringi(GL_EXTENSIONS) returned null pointer", DE_NULL, __FILE__, __LINE__);
			}

		}

		return extensions;
	}
}
/** Verify if get* queries for GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT pname works as expected.
 *
 *  @param gl   OpenGL functions wrapper
 *
 *  @return Returns true if queries test passed, false otherwise.
 */
bool TextureFilterAnisotropicQueriesTestCase::verifyGet(const glw::Functions& gl)
{
	GLboolean bValue;
	GLint	 iValue;
	GLfloat   fValue;
	GLdouble  dValue;

	gl.getBooleanv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &bValue);
	GLU_EXPECT_NO_ERROR(gl.getError(), "getBooleanv");

	gl.getIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &iValue);
	GLU_EXPECT_NO_ERROR(gl.getError(), "getIntegerv");

	gl.getFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fValue);
	GLU_EXPECT_NO_ERROR(gl.getError(), "getFloatv");

	if (glu::isContextTypeGLCore(m_context.getRenderContext().getType()))
	{
		gl.getDoublev(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &dValue);
		GLU_EXPECT_NO_ERROR(gl.getError(), "getDoublev");
	}

	return true;
}
static std::vector<int> querySampleCounts (const glw::Functions& gl, deUint32 format)
{
	int					numSampleCounts		= 0;
	std::vector<int>	sampleCounts;

	gl.getInternalformativ(GL_RENDERBUFFER, format, GL_NUM_SAMPLE_COUNTS, 1, &numSampleCounts);

	if (numSampleCounts > 0)
	{
		sampleCounts.resize(numSampleCounts);
		gl.getInternalformativ(GL_RENDERBUFFER, format, GL_SAMPLES, (glw::GLsizei)sampleCounts.size(), &sampleCounts[0]);
	}

	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to query sample counts for format");

	return sampleCounts;
}
/** Generate texture and set filtering parameters.
 *
 *  @param gl              OpenGL functions wrapper
 *  @param target          Texture target
 *  @param internalFormat  Texture internal format
 */
void TextureFilterAnisotropicDrawingTestCase::generateTexture(const glw::Functions& gl, GLenum target)
{
	gl.genTextures(1, &m_texture);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures");
	gl.bindTexture(target, m_texture);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture");

	gl.texParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glTexParameteri");
	gl.texParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glTexParameteri");
	gl.texParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glTexParameteri");
	gl.texParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glTexParameteri");
	gl.texParameteri(target, GL_TEXTURE_MAX_LEVEL, 1);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glTexParameteri");
}
/** Render polygon with anisotropic filtering.
 *
 *  @param gl           OpenGL functions wrapper
 *  @param target       Texture target
 *  @param anisoDegree  Degree of anisotropy
 *
 *  @return Returns true if no error occured, false otherwise.
 */
bool TextureFilterAnisotropicDrawingTestCase::drawTexture(const glw::Functions& gl, GLenum target, GLfloat anisoDegree)
{
	const GLfloat vertices2[] = { -1.0f, 0.0f, -0.5f, 0.0f, 0.0f, -4.0f, 4.0f, -2.0f, 0.0f, 1.0f,
								  1.0f,  0.0f, -0.5f, 1.0f, 0.0f, -2.0f, 4.0f, -2.0f, 1.0f, 1.0f };
	const GLfloat vertices3[] = { -1.0f, 0.0f, -0.5f, 0.0f, 0.0f, 0.0f, -4.0f, 4.0f, -2.0f, 0.0f, 1.0f, 0.0f,
								  1.0f,  0.0f, -0.5f, 1.0f, 0.0f, 0.0f, -2.0f, 4.0f, -2.0f, 1.0f, 1.0f, 0.0f };

	// Projection values.
	const GLfloat projectionMatrix[] = { 0.5f, 0.0f, 0.0f,		   0.0f,		 0.0f, 0.5f, 0.0f,  0.0f,
										 0.0f, 0.0f, -2.5f / 1.5f, -2.0f / 1.5f, 0.0f, 0.0f, -1.0f, 0.0f };

	gl.viewport(0, 0, 32, 32);

	std::string vertexShader   = m_vertex;
	std::string fragmentShader = m_fragment;

	std::string texCoordType;
	std::string samplerType;
	TextureFilterAnisotropicUtils::generateTokens(target, texCoordType, samplerType);

	TextureFilterAnisotropicUtils::replaceToken("<TEXCOORD_TYPE>", texCoordType.c_str(), vertexShader);
	TextureFilterAnisotropicUtils::replaceToken("<TEXCOORD_TYPE>", texCoordType.c_str(), fragmentShader);
	TextureFilterAnisotropicUtils::replaceToken("<SAMPLER_TYPE>", samplerType.c_str(), vertexShader);
	TextureFilterAnisotropicUtils::replaceToken("<SAMPLER_TYPE>", samplerType.c_str(), fragmentShader);

	if (glu::isContextTypeGLCore(m_context.getRenderContext().getType()))
	{
		TextureFilterAnisotropicUtils::replaceToken("<VERSION>", "130", vertexShader);
		TextureFilterAnisotropicUtils::replaceToken("<VERSION>", "130", fragmentShader);
	}
	else
	{
		TextureFilterAnisotropicUtils::replaceToken("<VERSION>", "300 es", vertexShader);
		TextureFilterAnisotropicUtils::replaceToken("<VERSION>", "300 es", fragmentShader);
	}

	ProgramSources sources = makeVtxFragSources(vertexShader, fragmentShader);
	ShaderProgram  program(gl, sources);

	if (!program.isOk())
	{
		m_testCtx.getLog() << tcu::TestLog::Message << "Shader build failed.\n"
						   << "Vertex: " << program.getShaderInfo(SHADERTYPE_VERTEX).infoLog << "\n"
						   << vertexShader << "\n"
						   << "Fragment: " << program.getShaderInfo(SHADERTYPE_FRAGMENT).infoLog << "\n"
						   << fragmentShader << "\n"
						   << "Program: " << program.getProgramInfo().infoLog << tcu::TestLog::EndMessage;
		return false;
	}

	GLuint vao;
	gl.genVertexArrays(1, &vao);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays");
	gl.bindVertexArray(vao);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray");

	GLuint vbo;
	gl.genBuffers(1, &vbo);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers");
	gl.bindBuffer(GL_ARRAY_BUFFER, vbo);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer");

	std::vector<GLfloat> vboData;
	vboData.resize(24);

	GLuint texCoordDim;
	if (texCoordType == "vec2")
	{
		texCoordDim = 2;
		deMemcpy((void*)vboData.data(), (void*)vertices2, sizeof(vertices2));
	}
	else
	{
		texCoordDim = 3;
		deMemcpy((void*)vboData.data(), (void*)vertices3, sizeof(vertices3));
	}

	gl.bufferData(GL_ARRAY_BUFFER, vboData.size() * sizeof(GLfloat), (GLvoid*)vboData.data(), GL_DYNAMIC_DRAW);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData");

	gl.useProgram(program.getProgram());
	GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram");

	GLuint matrixLocation = gl.getUniformLocation(program.getProgram(), "projectionMatrix");
	GLuint texLocation	= gl.getUniformLocation(program.getProgram(), "tex");

	gl.activeTexture(GL_TEXTURE0);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glActiveTexture");
	gl.bindTexture(target, m_texture);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture");
	gl.uniformMatrix4fv(matrixLocation, 1, GL_FALSE, projectionMatrix);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glUniformMatrix4fv");
	gl.uniform1i(texLocation, 0);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform1i");

	gl.texParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoDegree);
	GLU_EXPECT_NO_ERROR(gl.getError(), "texParameterfv");

	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor");
	gl.clear(GL_COLOR_BUFFER_BIT);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glClear");

	gl.enableVertexAttribArray(0);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray");
	gl.enableVertexAttribArray(1);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray");

	GLint attrLocationVertex = gl.getAttribLocation(program.getProgram(), "vertex");
	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetAttribLocation");
	GLint attrLocationInTexCoord = gl.getAttribLocation(program.getProgram(), "inTexCoord");
	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetAttribLocation");

	GLuint strideSize = (3 + texCoordDim) * sizeof(GLfloat);
	gl.vertexAttribPointer(attrLocationVertex, 3, GL_FLOAT, GL_FALSE, strideSize, DE_NULL);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer");
	gl.vertexAttribPointer(attrLocationInTexCoord, texCoordDim, GL_FLOAT, GL_FALSE, strideSize,
						   (GLvoid*)(3 * sizeof(GLfloat)));
	GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer");

	gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArray");

	gl.disableVertexAttribArray(0);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glDisableVertexAttribArray");
	gl.disableVertexAttribArray(1);
	GLU_EXPECT_NO_ERROR(gl.getError(), "glDisableVertexAttribArray");

	if (vbo)
	{
		gl.deleteBuffers(1, &vbo);
		GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteBuffers");
	}

	if (vao)
	{
		gl.deleteVertexArrays(1, &vao);
		GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteVertexArrays");
	}

	return true;
}
/** Verify if texParameter*, getTexParameter* queries for GL_TEXTURE_MAX_ANISOTROPY_EXT pname works as expected.
 *
 *  @param gl   OpenGL functions wrapper
 *
 *  @return Returns true if queries test passed, false otherwise.
 */
bool TextureFilterAnisotropicQueriesTestCase::verifyTexParameters(const glw::Functions& gl)
{
	GLint iValue;
	gl.getTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &iValue);
	GLU_EXPECT_NO_ERROR(gl.getError(), "getTexParameteriv");

	// Verify initial integer value which should be equal to 1
	if (iValue != 1)
	{
		m_testCtx.getLog() << tcu::TestLog::Message
						   << "GetTexParameteriv failed. Expected value: 1, Queried value: " << iValue
						   << tcu::TestLog::EndMessage;
		return false;
	}

	GLfloat fValue;
	gl.getTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &fValue);
	GLU_EXPECT_NO_ERROR(gl.getError(), "getTexParameterfv");

	// Verify initial float value which should be equal to 1.0f
	if (fValue != 1.0f)
	{
		m_testCtx.getLog() << tcu::TestLog::Message
						   << "GetTexParameterfv failed. Expected value: 1.0, Queried value: " << iValue
						   << tcu::TestLog::EndMessage;
		return false;
	}

	// Set custom integer value and verify it
	iValue = 2;
	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, iValue);
	GLU_EXPECT_NO_ERROR(gl.getError(), "texParameteri");

	gl.getTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &iValue);
	GLU_EXPECT_NO_ERROR(gl.getError(), "getTexParameteriv");

	if (iValue != 2)
	{
		m_testCtx.getLog() << tcu::TestLog::Message
						   << "texParameteri failed. Expected value: 2, Queried value: " << iValue
						   << tcu::TestLog::EndMessage;
		return false;
	}

	// Set custom float value and verify it
	fValue = 1.5f;
	gl.texParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fValue);
	GLU_EXPECT_NO_ERROR(gl.getError(), "texParameterf");

	gl.getTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &fValue);
	GLU_EXPECT_NO_ERROR(gl.getError(), "getTexParameterfv");

	if (fValue != 1.5f)
	{
		m_testCtx.getLog() << tcu::TestLog::Message
						   << "texParameterf failed. Expected value: 1.5, Queried value: " << fValue
						   << tcu::TestLog::EndMessage;
		return false;
	}

	// Set custom integer value and verify it
	iValue = 1;
	gl.texParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &iValue);
	GLU_EXPECT_NO_ERROR(gl.getError(), "texParameteriv");

	gl.getTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &iValue);
	GLU_EXPECT_NO_ERROR(gl.getError(), "getTexParameteriv");

	if (iValue != 1)
	{
		m_testCtx.getLog() << tcu::TestLog::Message
						   << "texParameteriv failed. Expected value: 1, Queried value: " << iValue
						   << tcu::TestLog::EndMessage;
		return false;
	}

	// Set custom float value and verify it
	fValue = 2.0f;
	gl.texParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &fValue);
	GLU_EXPECT_NO_ERROR(gl.getError(), "texParameterfv");

	gl.getTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &fValue);
	GLU_EXPECT_NO_ERROR(gl.getError(), "getTexParameterfv");

	if (fValue != 2.0f)
	{
		m_testCtx.getLog() << tcu::TestLog::Message
						   << "texParameterfv failed. Expected value: 2.0, Queried value: " << fValue
						   << tcu::TestLog::EndMessage;
		return false;
	}

	// Set texture filter anisotropic to 0.9f and check if INVALID_VALUE error is generated
	fValue = 0.9f;
	gl.texParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &fValue);
	GLint error = gl.getError();
	if (error != GL_INVALID_VALUE)
	{
		m_testCtx.getLog()
			<< tcu::TestLog::Message
			<< "texParameterfv failed for values less then 1.0f. Expected INVALID_VALUE error. Generated error: "
			<< glu::getErrorName(error) << tcu::TestLog::EndMessage;
		return false;
	}

	return true;
}