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

#include "es3fVertexArrayTest.hpp"
#include "glsVertexArrayTests.hpp"

#include <sstream>

using namespace deqp::gls;

namespace deqp
{
namespace gles3
{
namespace Functional
{

class SingleVertexArrayUsageGroup : public TestCaseGroup
{
public:
									SingleVertexArrayUsageGroup		(Context& context, Array::Usage usage);
	virtual							~SingleVertexArrayUsageGroup	(void);

	virtual void					init							(void);

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

	Array::Usage					m_usage;
};

SingleVertexArrayUsageGroup::SingleVertexArrayUsageGroup (Context& context, Array::Usage usage)
	: TestCaseGroup	(context, Array::usageTypeToString(usage).c_str(), Array::usageTypeToString(usage).c_str())
	, m_usage		(usage)
{
}

SingleVertexArrayUsageGroup::~SingleVertexArrayUsageGroup (void)
{
}

template<class T>
static std::string typeToString (T t)
{
	std::stringstream strm;
	strm << t;
	return strm.str();
}

void SingleVertexArrayUsageGroup::init (void)
{
	int					counts[]		= {1, 256};
	int					strides[]		= {0, -1, 17, 32}; // Tread negative value as sizeof input. Same as 0, but done outside of GL.
	Array::InputType	inputTypes[]	= {Array::INPUTTYPE_FLOAT, Array::INPUTTYPE_FIXED, Array::INPUTTYPE_SHORT, Array::INPUTTYPE_BYTE};

	for (int inputTypeNdx = 0; inputTypeNdx < DE_LENGTH_OF_ARRAY(inputTypes); inputTypeNdx++)
	{
		for (int countNdx = 0; countNdx < DE_LENGTH_OF_ARRAY(counts); countNdx++)
		{
			for (int strideNdx = 0; strideNdx < DE_LENGTH_OF_ARRAY(strides); strideNdx++)
			{
				const int			stride	= (strides[strideNdx] < 0 ? Array::inputTypeSize(inputTypes[inputTypeNdx]) * 2 : strides[strideNdx]);
				const bool			aligned	= (stride % Array::inputTypeSize(inputTypes[inputTypeNdx])) == 0;
				const std::string	name	= "stride" + typeToString(stride) + "_" + Array::inputTypeToString(inputTypes[inputTypeNdx]) + "_quads" + typeToString(counts[countNdx]);

				MultiVertexArrayTest::Spec::ArraySpec arraySpec(inputTypes[inputTypeNdx],
																Array::OUTPUTTYPE_VEC2,
																Array::STORAGE_BUFFER,
																m_usage,
																2,
																0,
																stride,
																false,
																GLValue::getMinValue(inputTypes[inputTypeNdx]),
																GLValue::getMaxValue(inputTypes[inputTypeNdx]));

				MultiVertexArrayTest::Spec spec;
				spec.primitive	= Array::PRIMITIVE_TRIANGLES;
				spec.drawCount	= counts[countNdx];
				spec.first		= 0;
				spec.arrays.push_back(arraySpec);

				if (aligned)
					addChild(new MultiVertexArrayTest(m_testCtx, m_context.getRenderContext(), spec, name.c_str(), name.c_str()));
			}
		}
	}
}

class SingleVertexArrayUsageTests : public TestCaseGroup
{
public:
									SingleVertexArrayUsageTests		(Context& context);
	virtual							~SingleVertexArrayUsageTests	(void);

	virtual void					init							(void);

private:
									SingleVertexArrayUsageTests		(const SingleVertexArrayUsageTests& other);
	SingleVertexArrayUsageTests&	operator=						(const SingleVertexArrayUsageTests& other);
};

SingleVertexArrayUsageTests::SingleVertexArrayUsageTests (Context& context)
	: TestCaseGroup(context, "usages", "Single vertex atribute, usage")
{
}

SingleVertexArrayUsageTests::~SingleVertexArrayUsageTests (void)
{
}

void SingleVertexArrayUsageTests::init (void)
{
	// Test usage
	Array::Usage		usages[]		= { Array::USAGE_STATIC_DRAW, Array::USAGE_STREAM_DRAW, Array::USAGE_DYNAMIC_DRAW, Array::USAGE_STATIC_COPY, Array::USAGE_STREAM_COPY, Array::USAGE_DYNAMIC_COPY, Array::USAGE_STATIC_READ, Array::USAGE_STREAM_READ, Array::USAGE_DYNAMIC_READ };
	for (int usageNdx = 0; usageNdx < DE_LENGTH_OF_ARRAY(usages); usageNdx++)
	{
		addChild(new SingleVertexArrayUsageGroup(m_context, usages[usageNdx]));
	}
}

class SingleVertexArrayStrideGroup : public TestCaseGroup
{
public:
									SingleVertexArrayStrideGroup	(Context& context, Array::InputType type);
	virtual							~SingleVertexArrayStrideGroup	(void);

	virtual void					init							(void);

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

