/*-------------------------------------------------------------------------
 * 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 Fbo state query tests.
 *//*--------------------------------------------------------------------*/

#include "es3fFboStateQueryTests.hpp"
#include "glsStateQueryUtil.hpp"
#include "es3fApiCase.hpp"
#include "gluRenderContext.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuRenderTarget.hpp"
#include "deMath.h"

using namespace glw; // GLint and other GL types
using deqp::gls::StateQueryUtil::StateQueryMemoryWriteGuard;


namespace deqp
{
namespace gles3
{
namespace Functional
{
namespace
{

void checkAttachmentComponentSizeAtLeast (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLenum target, GLenum attachment, int r, int g, int b, int a, int d, int s)
{
	using tcu::TestLog;

	const int referenceSizes[] = {r, g, b, a, d, s};
	const GLenum paramNames[] =
	{
		GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE,	GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE,
		GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
		GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE
	};

	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(referenceSizes) == DE_LENGTH_OF_ARRAY(paramNames));

	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(referenceSizes); ++ndx)
	{
		if (referenceSizes[ndx] == -1)
			continue;

		StateQueryMemoryWriteGuard<GLint> state;
		gl.glGetFramebufferAttachmentParameteriv(target, attachment, paramNames[ndx], &state);

		if (!state.verifyValidity(testCtx))
		{
			gl.glGetError(); // just log the error
			continue;
		}

		if (state < referenceSizes[ndx])
		{
			testCtx.getLog() << TestLog::Message << "// ERROR: Expected greater or equal to " << referenceSizes[ndx] << "; got " << state << TestLog::EndMessage;
			if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
				testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid value");
		}
	}
}

void checkAttachmentComponentSizeExactly (tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLenum target, GLenum attachment, int r, int g, int b, int a, int d, int s)
{
	using tcu::TestLog;

	const int referenceSizes[] = {r, g, b, a, d, s};
	const GLenum paramNames[] =
	{
		GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE,	GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE,
		GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
		GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE
	};

	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(referenceSizes) == DE_LENGTH_OF_ARRAY(paramNames));

	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(referenceSizes); ++ndx)
	{
		if (referenceSizes[ndx] == -1)
			continue;

		StateQueryMemoryWriteGuard<GLint> state;
		gl.glGetFramebufferAttachmentParameteriv(target, attachment, paramNames[ndx], &state);

		if (!state.verifyValidity(testCtx))
		{
			gl.glGetError(); // just log the error
			continue;
		}

		if (state != referenceSizes[ndx])
		{
			testCtx.getLog() << TestLog::Message << "// ERROR: Expected " << referenceSizes[ndx] << "; got " << state << TestLog::EndMessage;
			if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
				testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid value");
		}
	}
}

void checkIntEquals (tcu::TestContext& testCtx, GLint got, GLint expected)
{
	using tcu::TestLog;

	if (got != expected)
	{
		testCtx.getLog() << TestLog::Message << "// ERROR: Expected " << expected << "; got " << got << TestLog::EndMessage;
		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid value");
	}
}

void checkIntEqualsAny (tcu::TestContext& testCtx, GLint got, GLint expected0, GLint expected1)
{
	using tcu::TestLog;

	if (got != expected0 && got != expected1)
	{
		testCtx.getLog() << TestLog::Message << "// ERROR: Expected " << expected0 << " or " << expected1 << "; got " << got << TestLog::EndMessage;
		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid value");
	}
}

void checkAttachmentParam(tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLenum target, GLenum attachment, GLenum pname, GLenum reference)
{
	StateQueryMemoryWriteGuard<GLint> state;
	gl.glGetFramebufferAttachmentParameteriv(target, attachment, pname, &state);

	if (state.verifyValidity(testCtx))
		checkIntEquals(testCtx, state, reference);
}

void checkColorAttachmentParam(tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLenum target, GLenum pname, GLenum reference)
{
	checkAttachmentParam(testCtx, gl, target, GL_COLOR_ATTACHMENT0, pname, reference);
}

