C++程序  |  813行  |  25.04 KB

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

#include "es31fVertexAttributeBindingStateQueryTests.hpp"
#include "tcuTestLog.hpp"
#include "gluCallLogWrapper.hpp"
#include "gluRenderContext.hpp"
#include "gluObjectWrapper.hpp"
#include "gluStrUtil.hpp"
#include "glsStateQueryUtil.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "deRandom.hpp"

namespace deqp
{
namespace gles31
{
namespace Functional
{
namespace
{

class AttributeBindingCase : public TestCase
{
public:
					AttributeBindingCase	(Context& context, const char* name, const char* desc);
	IterateResult	iterate					(void);
};

AttributeBindingCase::AttributeBindingCase (Context& context, const char* name, const char* desc)
	: TestCase(context, name, desc)
{
}

AttributeBindingCase::IterateResult AttributeBindingCase::iterate (void)
{
	glu::CallLogWrapper gl			(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
	glu::VertexArray	vao			(m_context.getRenderContext());
	glw::GLenum			error		= 0;
	glw::GLint			maxAttrs	= -1;
	bool				allOk		= true;

	gl.enableLogging(true);

	gl.glBindVertexArray(*vao);
	gl.glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttrs);

	// initial
	{
		const tcu::ScopedLogSection section(m_testCtx.getLog(), "initial", "Initial values");

		for (int attr = 0; attr < de::max(16, maxAttrs); ++attr)
		{
			glw::GLint bindingState = -1;

			gl.glGetVertexAttribiv(attr, GL_VERTEX_ATTRIB_BINDING, &bindingState);
			error = gl.glGetError();

			if (error != GL_NO_ERROR)
			{
				m_testCtx.getLog() << tcu::TestLog::Message << "// ERROR: Got error " << glu::getErrorStr(error) << tcu::TestLog::EndMessage;
				allOk = false;
			}
			else if (bindingState != attr)
			{
				m_testCtx.getLog() << tcu::TestLog::Message << "// ERROR: Expected " << attr << ", got " << bindingState << tcu::TestLog::EndMessage;
				allOk = false;
			}
		}
	}

	// is part of vao
	{
		const tcu::ScopedLogSection section			(m_testCtx.getLog(), "vao", "VAO state");
		glu::VertexArray			otherVao		(m_context.getRenderContext());
		glw::GLint					bindingState	= -1;

		// set to value A in vao1
		gl.glVertexAttribBinding(1, 4);

		// set to value B in vao2
		gl.glBindVertexArray(*otherVao);
		gl.glVertexAttribBinding(1, 7);

		// check value is still ok in original vao
		gl.glBindVertexArray(*vao);
		gl.glGetVertexAttribiv(1, GL_VERTEX_ATTRIB_BINDING, &bindingState);
		error = gl.glGetError();

		if (error != GL_NO_ERROR)
		{
			m_testCtx.getLog() << tcu::TestLog::Message << "// ERROR: Got error " << glu::getErrorStr(error) << tcu::TestLog::EndMessage;
			allOk = false;
		}
		else if (bindingState != 4)
		{
			m_testCtx.getLog() << tcu::TestLog::Message << "// ERROR: Expected 4, got " << bindingState << tcu::TestLog::EndMessage;
			allOk = false;
		}
	}

	// random values
	{
		const tcu::ScopedLogSection	section			(m_testCtx.getLog(), "random", "Random values");
		de::Random					rnd				(0xabc);
		const int					numRandomTests	= 10;

		for (int randomTestNdx = 0; randomTestNdx < numRandomTests; ++randomTestNdx)
		{
			// switch random va to random binding
			const int	va				= rnd.getInt(0, de::max(16, maxAttrs)-1);
			const int	binding			= rnd.getInt(0, 16);
			glw::GLint	bindingState	= -1;

			gl.glVertexAttribBinding(va, binding);
			gl.glGetVertexAttribiv(va, GL_VERTEX_ATTRIB_BINDING, &bindingState);
			error = gl.glGetError();

			if (error != GL_NO_ERROR)
			{
				m_testCtx.getLog() << tcu::TestLog::Message << "// ERROR: Got error " << glu::getErrorStr(error) << tcu::TestLog::EndMessage;
				allOk = false;
			}
			else if (bindingState != binding)
			{
				m_testCtx.getLog() << tcu::TestLog::Message << "// ERROR: Expected " << binding << ", got " << bindingState << tcu::TestLog::EndMessage;
				allOk = false;
			}
		}
	}

	if (allOk)
		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
	else
		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid value");
	return STOP;
}

class AttributeRelativeOffsetCase : public TestCase
{
public:
					AttributeRelativeOffsetCase	(Context& context, const char* name, const char* desc);
	IterateResult	iterate						(void);
};

AttributeRelativeOffsetCase::AttributeRelativeOffsetCase (Context& context, const char* name, const char* desc)
	: TestCase(context, name, desc)
{
}

AttributeRelativeOffsetCase::IterateResult AttributeRelativeOffsetCase::iterate (void)
{
	glu::CallLogWrapper gl			(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
	glu::VertexArray	vao			(m_context.getRenderContext());
	glw::GLenum			error		= 0;
	glw::GLint			maxAttrs	= -1;
	bool				allOk		= true;

	gl.enableLogging(true);

	gl.glBindVertexArray(*vao);
	gl.glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttrs);

	// initial
	{
		const tcu::ScopedLogSection section(m_testCtx.getLog(), "initial", "Initial values");

		for (int attr = 0; attr < de::max(16, maxAttrs); ++attr)
		{
			glw::GLint relOffsetState = -1;

			gl.glGetVertexAttribiv(attr, GL_VERTEX_ATTRIB_RELATIVE_OFFSET, &relOffsetState);
			error = gl.glGetError();

			if (error != GL_NO_ERROR)
			{
				m_testCtx.getLog() << tcu::TestLog::Message << "// ERROR: Got error " << glu::getErrorStr(error) << tcu::TestLog::EndMessage;
				allOk = false;
			}
			else if (relOffsetState != 0)
			{
				m_testCtx.getLog() << tcu::TestLog::Message << "// ERROR: Expected 0, got " << relOffsetState << tcu::TestLog::EndMessage;
				allOk = false;
			}
		}
	}

	// is part of vao
	{
		const tcu::ScopedLogSection section			(m_testCtx.getLog(), "vao", "VAO state");
		glu::VertexArray			otherVao		(m_context.getRenderContext());
		glw::GLint					relOffsetState	= -1;

		// set to value A in vao1
		gl.glVertexAttribFormat(1, 4, GL_FLOAT, GL_FALSE, 9);

		// set to value B in vao2
		gl.glBindVertexArray(*otherVao);
		gl.glVertexAttribFormat(1, 4, GL_FLOAT, GL_FALSE, 21);

		// check value is still ok in original vao
		gl.glBindVertexArray(*vao);
		gl.glGetVertexAttribiv(1, GL_VERTEX_ATTRIB_RELATIVE_OFFSET, &relOffsetState);
		error = gl.glGetError();

		if (error != GL_NO_ERROR)
		{
			m_testCtx.getLog() << tcu::TestLog::Message << "// ERROR: Got error " << glu::getErrorStr(error) << tcu::TestLog::EndMessage;
			allOk = false;
		}
		else if (relOffsetState != 9)
		{
			m_testCtx.getLog() << tcu::TestLog::Message << "// ERROR: Expected 9, got " << relOffsetState << tcu::TestLog::EndMessage;
			allOk = false;
		}
	}

	// random values
	{
		const tcu::ScopedLogSection	section			(m_testCtx.getLog(), "random", "Random values");
		de::Random					rnd				(0xabc);
		const int					numRandomTests	= 10;

		for (int randomTestNdx = 0; randomTestNdx < numRandomTests; ++randomTestNdx)
		{
			const int	va				= rnd.getInt(0, de::max(16, maxAttrs)-1);
			const int	offset			= rnd.getInt(0, 2047);
			glw::GLint	relOffsetState	= -1;

			gl.glVertexAttribFormat(va, 4, GL_FLOAT, GL_FALSE, offset);
			gl.glGetVertexAttribiv(va, GL_VERTEX_ATTRIB_RELATIVE_OFFSET, &relOffsetState);
			error = gl.glGetError();

			if (error != GL_NO_ERROR)
			{
				m_testCtx.getLog() << tcu::TestLog::Message << "// ERROR: Got error " << glu::getErrorStr(error) << tcu::TestLog::EndMessage;
				allOk = false;
			}
			else if (relOffsetState != offset)
			{
				m_testCtx.getLog() << tcu::TestLog::Message << "// ERROR: Expected " << offset << ", got " << relOffsetState << tcu::TestLog::EndMessage;
				allOk = false;
			}
		}
	}

	if (allOk)
		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
	else
		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid value");
	return STOP;
}

class IndexedCase : public TestCase
{
public:
	enum VerifierType
	{
		VERIFIER_INT,
		VERIFIER_INT64,