	Array::InputType				m_type;
};

SingleVertexArrayStrideGroup::SingleVertexArrayStrideGroup (Context& context, Array::InputType type)
	: TestCaseGroup	(context, Array::inputTypeToString(type).c_str(), Array::inputTypeToString(type).c_str())
	, m_type		(type)
{
}

SingleVertexArrayStrideGroup::~SingleVertexArrayStrideGroup (void)
{
}

void SingleVertexArrayStrideGroup::init (void)
{
	Array::Storage		storages[]		= {Array::STORAGE_USER, Array::STORAGE_BUFFER};
	int					counts[]		= {1, 256};
	int					strides[]		= {/*0,*/ -1, 17, 32}; // Tread negative value as sizeof input. Same as 0, but done outside of GL.

	for (int storageNdx = 0; storageNdx < DE_LENGTH_OF_ARRAY(storages); storageNdx++)
	{
		for (int componentCount = 2; componentCount < 5; componentCount++)
		{
			for (int countNdx = 0; countNdx < DE_LENGTH_OF_ARRAY(counts); countNdx++)
			{
				for (int strideNdx = 0; strideNdx < DE_LENGTH_OF_ARRAY(strides); strideNdx++)
				{
					const bool	packed			= m_type == Array::INPUTTYPE_UNSIGNED_INT_2_10_10_10 || m_type == Array::INPUTTYPE_INT_2_10_10_10;
					const int	stride			= (strides[strideNdx] < 0) ? ((packed) ? (16) : (Array::inputTypeSize(m_type) * componentCount)) : (strides[strideNdx]);
					const int	alignment		= (packed) ? (Array::inputTypeSize(m_type) * componentCount) : (Array::inputTypeSize(m_type));
					const bool	bufferUnaligned	= (storages[storageNdx] == Array::STORAGE_BUFFER) && (stride % alignment) != 0;

					std::string name = Array::storageToString(storages[storageNdx]) + "_stride" + typeToString(stride) + "_components" + typeToString(componentCount) + "_quads" + typeToString(counts[countNdx]);

					if((m_type == Array::INPUTTYPE_UNSIGNED_INT_2_10_10_10 || m_type == Array::INPUTTYPE_INT_2_10_10_10) && componentCount != 4)
						continue;

					MultiVertexArrayTest::Spec::ArraySpec arraySpec(m_type,
																	Array::OUTPUTTYPE_VEC4,
																	storages[storageNdx],
																	Array::USAGE_DYNAMIC_DRAW,
																	componentCount,
																	0,
																	stride,
																	false,
																	GLValue::getMinValue(m_type),
																	GLValue::getMaxValue(m_type));

					MultiVertexArrayTest::Spec spec;
					spec.primitive	= Array::PRIMITIVE_TRIANGLES;
					spec.drawCount	= counts[countNdx];
					spec.first		= 0;
					spec.arrays.push_back(arraySpec);

					if (!bufferUnaligned)
						addChild(new MultiVertexArrayTest(m_testCtx, m_context.getRenderContext(), spec, name.c_str(), name.c_str()));
				}
			}
		}
	}
}

class SingleVertexArrayStrideTests : public TestCaseGroup
{
public:
									SingleVertexArrayStrideTests	(Context& context);
	virtual							~SingleVertexArrayStrideTests	(void);

	virtual void					init							(void);

private:
									SingleVertexArrayStrideTests	(const SingleVertexArrayStrideTests& other);
	SingleVertexArrayStrideTests&	operator=						(const SingleVertexArrayStrideTests& other);
};

SingleVertexArrayStrideTests::SingleVertexArrayStrideTests (Context& context)
	: TestCaseGroup(context, "strides", "Single stride vertex atribute")
{
}

SingleVertexArrayStrideTests::~SingleVertexArrayStrideTests (void)
{
}

void SingleVertexArrayStrideTests::init (void)
{
	Array::InputType	inputTypes[]	= {Array::INPUTTYPE_FLOAT, Array::INPUTTYPE_SHORT, Array::INPUTTYPE_BYTE, /*Array::INPUTTYPE_UNSIGNED_SHORT, Array::INPUTTYPE_UNSIGNED_BYTE,*/ Array::INPUTTYPE_FIXED, Array::INPUTTYPE_INT_2_10_10_10 };

	for (int inputTypeNdx = 0; inputTypeNdx < DE_LENGTH_OF_ARRAY(inputTypes); inputTypeNdx++)
	{
		addChild(new SingleVertexArrayStrideGroup(m_context, inputTypes[inputTypeNdx]));
	}
}

class SingleVertexArrayFirstGroup : public TestCaseGroup
{
public:
									SingleVertexArrayFirstGroup	(Context& context, Array::InputType type);
	virtual							~SingleVertexArrayFirstGroup	(void);