class DefaultFramebufferCase : public ApiCase
{
public:
	DefaultFramebufferCase (Context& context, const char* name, const char* description, GLenum framebufferTarget)
		: ApiCase				(context, name, description)
		, m_framebufferTarget	(framebufferTarget)
	{
	}

	void test (void)
	{
		const bool		hasColorBuffer =	m_context.getRenderTarget().getPixelFormat().redBits   > 0 ||
											m_context.getRenderTarget().getPixelFormat().greenBits > 0 ||
											m_context.getRenderTarget().getPixelFormat().blueBits  > 0 ||
											m_context.getRenderTarget().getPixelFormat().alphaBits > 0;
		const GLenum	attachments[] =
		{
			GL_BACK,
			GL_DEPTH,
			GL_STENCIL
		};
		const bool		attachmentExists[] =
		{
			hasColorBuffer,
			m_context.getRenderTarget().getDepthBits() > 0,
			m_context.getRenderTarget().getStencilBits() > 0
		};

		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(attachments); ++ndx)
		{
			StateQueryMemoryWriteGuard<GLint> state;
			glGetFramebufferAttachmentParameteriv(m_framebufferTarget, attachments[ndx], GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &state);
			expectError(GL_NO_ERROR);

			if (state.verifyValidity(m_testCtx))
			{
				if (attachmentExists[ndx])
				{
					checkIntEquals(m_testCtx, state, GL_FRAMEBUFFER_DEFAULT);
				}
				else
				{
					// \note [jarkko] FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE "identifes the type of object which contains the attached image". However, it
					// is unclear if an object of type FRAMEBUFFER_DEFAULT can contain a null image (or a 0-bits-per-pixel image). Accept both
					// FRAMEBUFFER_DEFAULT and NONE as valid results in these cases.
					checkIntEqualsAny(m_testCtx, state, GL_FRAMEBUFFER_DEFAULT, GL_NONE);
				}
			}
		}
	}

private:
	GLenum m_framebufferTarget;
};

class AttachmentObjectCase : public ApiCase
{
public:
	AttachmentObjectCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		GLuint framebufferID = 0;
		glGenFramebuffers(1, &framebufferID);
		glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
		expectError(GL_NO_ERROR);

		// initial
		{
			checkAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, GL_NONE);
			checkAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, 0);
			expectError(GL_NO_ERROR);
		}

		// texture
		{
			GLuint textureID = 0;
			glGenTextures(1, &textureID);
			glBindTexture(GL_TEXTURE_2D, textureID);
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			expectError(GL_NO_ERROR);

			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0);
			expectError(GL_NO_ERROR);

			checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, GL_TEXTURE);
			checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, textureID);

			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
			glDeleteTextures(1, &textureID);
		}

		// rb
		{
			GLuint renderbufferID = 0;
			glGenRenderbuffers(1, &renderbufferID);
			glBindRenderbuffer(GL_RENDERBUFFER, renderbufferID);
			glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB8, 128, 128);
			expectError(GL_NO_ERROR);

			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbufferID);
			expectError(GL_NO_ERROR);

			checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, GL_RENDERBUFFER);
			checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, renderbufferID);

			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0);
			glDeleteRenderbuffers(1, &renderbufferID);
		}

		glDeleteFramebuffers(1, &framebufferID);
		expectError(GL_NO_ERROR);
	}
};

class AttachmentTextureLevelCase : public ApiCase
{
public:
	AttachmentTextureLevelCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		GLuint framebufferID = 0;
		glGenFramebuffers(1, &framebufferID);
		glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
		expectError(GL_NO_ERROR);

		for (int mipmapLevel = 0; mipmapLevel < 7; ++mipmapLevel)
		{
			GLuint textureID = 0;
			glGenTextures(1, &textureID);
			glBindTexture(GL_TEXTURE_2D, textureID);
			glTexStorage2D(GL_TEXTURE_2D, 7, GL_RGB8, 128, 128);
			expectError(GL_NO_ERROR);

			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, mipmapLevel);
			expectError(GL_NO_ERROR);

			checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, mipmapLevel);

			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
			glDeleteTextures(1, &textureID);
		}

		glDeleteFramebuffers(1, &framebufferID);
		expectError(GL_NO_ERROR);
	}
};

class AttachmentTextureCubeMapFaceCase : public ApiCase
{
public:
	AttachmentTextureCubeMapFaceCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		GLuint framebufferID = 0;
		glGenFramebuffers(1, &framebufferID);
		glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
		expectError(GL_NO_ERROR);

		{
			GLuint textureID = 0;
			glGenTextures(1, &textureID);
			glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
			expectError(GL_NO_ERROR);

			glTexStorage2D(GL_TEXTURE_CUBE_MAP, 1, GL_RGB8, 128, 128);
			expectError(GL_NO_ERROR);

			const GLenum faces[] =
			{
				GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
				GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
				GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
			};

			for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(faces); ++ndx)
			{
				glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, faces[ndx], textureID, 0);
				checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE, faces[ndx]);
			}

			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
			glDeleteTextures(1, &textureID);
		}

		glDeleteFramebuffers(1, &framebufferID);
		expectError(GL_NO_ERROR);
	}
};

class AttachmentTextureLayerCase : public ApiCase
{
public:
	AttachmentTextureLayerCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		GLuint framebufferID = 0;
		glGenFramebuffers(1, &framebufferID);
		glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
		expectError(GL_NO_ERROR);

		// tex3d
		{
			GLuint textureID = 0;
			glGenTextures(1, &textureID);
			glBindTexture(GL_TEXTURE_3D, textureID);
			glTexStorage3D(GL_TEXTURE_3D, 1, GL_RGBA8, 16, 16, 16);

			for (int layer = 0; layer < 16; ++layer)
			{
				glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureID, 0, layer);
				checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER, layer);
			}

			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
			glDeleteTextures(1, &textureID);
		}
		// tex2d array
		{
			GLuint textureID = 0;
			glGenTextures(1, &textureID);
			glBindTexture(GL_TEXTURE_2D_ARRAY, textureID);
			glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, 16, 16, 16);

			for (int layer = 0; layer < 16; ++layer)
			{
				glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureID, 0, layer);
				checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER, layer);
			}

			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
			glDeleteTextures(1, &textureID);
		}

		glDeleteFramebuffers(1, &framebufferID);
		expectError(GL_NO_ERROR);
	}
};

class AttachmentTextureColorCodingCase : public ApiCase
{
public:
	AttachmentTextureColorCodingCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		GLuint framebufferID = 0;
		glGenFramebuffers(1, &framebufferID);
		glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
		expectError(GL_NO_ERROR);

		// rgb8 color
		{
			GLuint renderbufferID = 0;
			glGenRenderbuffers(1, &renderbufferID);
			glBindRenderbuffer(GL_RENDERBUFFER, renderbufferID);
			glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB8, 128, 128);
			expectError(GL_NO_ERROR);

			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbufferID);
			expectError(GL_NO_ERROR);

			checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, GL_LINEAR);

			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0);
			glDeleteRenderbuffers(1, &renderbufferID);
		}

		// srgb8_alpha8 color
		{
			GLuint renderbufferID = 0;
			glGenRenderbuffers(1, &renderbufferID);
			glBindRenderbuffer(GL_RENDERBUFFER, renderbufferID);
			glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8_ALPHA8, 128, 128);
			expectError(GL_NO_ERROR);

			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbufferID);
			expectError(GL_NO_ERROR);

			checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, GL_SRGB);

			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0);
			glDeleteRenderbuffers(1, &renderbufferID);
		}

		// depth
		{
			GLuint renderbufferID = 0;
			glGenRenderbuffers(1, &renderbufferID);
			glBindRenderbuffer(GL_RENDERBUFFER, renderbufferID);
			glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 128, 128);
			expectError(GL_NO_ERROR);

			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbufferID);
			expectError(GL_NO_ERROR);

			checkAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, GL_LINEAR);

			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0);
			glDeleteRenderbuffers(1, &renderbufferID);
		}

		glDeleteFramebuffers(1, &framebufferID);
		expectError(GL_NO_ERROR);
	}
};