		VERIFIER_LAST
	};

						IndexedCase			(Context& context, const char* name, const char* desc, VerifierType verifier);

	IterateResult		iterate				(void);
	void				verifyValue			(glu::CallLogWrapper& gl, glw::GLenum name, int index, int expected);

	virtual void		test				(void) = 0;
private:
	const VerifierType	m_verifier;
};

IndexedCase::IndexedCase (Context& context, const char* name, const char* desc, VerifierType verifier)
	: TestCase		(context, name, desc)
	, m_verifier	(verifier)
{
	DE_ASSERT(verifier < VERIFIER_LAST);
}

IndexedCase::IterateResult IndexedCase::iterate (void)
{
	// default value
	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");

	test();

	return STOP;
}

void IndexedCase::verifyValue (glu::CallLogWrapper& gl, glw::GLenum name, int index, int expected)
{
	if (m_verifier == VERIFIER_INT)
	{
		gls::StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint>	value;
		glw::GLenum													error = 0;

		gl.glGetIntegeri_v(name, index, &value);
		error = gl.glGetError();

		if (error != GL_NO_ERROR)
		{
			m_testCtx.getLog() << tcu::TestLog::Message << "// ERROR: Got unexpected error: " << glu::getErrorStr(error) << tcu::TestLog::EndMessage;
			if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected error");
		}
		else if (!value.verifyValidity(m_testCtx))
		{
			// verifyValidity sets error
		}
		else
		{
			if (value != expected)
			{
				m_testCtx.getLog() << tcu::TestLog::Message << "// ERROR: Expected " << expected << ", got " << value << tcu::TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected value");
			}
		}
	}
	else if (m_verifier == VERIFIER_INT64)
	{
		gls::StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint64>	value;
		glw::GLenum														error = 0;

		gl.glGetInteger64i_v(name, index, &value);
		error = gl.glGetError();

		if (error != GL_NO_ERROR)
		{
			m_testCtx.getLog() << tcu::TestLog::Message << "// ERROR: Got unexpected error: " << glu::getErrorStr(error) << tcu::TestLog::EndMessage;
			if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected error");
		}
		else if (!value.verifyValidity(m_testCtx))
		{
			// verifyValidity sets error
		}
		else
		{
			if (value != expected)
			{
				m_testCtx.getLog() << tcu::TestLog::Message << "// ERROR: Expected " << expected << ", got " << value << tcu::TestLog::EndMessage;
				if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected value");
			}
		}
	}
	else
		DE_ASSERT(false);
}

class VertexBindingDivisorCase : public IndexedCase
{
public:
			VertexBindingDivisorCase	(Context& context, const char* name, const char* desc, VerifierType verifier);
	void	test						(void);
};

VertexBindingDivisorCase::VertexBindingDivisorCase (Context& context, const char* name, const char* desc, VerifierType verifier)
	: IndexedCase(context, name, desc, verifier)
{
}

void VertexBindingDivisorCase::test (void)
{
	glu::CallLogWrapper gl					(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
	glu::VertexArray	vao					(m_context.getRenderContext());
	glw::GLint			reportedMaxBindings	= -1;
	glw::GLint			maxBindings;

	gl.enableLogging(true);

	gl.glBindVertexArray(*vao);
	gl.glGetIntegerv(GL_MAX_VERTEX_ATTRIB_BINDINGS, &reportedMaxBindings);
	maxBindings = de::max(16, reportedMaxBindings);

	// initial
	{
		const tcu::ScopedLogSection section(m_testCtx.getLog(), "initial", "Initial values");

		for (int binding = 0; binding < maxBindings; ++binding)
			verifyValue(gl, GL_VERTEX_BINDING_DIVISOR, binding, 0);
	}

	// is part of vao
	{
		const tcu::ScopedLogSection section			(m_testCtx.getLog(), "vao", "VAO state");
		glu::VertexArray			otherVao		(m_context.getRenderContext());

		// set to value A in vao1
		gl.glVertexBindingDivisor(1, 4);

		// set to value B in vao2
		gl.glBindVertexArray(*otherVao);
		gl.glVertexBindingDivisor(1, 9);

		// check value is still ok in original vao
		gl.glBindVertexArray(*vao);
		verifyValue(gl, GL_VERTEX_BINDING_DIVISOR, 1, 4);
	}

	// random values
	{
		const tcu::ScopedLogSection	section			(m_testCtx.getLog(), "random", "Random values");
		de::Random					rnd				(0xabc);
		const int					numRandomTests	= 10;

		for (int randomTestNdx = 0; randomTestNdx < numRandomTests; ++randomTestNdx)
		{
			const int	binding			= rnd.getInt(0, maxBindings-1);
			const int	divisor			= rnd.getInt(0, 2047);

			gl.glVertexBindingDivisor(binding, divisor);
			verifyValue(gl, GL_VERTEX_BINDING_DIVISOR, binding, divisor);
		}
	}
}

class VertexBindingOffsetCase : public IndexedCase
{
public:
			VertexBindingOffsetCase		(Context& context, const char* name, const char* desc, VerifierType verifier);
	void	test						(void);
};

VertexBindingOffsetCase::VertexBindingOffsetCase (Context& context, const char* name, const char* desc, VerifierType verifier)
	: IndexedCase(context, name, desc, verifier)
{
}

void VertexBindingOffsetCase::test (void)
{
	glu::CallLogWrapper gl					(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
	glu::VertexArray	vao					(m_context.getRenderContext());
	glu::Buffer			buffer				(m_context.getRenderContext());
	glw::GLint			reportedMaxBindings	= -1;
	glw::GLint			maxBindings;

	gl.enableLogging(true);

	gl.glBindVertexArray(*vao);
	gl.glGetIntegerv(GL_MAX_VERTEX_ATTRIB_BINDINGS, &reportedMaxBindings);
	maxBindings = de::max(16, reportedMaxBindings);

	// initial
	{
		const tcu::ScopedLogSection section(m_testCtx.getLog(), "initial", "Initial values");

		for (int binding = 0; binding < maxBindings; ++binding)
			verifyValue(gl, GL_VERTEX_BINDING_OFFSET, binding, 0);
	}

	// is part of vao
	{
		const tcu::ScopedLogSection section			(m_testCtx.getLog(), "vao", "VAO state");
		glu::VertexArray			otherVao		(m_context.getRenderContext());

		// set to value A in vao1
		gl.glBindVertexBuffer(1, *buffer, 4, 32);

		// set to value B in vao2
		gl.glBindVertexArray(*otherVao);
		gl.glBindVertexBuffer(1, *buffer, 13, 32);

		// check value is still ok in original vao
		gl.glBindVertexArray(*vao);
		verifyValue(gl, GL_VERTEX_BINDING_OFFSET, 1, 4);
	}

	// random values
	{
		const tcu::ScopedLogSection	section			(m_testCtx.getLog(), "random", "Random values");
		de::Random					rnd				(0xabc);
		const int					numRandomTests	= 10;

		for (int randomTestNdx = 0; randomTestNdx < numRandomTests; ++randomTestNdx)
		{
			const int	binding			= rnd.getInt(0, maxBindings-1);
			const int	offset			= rnd.getInt(0, 4000);

			gl.glBindVertexBuffer(binding, *buffer, offset, 32);
			verifyValue(gl, GL_VERTEX_BINDING_OFFSET, binding, offset);
		}
	}
}

class VertexBindingStrideCase : public IndexedCase
{
public:
			VertexBindingStrideCase		(Context& context, const char* name, const char* desc, VerifierType verifier);
	void	test						(void);
};

VertexBindingStrideCase::VertexBindingStrideCase (Context& context, const char* name, const char* desc, VerifierType verifier)
	: IndexedCase(context, name, desc, verifier)
{
}

void VertexBindingStrideCase::test (void)
{
	glu::CallLogWrapper gl					(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
	glu::VertexArray	vao					(m_context.getRenderContext());
	glu::Buffer			buffer				(m_context.getRenderContext());
	glw::GLint			reportedMaxBindings	= -1;
	glw::GLint			maxBindings;

	gl.enableLogging(true);

	gl.glBindVertexArray(*vao);
	gl.glGetIntegerv(GL_MAX_VERTEX_ATTRIB_BINDINGS, &reportedMaxBindings);
	maxBindings = de::max(16, reportedMaxBindings);

	// initial
	{
		const tcu::ScopedLogSection section(m_testCtx.getLog(), "initial", "Initial values");

		for (int binding = 0; binding < maxBindings; ++binding)
			verifyValue(gl, GL_VERTEX_BINDING_STRIDE, binding, 16);
	}

	// is part of vao
	{
		const tcu::ScopedLogSection section			(m_testCtx.getLog(), "vao", "VAO state");
		glu::VertexArray			otherVao		(m_context.getRenderContext());

		// set to value A in vao1
		gl.glBindVertexBuffer(1, *buffer, 0, 32);

		// set to value B in vao2
		gl.glBindVertexArray(*otherVao);
		gl.glBindVertexBuffer(1, *buffer, 0, 64);

		// check value is still ok in original vao
		gl.glBindVertexArray(*vao);
		verifyValue(gl, GL_VERTEX_BINDING_STRIDE, 1, 32);
	}

	// random values
	{
		const tcu::ScopedLogSection	section			(m_testCtx.getLog(), "random", "Random values");
		de::Random					rnd				(0xabc);
		const int					numRandomTests	= 10;

		for (int randomTestNdx = 0; randomTestNdx < numRandomTests; ++randomTestNdx)
		{
			const int	binding			= rnd.getInt(0, maxBindings-1);
			const int	stride			= rnd.getInt(0, 2048);

			gl.glBindVertexBuffer(binding, *buffer, 0, stride);
			verifyValue(gl, GL_VERTEX_BINDING_STRIDE, binding, stride);
		}
	}
}

class VertexBindingBufferCase : public IndexedCase
{
public:
			VertexBindingBufferCase		(Context& context, const char* name, const char* desc, VerifierType verifier);
	void	test						(void);
};

VertexBindingBufferCase::VertexBindingBufferCase (Context& context, const char* name, const char* desc, VerifierType verifier)
	: IndexedCase(context, name, desc, verifier)
{
}

void VertexBindingBufferCase::test (void)
{
	glu::CallLogWrapper gl					(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
	glu::VertexArray	vao					(m_context.getRenderContext());
	glu::Buffer			buffer				(m_context.getRenderContext());
	glw::GLint			reportedMaxBindings	= -1;
	glw::GLint			maxBindings;

	gl.enableLogging(true);

	gl.glBindVertexArray(*vao);
	gl.glGetIntegerv(GL_MAX_VERTEX_ATTRIB_BINDINGS, &reportedMaxBindings);
	maxBindings = de::max(16, reportedMaxBindings);

	// initial
	{
		const tcu::ScopedLogSection section(m_testCtx.getLog(), "initial", "Initial values");

		for (int binding = 0; binding < maxBindings; ++binding)
			verifyValue(gl, GL_VERTEX_BINDING_BUFFER, binding, 0);
	}

	// is part of vao
	{
		const tcu::ScopedLogSection section			(m_testCtx.getLog(), "vao", "VAO state");
		glu::VertexArray			otherVao		(m_context.getRenderContext());
		glu::Buffer					otherBuffer		(m_context.getRenderContext());

		// set to value A in vao1
		gl.glBindVertexBuffer(1, *buffer, 0, 32);

		// set to value B in vao2
		gl.glBindVertexArray(*otherVao);
		gl.glBindVertexBuffer(1, *otherBuffer, 0, 32);

		// check value is still ok in original vao
		gl.glBindVertexArray(*vao);
		verifyValue(gl, GL_VERTEX_BINDING_BUFFER, 1, *buffer);
	}

	// Is detached in delete from active vao and not from deactive
	{
		const tcu::ScopedLogSection section			(m_testCtx.getLog(), "autoUnbind", "Unbind on delete");
		glu::VertexArray			otherVao		(m_context.getRenderContext());
		glw::GLuint					otherBuffer		= -1;

		gl.glGenBuffers(1, &otherBuffer);

		// set in vao1 and vao2
		gl.glBindVertexBuffer(1, otherBuffer, 0, 32);
		gl.glBindVertexArray(*otherVao);
		gl.glBindVertexBuffer(1, otherBuffer, 0, 32);

		// delete buffer. This unbinds it from active (vao2) but not from unactive
		gl.glDeleteBuffers(1, &otherBuffer);
		verifyValue(gl, GL_VERTEX_BINDING_BUFFER, 1, 0);
		gl.glBindVertexArray(*vao);
		verifyValue(gl, GL_VERTEX_BINDING_BUFFER, 1, otherBuffer);
	}
}

class MixedVertexBindingDivisorCase : public IndexedCase
{
public:
			MixedVertexBindingDivisorCase	(Context& context, const char* name, const char* desc);
	void	test							(void);
};

MixedVertexBindingDivisorCase::MixedVertexBindingDivisorCase (Context& context, const char* name, const char* desc)
	: IndexedCase(context, name, desc, VERIFIER_INT)
{
}

void MixedVertexBindingDivisorCase::test (void)
{
	glu::CallLogWrapper gl					(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
	glu::VertexArray	vao					(m_context.getRenderContext());

	gl.enableLogging(true);

	gl.glVertexAttribDivisor(1, 4);
	verifyValue(gl, GL_VERTEX_BINDING_DIVISOR, 1, 4);
}

class MixedVertexBindingOffsetCase : public IndexedCase
{
public:
			MixedVertexBindingOffsetCase	(Context& context, const char* name, const char* desc);
	void	test							(void);
};

MixedVertexBindingOffsetCase::MixedVertexBindingOffsetCase (Context& context, const char* name, const char* desc)
	: IndexedCase(context, name, desc, VERIFIER_INT)
{
}

void MixedVertexBindingOffsetCase::test (void)
{
	glu::CallLogWrapper gl					(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
	glu::VertexArray	vao					(m_context.getRenderContext());
	glu::Buffer			buffer				(m_context.getRenderContext());

	gl.enableLogging(true);

	gl.glBindBuffer(GL_ARRAY_BUFFER, *buffer);
	gl.glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (const deUint8*)DE_NULL + 12);

	verifyValue(gl, GL_VERTEX_BINDING_OFFSET, 1, 12);
}

class MixedVertexBindingStrideCase : public IndexedCase
{
public:
			MixedVertexBindingStrideCase	(Context& context, const char* name, const char* desc);
	void	test							(void);
};

MixedVertexBindingStrideCase::MixedVertexBindingStrideCase (Context& context, const char* name, const char* desc)
	: IndexedCase(context, name, desc, VERIFIER_INT)
{
}

void MixedVertexBindingStrideCase::test (void)
{
	glu::CallLogWrapper gl					(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
	glu::VertexArray	vao					(m_context.getRenderContext());
	glu::Buffer			buffer				(m_context.getRenderContext());

	gl.enableLogging(true);

	gl.glBindBuffer(GL_ARRAY_BUFFER, *buffer);
	gl.glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 12, 0);
	verifyValue(gl, GL_VERTEX_BINDING_STRIDE, 1, 12);

	// test effectiveStride
	gl.glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0);
	verifyValue(gl, GL_VERTEX_BINDING_STRIDE, 1, 16);
}

class MixedVertexBindingBufferCase : public IndexedCase
{
public:
			MixedVertexBindingBufferCase	(Context& context, const char* name, const char* desc);
	void	test							(void);
};

MixedVertexBindingBufferCase::MixedVertexBindingBufferCase (Context& context, const char* name, const char* desc)
	: IndexedCase(context, name, desc, VERIFIER_INT)
{
}

void MixedVertexBindingBufferCase::test (void)
{
	glu::CallLogWrapper gl					(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
	glu::VertexArray	vao					(m_context.getRenderContext());
	glu::Buffer			buffer				(m_context.getRenderContext());

	gl.enableLogging(true);

	gl.glBindBuffer(GL_ARRAY_BUFFER, *buffer);
	gl.glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0);
	verifyValue(gl, GL_VERTEX_BINDING_BUFFER, 1, *buffer);
}

} // anonymous

VertexAttributeBindingStateQueryTests::VertexAttributeBindingStateQueryTests (Context& context)
	: TestCaseGroup(context, "vertex_attribute_binding", "Query vertex attribute binding state.")
{
}

VertexAttributeBindingStateQueryTests::~VertexAttributeBindingStateQueryTests (void)
{
}

void VertexAttributeBindingStateQueryTests::init (void)
{
	tcu::TestCaseGroup* const attributeGroup	= new TestCaseGroup(m_context, "vertex_attrib", "Vertex attribute state");
	tcu::TestCaseGroup* const indexedGroup		= new TestCaseGroup(m_context, "indexed", "Indexed state");

	addChild(attributeGroup);
	addChild(indexedGroup);

	// .vertex_attrib
	{
		attributeGroup->addChild(new AttributeBindingCase		(m_context,	"vertex_attrib_binding",			"Test VERTEX_ATTRIB_BINDING"));
		attributeGroup->addChild(new AttributeRelativeOffsetCase(m_context,	"vertex_attrib_relative_offset",	"Test VERTEX_ATTRIB_RELATIVE_OFFSET"));
	}

	// .indexed (and 64)
	{
		static const struct Verifier
		{
			const char*					name;
			IndexedCase::VerifierType	type;
		} verifiers[] =
		{
			{ "getintegeri",	IndexedCase::VERIFIER_INT	},
			{ "getintegeri64",	IndexedCase::VERIFIER_INT64	},
		};

		// states

		for (int verifierNdx = 0; verifierNdx < DE_LENGTH_OF_ARRAY(verifiers); ++verifierNdx)
		{
			indexedGroup->addChild(new VertexBindingDivisorCase	(m_context, (std::string("vertex_binding_divisor_") + verifiers[verifierNdx].name).c_str(),	"Test VERTEX_BINDING_DIVISOR",	verifiers[verifierNdx].type));
			indexedGroup->addChild(new VertexBindingOffsetCase	(m_context, (std::string("vertex_binding_offset_") + verifiers[verifierNdx].name).c_str(),	"Test VERTEX_BINDING_OFFSET",	verifiers[verifierNdx].type));
			indexedGroup->addChild(new VertexBindingStrideCase	(m_context, (std::string("vertex_binding_stride_") + verifiers[verifierNdx].name).c_str(),	"Test VERTEX_BINDING_STRIDE",	verifiers[verifierNdx].type));
			indexedGroup->addChild(new VertexBindingBufferCase	(m_context, (std::string("vertex_binding_buffer_") + verifiers[verifierNdx].name).c_str(),	"Test VERTEX_BINDING_BUFFER",	verifiers[verifierNdx].type));
		}

		// mixed apis

		indexedGroup->addChild(new MixedVertexBindingDivisorCase(m_context, "vertex_binding_divisor_mixed",	"Test VERTEX_BINDING_DIVISOR"));
		indexedGroup->addChild(new MixedVertexBindingOffsetCase	(m_context, "vertex_binding_offset_mixed",	"Test VERTEX_BINDING_OFFSET"));
		indexedGroup->addChild(new MixedVertexBindingStrideCase	(m_context, "vertex_binding_stride_mixed",	"Test VERTEX_BINDING_STRIDE"));
		indexedGroup->addChild(new MixedVertexBindingBufferCase	(m_context, "vertex_binding_buffer_mixed",	"Test VERTEX_BINDING_BUFFER"));
	}
}

} // Functional
} // gles31
} // deqp