	virtual void					init							(void);

private:
									SingleVertexArrayFirstGroup	(const SingleVertexArrayFirstGroup& other);
	SingleVertexArrayFirstGroup&	operator=						(const SingleVertexArrayFirstGroup& other);
	Array::InputType				m_type;
};

SingleVertexArrayFirstGroup::SingleVertexArrayFirstGroup (Context& context, Array::InputType type)
	: TestCaseGroup	(context, Array::inputTypeToString(type).c_str(), Array::inputTypeToString(type).c_str())
	, m_type		(type)
{
}

SingleVertexArrayFirstGroup::~SingleVertexArrayFirstGroup (void)
{
}

void SingleVertexArrayFirstGroup::init (void)
{
	int					counts[]		= {5, 256};
	int					firsts[]		= {6, 24};
	int					offsets[]		= {1, 16, 17};
	int					strides[]		= {/*0,*/ -1, 17, 32}; // Tread negative value as sizeof input. Same as 0, but done outside of GL.

	for (int offsetNdx = 0; offsetNdx < DE_LENGTH_OF_ARRAY(offsets); offsetNdx++)
	{
		for (int countNdx = 0; countNdx < DE_LENGTH_OF_ARRAY(counts); countNdx++)
		{
			for (int strideNdx = 0; strideNdx < DE_LENGTH_OF_ARRAY(strides); strideNdx++)
			{
				for (int firstNdx = 0; firstNdx < DE_LENGTH_OF_ARRAY(firsts); firstNdx++)
				{
					const bool	packed			= m_type == Array::INPUTTYPE_UNSIGNED_INT_2_10_10_10 || m_type == Array::INPUTTYPE_INT_2_10_10_10;
					const int	componentCount	= (packed) ? (4) : (2);
					const int	stride			= (strides[strideNdx] < 0) ? ((packed) ? (8) : (Array::inputTypeSize(m_type) * componentCount)) : (strides[strideNdx]);
					const int	alignment		= (packed) ? (Array::inputTypeSize(m_type) * componentCount) : (Array::inputTypeSize(m_type));
					const bool	aligned			= ((stride % alignment) == 0) && ((offsets[offsetNdx] % alignment) == 0);
					std::string name			= "first" + typeToString(firsts[firstNdx]) + "_offset" + typeToString(offsets[offsetNdx]) + "_stride" + typeToString(stride) + "_quads" + typeToString(counts[countNdx]);

					MultiVertexArrayTest::Spec::ArraySpec arraySpec(m_type,
																	Array::OUTPUTTYPE_VEC2,
																	Array::STORAGE_BUFFER,
																	Array::USAGE_DYNAMIC_DRAW,
																	componentCount,
																	offsets[offsetNdx],
																	stride,
																	false,
																	GLValue::getMinValue(m_type),
																	GLValue::getMaxValue(m_type));

					MultiVertexArrayTest::Spec spec;
					spec.primitive	= Array::PRIMITIVE_TRIANGLES;
					spec.drawCount	= counts[countNdx];
					spec.first		= firsts[firstNdx];
					spec.arrays.push_back(arraySpec);

					if (aligned)
						addChild(new MultiVertexArrayTest(m_testCtx, m_context.getRenderContext(), spec, name.c_str(), name.c_str()));
				}
			}
		}
	}
}

class SingleVertexArrayFirstTests : public TestCaseGroup
{
public:
									SingleVertexArrayFirstTests	(Context& context);
	virtual							~SingleVertexArrayFirstTests	(void);

	virtual void					init							(void);

private:
									SingleVertexArrayFirstTests	(const SingleVertexArrayFirstTests& other);
	SingleVertexArrayFirstTests&	operator=						(const SingleVertexArrayFirstTests& other);
};

SingleVertexArrayFirstTests::SingleVertexArrayFirstTests (Context& context)
	: TestCaseGroup(context, "first", "Single vertex attribute, different first values to drawArrays")
{
}

SingleVertexArrayFirstTests::~SingleVertexArrayFirstTests (void)
{
}

void SingleVertexArrayFirstTests::init (void)
{
	// Test offset with different input types, component counts and storage, Usage(?)
	Array::InputType	inputTypes[]	= {Array::INPUTTYPE_FLOAT, Array::INPUTTYPE_BYTE, Array::INPUTTYPE_INT_2_10_10_10 };

	for (int inputTypeNdx = 0; inputTypeNdx < DE_LENGTH_OF_ARRAY(inputTypes); inputTypeNdx++)
	{
		addChild(new SingleVertexArrayFirstGroup(m_context, inputTypes[inputTypeNdx]));
	}
}

class SingleVertexArrayOffsetGroup : public TestCaseGroup
{
public:
									SingleVertexArrayOffsetGroup	(Context& context, Array::InputType type);
	virtual							~SingleVertexArrayOffsetGroup	(void);

	virtual void					init							(void);

private:
									SingleVertexArrayOffsetGroup	(const SingleVertexArrayOffsetGroup& other);
	SingleVertexArrayOffsetGroup&	operator=						(const SingleVertexArrayOffsetGroup& other);
	Array::InputType				m_type;
};

SingleVertexArrayOffsetGroup::SingleVertexArrayOffsetGroup (Context& context, Array::InputType type)
	: TestCaseGroup	(context, Array::inputTypeToString(type).c_str(), Array::inputTypeToString(type).c_str())
	, m_type		(type)
{
}

SingleVertexArrayOffsetGroup::~SingleVertexArrayOffsetGroup (void)
{
}

void SingleVertexArrayOffsetGroup::init (void)
{
	int					counts[]		= {1, 256};
	int					offsets[]		= {1, 4, 17, 32};
	int					strides[]		= {/*0,*/ -1, 17, 32}; // Tread negative value as sizeof input. Same as 0, but done outside of GL.

	for (int offsetNdx = 0; offsetNdx < DE_LENGTH_OF_ARRAY(offsets); offsetNdx++)
	{
		for (int countNdx = 0; countNdx < DE_LENGTH_OF_ARRAY(counts); countNdx++)
		{
			for (int strideNdx = 0; strideNdx < DE_LENGTH_OF_ARRAY(strides); strideNdx++)
			{
				const bool			packed			= m_type == Array::INPUTTYPE_UNSIGNED_INT_2_10_10_10 || m_type == Array::INPUTTYPE_INT_2_10_10_10;
				const int			componentCount	= (packed) ? (4) : (2);
				const int			stride			= (strides[strideNdx] < 0 ? Array::inputTypeSize(m_type) * componentCount : strides[strideNdx]);
				const int			alignment		= (packed) ? (Array::inputTypeSize(m_type) * componentCount) : (Array::inputTypeSize(m_type));
				const bool			aligned			= ((stride % alignment) == 0) && ((offsets[offsetNdx] % alignment) == 0);
				const std::string	name			= "offset" + typeToString(offsets[offsetNdx]) + "_stride" + typeToString(stride) + "_quads" + typeToString(counts[countNdx]);

				MultiVertexArrayTest::Spec::ArraySpec arraySpec(m_type,
																Array::OUTPUTTYPE_VEC2,
																Array::STORAGE_BUFFER,
																Array::USAGE_DYNAMIC_DRAW,
																componentCount,
																offsets[offsetNdx],
																stride,
																false,
																GLValue::getMinValue(m_type),
																GLValue::getMaxValue(m_type));

				MultiVertexArrayTest::Spec spec;
				spec.primitive	= Array::PRIMITIVE_TRIANGLES;
				spec.drawCount	= counts[countNdx];
				spec.first		= 0;
				spec.arrays.push_back(arraySpec);

				if (aligned)
					addChild(new MultiVertexArrayTest(m_testCtx, m_context.getRenderContext(), spec, name.c_str(), name.c_str()));
			}
		}
	}
}

class SingleVertexArrayOffsetTests : public TestCaseGroup
{
public:
									SingleVertexArrayOffsetTests	(Context& context);
	virtual							~SingleVertexArrayOffsetTests	(void);