class AttachmentTextureComponentTypeCase : public ApiCase
{
public:
	AttachmentTextureComponentTypeCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		GLuint framebufferID = 0;
		glGenFramebuffers(1, &framebufferID);
		glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
		expectError(GL_NO_ERROR);

		// color-renderable required texture formats
		const struct RequiredColorFormat
		{
			GLenum internalFormat;
			GLenum componentType;
		} requiredColorformats[] =
		{
			{ GL_R8,			GL_UNSIGNED_NORMALIZED	},
			{ GL_RG8,			GL_UNSIGNED_NORMALIZED	},
			{ GL_RGB8,			GL_UNSIGNED_NORMALIZED	},
			{ GL_RGB565,		GL_UNSIGNED_NORMALIZED	},
			{ GL_RGBA4,			GL_UNSIGNED_NORMALIZED	},
			{ GL_RGB5_A1,		GL_UNSIGNED_NORMALIZED	},
			{ GL_RGBA8,			GL_UNSIGNED_NORMALIZED	},
			{ GL_RGB10_A2,		GL_UNSIGNED_NORMALIZED	},
			{ GL_RGB10_A2UI,	GL_UNSIGNED_INT			},
			{ GL_SRGB8_ALPHA8,	GL_UNSIGNED_NORMALIZED	},
			{ GL_R8I,			GL_INT					},
			{ GL_R8UI,			GL_UNSIGNED_INT			},
			{ GL_R16I,			GL_INT					},
			{ GL_R16UI,			GL_UNSIGNED_INT			},
			{ GL_R32I,			GL_INT					},
			{ GL_R32UI,			GL_UNSIGNED_INT			},
			{ GL_RG8I,			GL_INT					},
			{ GL_RG8UI,			GL_UNSIGNED_INT			},
			{ GL_RG16I,			GL_INT					},
			{ GL_RG16UI,		GL_UNSIGNED_INT			},
			{ GL_RG32I,			GL_INT					},
			{ GL_RG32UI,		GL_UNSIGNED_INT			},
			{ GL_RGBA8I,		GL_INT					},
			{ GL_RGBA8UI,		GL_UNSIGNED_INT			},
			{ GL_RGBA16I,		GL_INT					},
			{ GL_RGBA16UI,		GL_UNSIGNED_INT			},
			{ GL_RGBA32I,		GL_INT					},
			{ GL_RGBA32UI,		GL_UNSIGNED_INT			}
		};

		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(requiredColorformats); ++ndx)
		{
			const GLenum colorFormat = requiredColorformats[ndx].internalFormat;

			GLuint textureID = 0;
			glGenTextures(1, &textureID);
			glBindTexture(GL_TEXTURE_2D, textureID);
			glTexStorage2D(GL_TEXTURE_2D, 1, colorFormat, 128, 128);
			expectError(GL_NO_ERROR);

			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0);
			expectError(GL_NO_ERROR);

			checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE, requiredColorformats[ndx].componentType);

			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
			glDeleteTextures(1, &textureID);
		}

		glDeleteFramebuffers(1, &framebufferID);
		expectError(GL_NO_ERROR);
	}
};

class AttachmentSizeInitialCase : public ApiCase
{
public:
	AttachmentSizeInitialCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		// check default
		if (attachmentExists(GL_BACK))
		{
			checkAttachmentComponentSizeAtLeast(
				m_testCtx,
				*this,
				GL_FRAMEBUFFER,
				GL_BACK,
				m_context.getRenderTarget().getPixelFormat().redBits,
				m_context.getRenderTarget().getPixelFormat().greenBits,
				m_context.getRenderTarget().getPixelFormat().blueBits,
				m_context.getRenderTarget().getPixelFormat().alphaBits,
				-1,
				-1);
			expectError(GL_NO_ERROR);
		}