	virtual void					init							(void);

private:
									SingleVertexArrayOffsetTests	(const SingleVertexArrayOffsetTests& other);
	SingleVertexArrayOffsetTests&	operator=						(const SingleVertexArrayOffsetTests& other);
};

SingleVertexArrayOffsetTests::SingleVertexArrayOffsetTests (Context& context)
	: TestCaseGroup(context, "offset", "Single vertex atribute offset element")
{
}

SingleVertexArrayOffsetTests::~SingleVertexArrayOffsetTests (void)
{
}

void SingleVertexArrayOffsetTests::init (void)
{
	// Test offset with different input types, component counts and storage, Usage(?)
	Array::InputType	inputTypes[]	= {Array::INPUTTYPE_FLOAT, Array::INPUTTYPE_BYTE, Array::INPUTTYPE_INT_2_10_10_10 };

	for (int inputTypeNdx = 0; inputTypeNdx < DE_LENGTH_OF_ARRAY(inputTypes); inputTypeNdx++)
	{
		addChild(new SingleVertexArrayOffsetGroup(m_context, inputTypes[inputTypeNdx]));
	}
}

class SingleVertexArrayNormalizeGroup : public TestCaseGroup
{
public:
										SingleVertexArrayNormalizeGroup		(Context& context, Array::InputType type);
	virtual								~SingleVertexArrayNormalizeGroup	(void);

	virtual void						init								(void);

private:
										SingleVertexArrayNormalizeGroup		(const SingleVertexArrayNormalizeGroup& other);
	SingleVertexArrayNormalizeGroup&	operator=							(const SingleVertexArrayNormalizeGroup& other);
	Array::InputType					m_type;
};

SingleVertexArrayNormalizeGroup::SingleVertexArrayNormalizeGroup (Context& context, Array::InputType type)
	: TestCaseGroup	(context, Array::inputTypeToString(type).c_str(), Array::inputTypeToString(type).c_str())
	, m_type		(type)
{
}

SingleVertexArrayNormalizeGroup::~SingleVertexArrayNormalizeGroup (void)
{
}

void SingleVertexArrayNormalizeGroup::init (void)
{
	int					counts[]		= {1, 256};

	for (int componentCount = 2; componentCount < 5; componentCount++)
	{
		for (int countNdx = 0; countNdx < DE_LENGTH_OF_ARRAY(counts); countNdx++)
		{
			if((m_type == Array::INPUTTYPE_UNSIGNED_INT_2_10_10_10 || m_type == Array::INPUTTYPE_INT_2_10_10_10) && componentCount != 4)
				continue;

			std::string name = "components" + typeToString(componentCount) + "_quads" + typeToString(counts[countNdx]);

			MultiVertexArrayTest::Spec::ArraySpec arraySpec(m_type,
															Array::OUTPUTTYPE_VEC4,
															Array::STORAGE_USER,
															Array::USAGE_DYNAMIC_DRAW,
															componentCount,
															0,
															0,
															true,
															GLValue::getMinValue(m_type),
															GLValue::getMaxValue(m_type));

			MultiVertexArrayTest::Spec spec;
			spec.primitive	= Array::PRIMITIVE_TRIANGLES;
			spec.drawCount	= counts[countNdx];
			spec.first		= 0;
			spec.arrays.push_back(arraySpec);

			addChild(new MultiVertexArrayTest(m_testCtx, m_context.getRenderContext(), spec, name.c_str(), name.c_str()));
		}
	}
}

class SingleVertexArrayNormalizeTests : public TestCaseGroup
{
public:
										SingleVertexArrayNormalizeTests		(Context& context);
	virtual								~SingleVertexArrayNormalizeTests	(void);

	virtual void						init								(void);

private:
										SingleVertexArrayNormalizeTests		(const SingleVertexArrayNormalizeTests& other);
	SingleVertexArrayNormalizeTests&	operator=							(const SingleVertexArrayNormalizeTests& other);
};

SingleVertexArrayNormalizeTests::SingleVertexArrayNormalizeTests (Context& context)
	: TestCaseGroup(context, "normalize", "Single normalize vertex atribute")
{
}

SingleVertexArrayNormalizeTests::~SingleVertexArrayNormalizeTests (void)
{
}

void SingleVertexArrayNormalizeTests::init (void)
{
	// Test normalization with different input types, component counts and storage
	Array::InputType	inputTypes[]	= {Array::INPUTTYPE_FLOAT, Array::INPUTTYPE_SHORT, Array::INPUTTYPE_BYTE, Array::INPUTTYPE_UNSIGNED_SHORT, Array::INPUTTYPE_UNSIGNED_BYTE, Array::INPUTTYPE_FIXED, Array::INPUTTYPE_UNSIGNED_INT, Array::INPUTTYPE_INT, Array::INPUTTYPE_HALF , Array::INPUTTYPE_UNSIGNED_INT_2_10_10_10, Array::INPUTTYPE_INT_2_10_10_10 };

	for (int inputTypeNdx = 0; inputTypeNdx < DE_LENGTH_OF_ARRAY(inputTypes); inputTypeNdx++)
	{
		addChild(new SingleVertexArrayNormalizeGroup(m_context, inputTypes[inputTypeNdx]));
	}
}

class SingleVertexArrayOutputTypeGroup : public TestCaseGroup
{
public:
										SingleVertexArrayOutputTypeGroup	(Context& context, Array::InputType type);
	virtual								~SingleVertexArrayOutputTypeGroup	(void);

	virtual void						init								(void);

private:
										SingleVertexArrayOutputTypeGroup	(const SingleVertexArrayOutputTypeGroup& other);
	SingleVertexArrayOutputTypeGroup&	operator=							(const SingleVertexArrayOutputTypeGroup& other);
	Array::InputType					m_type;
};

SingleVertexArrayOutputTypeGroup::SingleVertexArrayOutputTypeGroup (Context& context, Array::InputType type)
	: TestCaseGroup	(context, Array::inputTypeToString(type).c_str(), Array::inputTypeToString(type).c_str())
	, m_type		(type)
{
}

SingleVertexArrayOutputTypeGroup::~SingleVertexArrayOutputTypeGroup (void)
{
}

void SingleVertexArrayOutputTypeGroup::init (void)
{
	Array::OutputType	outputTypes[]	= {Array::OUTPUTTYPE_VEC2, Array::OUTPUTTYPE_VEC3, Array::OUTPUTTYPE_VEC4, Array::OUTPUTTYPE_IVEC2, Array::OUTPUTTYPE_IVEC3, Array::OUTPUTTYPE_IVEC4, Array::OUTPUTTYPE_UVEC2, Array::OUTPUTTYPE_UVEC3, Array::OUTPUTTYPE_UVEC4 };
	Array::Storage		storages[]		= {Array::STORAGE_USER};
	int					counts[]		= {1, 256};

	for (int outputTypeNdx = 0; outputTypeNdx < DE_LENGTH_OF_ARRAY(outputTypes); outputTypeNdx++)
	{
		for (int storageNdx = 0; storageNdx < DE_LENGTH_OF_ARRAY(storages); storageNdx++)
		{
			for (int componentCount = 2; componentCount < 5; componentCount++)
			{
				for (int countNdx = 0; countNdx < DE_LENGTH_OF_ARRAY(counts); countNdx++)
				{
					std::string name = "components" + typeToString(componentCount) + "_" + Array::outputTypeToString(outputTypes[outputTypeNdx]) + "_quads" + typeToString(counts[countNdx]);

					const bool inputIsSignedInteger		= m_type == Array::INPUTTYPE_INT || m_type == Array::INPUTTYPE_SHORT || m_type == Array::INPUTTYPE_BYTE;
					const bool inputIsUnignedInteger	= m_type == Array::INPUTTYPE_UNSIGNED_INT || m_type == Array::INPUTTYPE_UNSIGNED_SHORT || m_type == Array::INPUTTYPE_UNSIGNED_BYTE;
					const bool outputIsSignedInteger	= outputTypes[outputTypeNdx] == Array::OUTPUTTYPE_IVEC2 || outputTypes[outputTypeNdx] == Array::OUTPUTTYPE_IVEC3 || outputTypes[outputTypeNdx] == Array::OUTPUTTYPE_IVEC4;
					const bool outputIsUnsignedInteger	= outputTypes[outputTypeNdx] == Array::OUTPUTTYPE_UVEC2 || outputTypes[outputTypeNdx] == Array::OUTPUTTYPE_UVEC3 || outputTypes[outputTypeNdx] == Array::OUTPUTTYPE_UVEC4;

					// If input type is float type and output type is int type skip
					if ((m_type == Array::INPUTTYPE_FLOAT ||  m_type == Array::INPUTTYPE_HALF || m_type == Array::INPUTTYPE_FIXED) && (outputTypes[outputTypeNdx] >= Array::OUTPUTTYPE_INT))
						continue;

					if((m_type == Array::INPUTTYPE_UNSIGNED_INT_2_10_10_10 || m_type == Array::INPUTTYPE_INT_2_10_10_10) && (outputTypes[outputTypeNdx] >= Array::OUTPUTTYPE_INT))
						continue;

					if((m_type == Array::INPUTTYPE_UNSIGNED_INT_2_10_10_10 || m_type == Array::INPUTTYPE_INT_2_10_10_10) && componentCount != 4)
						continue;

					// Loading signed data as unsigned causes undefined values and vice versa
					if (inputIsSignedInteger && outputIsUnsignedInteger)
						continue;
					if (inputIsUnignedInteger && outputIsSignedInteger)
						continue;

					MultiVertexArrayTest::Spec::ArraySpec arraySpec(m_type,
																	outputTypes[outputTypeNdx],
																	storages[storageNdx],
																	Array::USAGE_DYNAMIC_DRAW,
																	componentCount,
																	0,
																	0,
																	false,
																	GLValue::getMinValue(m_type),
																	GLValue::getMaxValue(m_type));

					MultiVertexArrayTest::Spec spec;
					spec.primitive	= Array::PRIMITIVE_TRIANGLES;
					spec.drawCount	= counts[countNdx];
					spec.first		= 0;
					spec.arrays.push_back(arraySpec);

					addChild(new MultiVertexArrayTest(m_testCtx, m_context.getRenderContext(), spec, name.c_str(), name.c_str()));
				}
			}
		}
	}
}

class SingleVertexArrayOutputTypeTests : public TestCaseGroup
{
public:
										SingleVertexArrayOutputTypeTests	(Context& context);
	virtual								~SingleVertexArrayOutputTypeTests	(void);