		if (attachmentExists(GL_DEPTH))
		{
			checkAttachmentComponentSizeAtLeast(
				m_testCtx,
				*this,
				GL_FRAMEBUFFER,
				GL_DEPTH,
				-1,
				-1,
				-1,
				-1,
				m_context.getRenderTarget().getDepthBits(),
				-1);
			expectError(GL_NO_ERROR);
		}

		if (attachmentExists(GL_STENCIL))
		{
			checkAttachmentComponentSizeAtLeast(
				m_testCtx,
				*this,
				GL_FRAMEBUFFER,
				GL_STENCIL,
				-1,
				-1,
				-1,
				-1,
				-1,
				m_context.getRenderTarget().getStencilBits());
			expectError(GL_NO_ERROR);
		}
	}

	bool attachmentExists (GLenum attachment)
	{
		StateQueryMemoryWriteGuard<GLint> state;
		glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, attachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &state);
		expectError(GL_NO_ERROR);

		return !state.isUndefined() && state != GL_NONE;
	}
};

class AttachmentSizeCase : public ApiCase
{
public:
	AttachmentSizeCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	virtual void testColorAttachment (GLenum internalFormat, GLenum attachment, GLint bitsR, GLint bitsG, GLint bitsB, GLint bitsA) = DE_NULL;

	virtual void testDepthAttachment (GLenum internalFormat, GLenum attachment, GLint bitsD, GLint bitsS) = DE_NULL;

	void test (void)
	{
		GLuint framebufferID = 0;
		glGenFramebuffers(1, &framebufferID);
		glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
		expectError(GL_NO_ERROR);

		// check some color targets

		const struct ColorAttachment
		{
			GLenum	internalFormat;
			int		bitsR, bitsG, bitsB, bitsA;
		} colorAttachments[] =
		{
			{ GL_RGBA8,		8,	8,	8,	8 },
			{ GL_RGB565,	5,	6,	5,	0 },
			{ GL_RGBA4,		4,	4,	4,	4 },
			{ GL_RGB5_A1,	5,	5,	5,	1 },
			{ GL_RGBA8I,	8,	8,	8,	8 },
			{ GL_RG32UI,	32,	32,	0,	0 }
		};
		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(colorAttachments); ++ndx)
			testColorAttachment(colorAttachments[ndx].internalFormat, GL_COLOR_ATTACHMENT0, colorAttachments[ndx].bitsR, colorAttachments[ndx].bitsG, colorAttachments[ndx].bitsB, colorAttachments[ndx].bitsA);

		// check some depth targets

		const struct DepthAttachment
		{
			GLenum	internalFormat;
			GLenum	attachment;
			int		dbits;
			int		sbits;
		} depthAttachments[] =
		{
			{ GL_DEPTH_COMPONENT16,		GL_DEPTH_ATTACHMENT,			16, 0 },
			{ GL_DEPTH_COMPONENT24,		GL_DEPTH_ATTACHMENT,			24, 0 },
			{ GL_DEPTH_COMPONENT32F,	GL_DEPTH_ATTACHMENT,			32, 0 },
			{ GL_DEPTH24_STENCIL8,		GL_DEPTH_STENCIL_ATTACHMENT,	24, 8 },
			{ GL_DEPTH32F_STENCIL8,		GL_DEPTH_STENCIL_ATTACHMENT,	32, 8 },
		};
		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(depthAttachments); ++ndx)
			testDepthAttachment(depthAttachments[ndx].internalFormat, depthAttachments[ndx].attachment, depthAttachments[ndx].dbits, depthAttachments[ndx].sbits);

		glDeleteFramebuffers(1, &framebufferID);
		expectError(GL_NO_ERROR);
	}
};

class AttachmentSizeRboCase : public AttachmentSizeCase
{
public:
	AttachmentSizeRboCase (Context& context, const char* name, const char* description)
		: AttachmentSizeCase(context, name, description)
	{
	}

	void testColorAttachment (GLenum internalFormat, GLenum attachment, GLint bitsR, GLint bitsG, GLint bitsB, GLint bitsA)
	{
		GLuint renderbufferID = 0;
		glGenRenderbuffers(1, &renderbufferID);
		glBindRenderbuffer(GL_RENDERBUFFER, renderbufferID);
		glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, 128, 128);
		expectError(GL_NO_ERROR);

		glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderbufferID);
		expectError(GL_NO_ERROR);

		checkAttachmentComponentSizeAtLeast	(m_testCtx, *this, GL_FRAMEBUFFER, attachment, bitsR, bitsG, bitsB, bitsA, -1, -1);
		checkAttachmentComponentSizeExactly	(m_testCtx, *this, GL_FRAMEBUFFER, attachment, -1, -1, -1, -1, 0, 0);

		glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, 0);
		glDeleteRenderbuffers(1, &renderbufferID);
	}

	void testDepthAttachment (GLenum internalFormat, GLenum attachment, GLint bitsD, GLint bitsS)
	{
		GLuint renderbufferID = 0;
		glGenRenderbuffers(1, &renderbufferID);
		glBindRenderbuffer(GL_RENDERBUFFER, renderbufferID);
		glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, 128, 128);
		expectError(GL_NO_ERROR);

		glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderbufferID);
		expectError(GL_NO_ERROR);

		checkAttachmentComponentSizeAtLeast	(m_testCtx, *this, GL_FRAMEBUFFER, attachment, -1, -1, -1, -1, bitsD, bitsS);
		checkAttachmentComponentSizeExactly	(m_testCtx, *this, GL_FRAMEBUFFER, attachment, 0, 0, 0, 0, -1, -1);

		glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, 0);
		glDeleteRenderbuffers(1, &renderbufferID);
	}
};

class AttachmentSizeTextureCase : public AttachmentSizeCase
{
public:
	AttachmentSizeTextureCase (Context& context, const char* name, const char* description)
		: AttachmentSizeCase(context, name, description)
	{
	}

	void testColorAttachment (GLenum internalFormat, GLenum attachment, GLint bitsR, GLint bitsG, GLint bitsB, GLint bitsA)
	{
		GLuint textureID = 0;
		glGenTextures(1, &textureID);
		glBindTexture(GL_TEXTURE_2D, textureID);
		glTexStorage2D(GL_TEXTURE_2D, 1, internalFormat, 128, 128);
		expectError(GL_NO_ERROR);

		glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, textureID, 0);
		expectError(GL_NO_ERROR);

		checkAttachmentComponentSizeAtLeast	(m_testCtx, *this, GL_FRAMEBUFFER, attachment, bitsR, bitsG, bitsB, bitsA, -1, -1);
		checkAttachmentComponentSizeExactly	(m_testCtx, *this, GL_FRAMEBUFFER, attachment, -1, -1, -1, -1, 0, 0);

		glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, 0, 0);
		glDeleteTextures(1, &textureID);
	}

	void testDepthAttachment (GLenum internalFormat, GLenum attachment, GLint bitsD, GLint bitsS)
	{
		// don't test stencil formats with textures
		if (attachment == GL_DEPTH_STENCIL_ATTACHMENT)
			return;

		GLuint textureID = 0;
		glGenTextures(1, &textureID);
		glBindTexture(GL_TEXTURE_2D, textureID);
		glTexStorage2D(GL_TEXTURE_2D, 1, internalFormat, 128, 128);
		expectError(GL_NO_ERROR);

		glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, textureID, 0);
		expectError(GL_NO_ERROR);

		checkAttachmentComponentSizeAtLeast	(m_testCtx, *this, GL_FRAMEBUFFER, attachment, -1, -1, -1, -1, bitsD, bitsS);
		checkAttachmentComponentSizeExactly	(m_testCtx, *this, GL_FRAMEBUFFER, attachment, 0, 0, 0, 0, -1, -1);

		glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, 0, 0);
		glDeleteTextures(1, &textureID);
	}
};

class UnspecifiedAttachmentTextureColorCodingCase : public ApiCase
{
public:
	UnspecifiedAttachmentTextureColorCodingCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		GLuint framebufferID = 0;
		glGenFramebuffers(1, &framebufferID);
		glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
		expectError(GL_NO_ERROR);