	virtual void						init								(void);

private:
										SingleVertexArrayOutputTypeTests	(const SingleVertexArrayOutputTypeTests& other);
	SingleVertexArrayOutputTypeTests&	operator=							(const SingleVertexArrayOutputTypeTests& other);
};

SingleVertexArrayOutputTypeTests::SingleVertexArrayOutputTypeTests (Context& context)
	: TestCaseGroup(context, "output_types", "Single output type vertex atribute")
{
}

SingleVertexArrayOutputTypeTests::~SingleVertexArrayOutputTypeTests (void)
{
}

void SingleVertexArrayOutputTypeTests::init (void)
{
	// Test output types with different input types, component counts and storage, Usage?, Precision?, float?
	Array::InputType	inputTypes[]	= {Array::INPUTTYPE_FLOAT, Array::INPUTTYPE_SHORT, Array::INPUTTYPE_BYTE, Array::INPUTTYPE_UNSIGNED_SHORT, Array::INPUTTYPE_UNSIGNED_BYTE, Array::INPUTTYPE_FIXED, Array::INPUTTYPE_UNSIGNED_INT, Array::INPUTTYPE_INT, Array::INPUTTYPE_HALF, Array::INPUTTYPE_UNSIGNED_INT_2_10_10_10, Array::INPUTTYPE_INT_2_10_10_10 };

	for (int inputTypeNdx = 0; inputTypeNdx < DE_LENGTH_OF_ARRAY(inputTypes); inputTypeNdx++)
	{
		addChild(new SingleVertexArrayOutputTypeGroup(m_context, inputTypes[inputTypeNdx]));
	}
}


class SingleVertexArrayTestGroup : public TestCaseGroup
{
public:
									SingleVertexArrayTestGroup	(Context& context);
	virtual							~SingleVertexArrayTestGroup	(void);

	virtual void					init						(void);

private:
									SingleVertexArrayTestGroup	(const SingleVertexArrayTestGroup& other);
	SingleVertexArrayTestGroup&		operator=					(const SingleVertexArrayTestGroup& other);
};

SingleVertexArrayTestGroup::SingleVertexArrayTestGroup (Context& context)
	: TestCaseGroup(context, "single_attribute", "Single vertex atribute")
{
}

SingleVertexArrayTestGroup::~SingleVertexArrayTestGroup (void)
{
}

void SingleVertexArrayTestGroup::init (void)
{
	addChild(new SingleVertexArrayStrideTests(m_context));
	addChild(new SingleVertexArrayNormalizeTests(m_context));
	addChild(new SingleVertexArrayOutputTypeTests(m_context));
	addChild(new SingleVertexArrayUsageTests(m_context));
	addChild(new SingleVertexArrayOffsetTests(m_context));
	addChild(new SingleVertexArrayFirstTests(m_context));
}

class MultiVertexArrayCountTests : public TestCaseGroup
{
public:
									MultiVertexArrayCountTests	(Context& context);
	virtual							~MultiVertexArrayCountTests	(void);

	virtual void					init						(void);

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

	std::string						getTestName					(const MultiVertexArrayTest::Spec& spec);
};

MultiVertexArrayCountTests::MultiVertexArrayCountTests (Context& context)
	: TestCaseGroup(context, "attribute_count", "Attribute counts")
{
}

MultiVertexArrayCountTests::~MultiVertexArrayCountTests (void)
{
}

std::string MultiVertexArrayCountTests::getTestName (const MultiVertexArrayTest::Spec& spec)
{
	std::stringstream name;
	name
		<< spec.arrays.size();

	return name.str();
}

void MultiVertexArrayCountTests::init (void)
{
	// Test attribute counts
	int arrayCounts[] = {2, 3, 4, 5, 6, 7, 8};

	for (int arrayCountNdx = 0; arrayCountNdx < DE_LENGTH_OF_ARRAY(arrayCounts); arrayCountNdx++)
	{
		MultiVertexArrayTest::Spec spec;

		spec.primitive	= Array::PRIMITIVE_TRIANGLES;
		spec.drawCount	= 256;
		spec.first		= 0;

		for (int arrayNdx = 0; arrayNdx < arrayCounts[arrayCountNdx]; arrayNdx++)
		{
			MultiVertexArrayTest::Spec::ArraySpec arraySpec(Array::INPUTTYPE_FLOAT,
															Array::OUTPUTTYPE_VEC2,
															Array::STORAGE_USER,
															Array::USAGE_DYNAMIC_DRAW,
															2,
															0,
															0,
															false,
															GLValue::getMinValue(Array::INPUTTYPE_FLOAT),
															GLValue::getMaxValue(Array::INPUTTYPE_FLOAT));

			spec.arrays.push_back(arraySpec);
		}

		std::string name = getTestName(spec);
		std::string desc = getTestName(spec);

		addChild(new MultiVertexArrayTest(m_testCtx, m_context.getRenderContext(), spec, name.c_str(), desc.c_str()));
	}
}

class MultiVertexArrayStorageTests : public TestCaseGroup
{
public:
									MultiVertexArrayStorageTests	(Context& context);
	virtual							~MultiVertexArrayStorageTests	(void);