		// color
		{
			GLuint renderbufferID = 0;
			glGenRenderbuffers(1, &renderbufferID);
			glBindRenderbuffer(GL_RENDERBUFFER, renderbufferID);
			expectError(GL_NO_ERROR);

			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbufferID);
			expectError(GL_NO_ERROR);

			checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, GL_LINEAR);
			expectError(GL_NO_ERROR);

			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0);
			glDeleteRenderbuffers(1, &renderbufferID);
		}

		// depth
		{
			GLuint renderbufferID = 0;
			glGenRenderbuffers(1, &renderbufferID);
			glBindRenderbuffer(GL_RENDERBUFFER, renderbufferID);
			expectError(GL_NO_ERROR);

			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbufferID);
			expectError(GL_NO_ERROR);

			checkAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, GL_LINEAR);

			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0);
			glDeleteRenderbuffers(1, &renderbufferID);
		}

		glDeleteFramebuffers(1, &framebufferID);
		expectError(GL_NO_ERROR);
	}
};

class UnspecifiedAttachmentTextureComponentTypeCase : public ApiCase
{
public:
	UnspecifiedAttachmentTextureComponentTypeCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		GLuint framebufferID = 0;
		glGenFramebuffers(1, &framebufferID);
		glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
		expectError(GL_NO_ERROR);

		{
			GLuint textureID = 0;
			glGenTextures(1, &textureID);
			glBindTexture(GL_TEXTURE_2D, textureID);
			expectError(GL_NO_ERROR);

			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0);
			expectError(GL_NO_ERROR);

			checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE, GL_NONE);
			expectError(GL_NO_ERROR);

			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
			glDeleteTextures(1, &textureID);
		}

		glDeleteFramebuffers(1, &framebufferID);
		expectError(GL_NO_ERROR);
	}
};

class UnspecifiedAttachmentSizeCase : public ApiCase
{
public:
	UnspecifiedAttachmentSizeCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	virtual void testColorAttachment (void) = DE_NULL;

	virtual void testDepthAttachment (void) = DE_NULL;

	void test (void)
	{
		GLuint framebufferID = 0;
		glGenFramebuffers(1, &framebufferID);
		glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
		expectError(GL_NO_ERROR);

		// check color target
		testColorAttachment();

		// check depth target
		testDepthAttachment();

		glDeleteFramebuffers(1, &framebufferID);
		expectError(GL_NO_ERROR);
	}
};

class UnspecifiedAttachmentSizeRboCase : public UnspecifiedAttachmentSizeCase
{
public:
	UnspecifiedAttachmentSizeRboCase (Context& context, const char* name, const char* description)
		: UnspecifiedAttachmentSizeCase(context, name, description)
	{
	}

	void testColorAttachment (void)
	{
		GLuint renderbufferID = 0;
		glGenRenderbuffers(1, &renderbufferID);
		glBindRenderbuffer(GL_RENDERBUFFER, renderbufferID);
		expectError(GL_NO_ERROR);

		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbufferID);
		expectError(GL_NO_ERROR);

		checkAttachmentComponentSizeExactly	(m_testCtx, *this, GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 0, 0, 0, 0, 0, 0);
		expectError(GL_NO_ERROR);

		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0);
		glDeleteRenderbuffers(1, &renderbufferID);
	}

	void testDepthAttachment (void)
	{
		GLuint renderbufferID = 0;
		glGenRenderbuffers(1, &renderbufferID);
		glBindRenderbuffer(GL_RENDERBUFFER, renderbufferID);
		expectError(GL_NO_ERROR);

		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbufferID);
		expectError(GL_NO_ERROR);

		checkAttachmentComponentSizeExactly	(m_testCtx, *this, GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, 0, 0, 0, 0, 0, 0);
		expectError(GL_NO_ERROR);

		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
		glDeleteRenderbuffers(1, &renderbufferID);
	}
};

class UnspecifiedAttachmentSizeTextureCase : public UnspecifiedAttachmentSizeCase
{
public:
	UnspecifiedAttachmentSizeTextureCase (Context& context, const char* name, const char* description)
		: UnspecifiedAttachmentSizeCase(context, name, description)
	{
	}