	virtual void					init							(void);

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

	void							addStorageCases 				(MultiVertexArrayTest::Spec spec, int depth);
	std::string						getTestName						(const MultiVertexArrayTest::Spec& spec);
};

MultiVertexArrayStorageTests::MultiVertexArrayStorageTests (Context& context)
	: TestCaseGroup(context, "storage", "Attribute storages")
{
}

MultiVertexArrayStorageTests::~MultiVertexArrayStorageTests (void)
{
}

std::string MultiVertexArrayStorageTests::getTestName (const MultiVertexArrayTest::Spec& spec)
{
	std::stringstream name;
	name
		<< spec.arrays.size();

	for (int arrayNdx = 0; arrayNdx < (int)spec.arrays.size(); arrayNdx++)
	{
		name
			<< "_"
			<< Array::storageToString(spec.arrays[arrayNdx].storage);
	}

	return name.str();
}

void MultiVertexArrayStorageTests::addStorageCases (MultiVertexArrayTest::Spec spec, int depth)
{
	if (depth == 0)
	{
		// Skip trivial case, used elsewhere
		bool ok = false;
		for (int arrayNdx = 0; arrayNdx < (int)spec.arrays.size(); arrayNdx++)
		{
			if (spec.arrays[arrayNdx].storage != Array::STORAGE_USER)
			{
				ok = true;
				break;
			}
		}

		if (!ok)
			return;

		std::string name = getTestName(spec);
		std::string desc = getTestName(spec);

		addChild(new MultiVertexArrayTest(m_testCtx, m_context.getRenderContext(), spec, name.c_str(), desc.c_str()));
		return;
	}

	Array::Storage storages[] = {Array::STORAGE_USER, Array::STORAGE_BUFFER};
	for (int storageNdx = 0; storageNdx < DE_LENGTH_OF_ARRAY(storages); storageNdx++)
	{
		MultiVertexArrayTest::Spec::ArraySpec arraySpec(Array::INPUTTYPE_FLOAT,
														Array::OUTPUTTYPE_VEC2,
														storages[storageNdx],
														Array::USAGE_DYNAMIC_DRAW,
														2,
														0,
														0,
														false,
														GLValue::getMinValue(Array::INPUTTYPE_FLOAT),
														GLValue::getMaxValue(Array::INPUTTYPE_FLOAT));

		MultiVertexArrayTest::Spec _spec = spec;
		_spec.arrays.push_back(arraySpec);
		addStorageCases(_spec, depth-1);
	}
}


void MultiVertexArrayStorageTests::init (void)
{
	// Test different storages
	int arrayCounts[] = {3};

	MultiVertexArrayTest::Spec spec;

	spec.primitive	= Array::PRIMITIVE_TRIANGLES;
	spec.drawCount	= 256;
	spec.first		= 0;

	for (int arrayCountNdx = 0; arrayCountNdx < DE_LENGTH_OF_ARRAY(arrayCounts); arrayCountNdx++)
		addStorageCases(spec, arrayCounts[arrayCountNdx]);
}

class MultiVertexArrayStrideTests : public TestCaseGroup
{
public:
									MultiVertexArrayStrideTests		(Context& context);
	virtual							~MultiVertexArrayStrideTests	(void);

	virtual void					init							(void);

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

	void							addStrideCases 					(MultiVertexArrayTest::Spec spec, int depth);
	std::string						getTestName						(const MultiVertexArrayTest::Spec& spec);
};

MultiVertexArrayStrideTests::MultiVertexArrayStrideTests (Context& context)
	: TestCaseGroup(context, "stride", "Strides")
{
}

MultiVertexArrayStrideTests::~MultiVertexArrayStrideTests (void)
{
}

std::string MultiVertexArrayStrideTests::getTestName (const MultiVertexArrayTest::Spec& spec)
{
	std::stringstream name;

	name
		<< spec.arrays.size();

	for (int arrayNdx = 0; arrayNdx < (int)spec.arrays.size(); arrayNdx++)
	{
		name
			<< "_"
			<< Array::inputTypeToString(spec.arrays[arrayNdx].inputType)
			<< spec.arrays[arrayNdx].componentCount << "_"
			<< spec.arrays[arrayNdx].stride;
	}

	return name.str();
}

void MultiVertexArrayStrideTests::init (void)
{
	// Test different strides, with multiple arrays, input types??
	int arrayCounts[] = {3};

	MultiVertexArrayTest::Spec spec;

	spec.primitive	= Array::PRIMITIVE_TRIANGLES;
	spec.drawCount	= 256;
	spec.first		= 0;

	for (int arrayCountNdx = 0; arrayCountNdx < DE_LENGTH_OF_ARRAY(arrayCounts); arrayCountNdx++)
		addStrideCases(spec, arrayCounts[arrayCountNdx]);
}

void MultiVertexArrayStrideTests::addStrideCases (MultiVertexArrayTest::Spec spec, int depth)
{
	if (depth == 0)
	{
		std::string name = getTestName(spec);
		std::string desc = getTestName(spec);
		addChild(new MultiVertexArrayTest(m_testCtx, m_context.getRenderContext(), spec, name.c_str(), desc.c_str()));
		return;
	}

	int strides[]	= {0, -1, 17, 32};

	for (int strideNdx = 0; strideNdx < DE_LENGTH_OF_ARRAY(strides); strideNdx++)
	{
		const int componentCount = 2;
		MultiVertexArrayTest::Spec::ArraySpec arraySpec(Array::INPUTTYPE_FLOAT,
														Array::OUTPUTTYPE_VEC2,
														Array::STORAGE_USER,
														Array::USAGE_DYNAMIC_DRAW,
														componentCount,
														0,
														(strides[strideNdx] >= 0 ? strides[strideNdx] : componentCount * Array::inputTypeSize(Array::INPUTTYPE_FLOAT)),
														false,
														GLValue::getMinValue(Array::INPUTTYPE_FLOAT),
														GLValue::getMaxValue(Array::INPUTTYPE_FLOAT));

		MultiVertexArrayTest::Spec _spec = spec;
		_spec.arrays.push_back(arraySpec);
		addStrideCases(_spec, depth-1);
	}
}

class MultiVertexArrayOutputTests : public TestCaseGroup
{
public:
										MultiVertexArrayOutputTests		(Context& context);
	virtual								~MultiVertexArrayOutputTests	(void);