	void testColorAttachment (void)
	{
		GLuint textureID = 0;
		glGenTextures(1, &textureID);
		glBindTexture(GL_TEXTURE_2D, textureID);
		expectError(GL_NO_ERROR);

		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0);
		expectError(GL_NO_ERROR);

		checkAttachmentComponentSizeExactly	(m_testCtx, *this, GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 0, 0, 0, 0, 0, 0);
		expectError(GL_NO_ERROR);

		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
		glDeleteTextures(1, &textureID);
	}

	void testDepthAttachment (void)
	{
		GLuint textureID = 0;
		glGenTextures(1, &textureID);
		glBindTexture(GL_TEXTURE_2D, textureID);
		expectError(GL_NO_ERROR);

		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textureID, 0);
		expectError(GL_NO_ERROR);

		checkAttachmentComponentSizeExactly	(m_testCtx, *this, GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, 0, 0, 0, 0, 0, 0);
		expectError(GL_NO_ERROR);

		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
		glDeleteTextures(1, &textureID);
	}
};

} // anonymous


FboStateQueryTests::FboStateQueryTests (Context& context)
	: TestCaseGroup(context, "fbo", "Fbo State Query tests")
{
}

void FboStateQueryTests::init (void)
{
	const struct FramebufferTarget
	{
		const char*	name;
		GLenum		target;
	} fboTargets[] =
	{
		{ "draw_framebuffer_", GL_DRAW_FRAMEBUFFER },
		{ "read_framebuffer_", GL_READ_FRAMEBUFFER }
	};

	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(fboTargets); ++ndx)
		addChild(new DefaultFramebufferCase(m_context, (std::string(fboTargets[ndx].name) + "default_framebuffer").c_str(), "default framebuffer", fboTargets[ndx].target));

	addChild(new AttachmentObjectCase							(m_context, "framebuffer_attachment_object",							"FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE and FRAMEBUFFER_ATTACHMENT_OBJECT_NAME"));
	addChild(new AttachmentTextureLevelCase						(m_context, "framebuffer_attachment_texture_level",						"FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL"));
	addChild(new AttachmentTextureCubeMapFaceCase				(m_context, "framebuffer_attachment_texture_cube_map_face",				"FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE"));
	addChild(new AttachmentTextureLayerCase						(m_context, "framebuffer_attachment_texture_layer",						"FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER"));
	addChild(new AttachmentTextureColorCodingCase				(m_context, "framebuffer_attachment_color_encoding",					"FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING"));
	addChild(new AttachmentTextureComponentTypeCase				(m_context, "framebuffer_attachment_component_type",					"FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE"));
	addChild(new AttachmentSizeInitialCase						(m_context, "framebuffer_attachment_x_size_initial",					"FRAMEBUFFER_ATTACHMENT_x_SIZE"));
	addChild(new AttachmentSizeRboCase							(m_context, "framebuffer_attachment_x_size_rbo",						"FRAMEBUFFER_ATTACHMENT_x_SIZE"));
	addChild(new AttachmentSizeTextureCase						(m_context, "framebuffer_attachment_x_size_texture",					"FRAMEBUFFER_ATTACHMENT_x_SIZE"));

	addChild(new UnspecifiedAttachmentTextureColorCodingCase	(m_context, "framebuffer_unspecified_attachment_color_encoding",		"FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING"));
	addChild(new UnspecifiedAttachmentTextureComponentTypeCase	(m_context, "framebuffer_unspecified_attachment_component_type",		"FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE"));
	addChild(new UnspecifiedAttachmentSizeRboCase				(m_context, "framebuffer_unspecified_attachment_x_size_rbo",			"FRAMEBUFFER_ATTACHMENT_x_SIZE"));
	addChild(new UnspecifiedAttachmentSizeTextureCase			(m_context, "framebuffer_unspecified_attachment_x_size_texture",		"FRAMEBUFFER_ATTACHMENT_x_SIZE"));
}

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