	virtual void						init								(void);

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

	void								addInputTypeCases 					(MultiVertexArrayTest::Spec spec, int depth);
	std::string							getTestName							(const MultiVertexArrayTest::Spec& spec);
};

MultiVertexArrayOutputTests::MultiVertexArrayOutputTests (Context& context)
	: TestCaseGroup(context, "input_types", "input types")
{
}

MultiVertexArrayOutputTests::~MultiVertexArrayOutputTests (void)
{
}

std::string MultiVertexArrayOutputTests::getTestName (const MultiVertexArrayTest::Spec& spec)
{
	std::stringstream name;

	name
		<< spec.arrays.size();

	for (int arrayNdx = 0; arrayNdx < (int)spec.arrays.size(); arrayNdx++)
	{
		name
			<< "_"
			<< Array::inputTypeToString(spec.arrays[arrayNdx].inputType)
			<< spec.arrays[arrayNdx].componentCount << "_"
			<< Array::outputTypeToString(spec.arrays[arrayNdx].outputType);
	}

	return name.str();
}

void MultiVertexArrayOutputTests::init (void)
{
	// Test different input types, with multiple arrays
	int arrayCounts[] = {3};

	MultiVertexArrayTest::Spec spec;

	spec.primitive	= Array::PRIMITIVE_TRIANGLES;
	spec.drawCount	= 256;
	spec.first		= 0;

	for (int arrayCountNdx = 0; arrayCountNdx < DE_LENGTH_OF_ARRAY(arrayCounts); arrayCountNdx++)
		addInputTypeCases(spec, arrayCounts[arrayCountNdx]);
}

void MultiVertexArrayOutputTests::addInputTypeCases (MultiVertexArrayTest::Spec spec, int depth)
{
	if (depth == 0)
	{
		std::string name = getTestName(spec);
		std::string desc = getTestName(spec);
		addChild(new MultiVertexArrayTest(m_testCtx, m_context.getRenderContext(), spec, name.c_str(), desc.c_str()));
		return;
	}

	Array::InputType inputTypes[] = {Array::INPUTTYPE_FIXED, Array::INPUTTYPE_BYTE, Array::INPUTTYPE_SHORT, Array::INPUTTYPE_UNSIGNED_BYTE, Array::INPUTTYPE_UNSIGNED_SHORT};
	for (int inputTypeNdx = 0; inputTypeNdx < DE_LENGTH_OF_ARRAY(inputTypes); inputTypeNdx++)
	{
		MultiVertexArrayTest::Spec::ArraySpec arraySpec(inputTypes[inputTypeNdx],
														Array::OUTPUTTYPE_VEC2,
														Array::STORAGE_USER,
														Array::USAGE_DYNAMIC_DRAW,
														2,
														0,
														0,
														false,
														GLValue::getMinValue(inputTypes[inputTypeNdx]),
														GLValue::getMaxValue(inputTypes[inputTypeNdx]));

		MultiVertexArrayTest::Spec _spec = spec;
		_spec.arrays.push_back(arraySpec);
		addInputTypeCases(_spec, depth-1);
	}
}

class MultiVertexArrayTestGroup : public TestCaseGroup
{
public:
									MultiVertexArrayTestGroup	(Context& context);
	virtual							~MultiVertexArrayTestGroup	(void);

	virtual void					init						(void);

private:
									MultiVertexArrayTestGroup	(const MultiVertexArrayTestGroup& other);
	MultiVertexArrayTestGroup&		operator=					(const MultiVertexArrayTestGroup& other);
};

MultiVertexArrayTestGroup::MultiVertexArrayTestGroup (Context& context)
	: TestCaseGroup(context, "multiple_attributes", "Multiple vertex atributes")
{
}

MultiVertexArrayTestGroup::~MultiVertexArrayTestGroup (void)
{
}

void MultiVertexArrayTestGroup::init (void)
{
	addChild(new MultiVertexArrayCountTests(m_context));
	addChild(new MultiVertexArrayStorageTests(m_context));
	addChild(new MultiVertexArrayStrideTests(m_context));
	addChild(new MultiVertexArrayOutputTests(m_context));
}

VertexArrayTestGroup::VertexArrayTestGroup (Context& context)
	: TestCaseGroup(context, "vertex_arrays", "Vertex array and array tests")
{
}

VertexArrayTestGroup::~VertexArrayTestGroup (void)
{
}

void VertexArrayTestGroup::init (void)
{
	addChild(new SingleVertexArrayTestGroup(m_context));
	addChild(new MultiVertexArrayTestGroup(m_context));
}

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