/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES Utilities
 * ------------------------------------------------
 *
 * 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 OpenGL State Reset.
 *//*--------------------------------------------------------------------*/

#include "gluStateReset.hpp"
#include "gluContextInfo.hpp"
#include "gluRenderContext.hpp"
#include "tcuRenderTarget.hpp"
#include "glwFunctions.hpp"
#include "glwEnums.hpp"
#include "deUniquePtr.hpp"

namespace glu
{
namespace
{
enum
{
	MAX_ERROR_COUNT = 10
};

void resetErrors (const glw::Functions& gl)
{
	size_t errorNdx = 0;

	for (errorNdx = 0; errorNdx < MAX_ERROR_COUNT; errorNdx++)
	{
		if (gl.getError() == GL_NONE)
			break;
	}

	if (errorNdx == MAX_ERROR_COUNT)
		TCU_FAIL("Couldn't reset error state");
}

}

void resetStateES (const RenderContext& renderCtx, const ContextInfo& ctxInfo)
{
	const glw::Functions&	gl	= renderCtx.getFunctions();
	const ContextType		type	= renderCtx.getType();

	// Reset error state
	resetErrors(gl);

	DE_ASSERT(isContextTypeES(type));

	// Vertex attrib array state.
	{
		int numVertexAttribArrays = 0;
		gl.getIntegerv(GL_MAX_VERTEX_ATTRIBS, &numVertexAttribArrays);

		gl.bindBuffer	(GL_ARRAY_BUFFER,			0);
		gl.bindBuffer	(GL_ELEMENT_ARRAY_BUFFER,	0);

		if (contextSupports(type, ApiType::es(3,0)))
		{
			gl.bindVertexArray	(0);
			gl.disable			(GL_PRIMITIVE_RESTART_FIXED_INDEX);
		}

		if (contextSupports(type, ApiType::es(3,1)))
			gl.bindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);

		for (int ndx = 0; ndx < numVertexAttribArrays; ndx++)
		{
			gl.disableVertexAttribArray	(ndx);
			gl.vertexAttribPointer		(ndx, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);

			if (contextSupports(type, ApiType::es(3,0)))
				gl.vertexAttribDivisor(ndx, 0);
		}

		GLU_EXPECT_NO_ERROR(gl.getError(), "Vertex attrib array state reset failed");
	}

	// Transformation state.
	{
		const tcu::RenderTarget& renderTarget = renderCtx.getRenderTarget();

		gl.viewport		(0, 0, renderTarget.getWidth(), renderTarget.getHeight());
		gl.depthRangef	(0.0f, 1.0f);

		if (contextSupports(type, ApiType::es(3,0)))
			gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Transformation state reset failed");
	}

	// Rasterization state
	{
		gl.lineWidth	(1.0f);
		gl.disable		(GL_CULL_FACE);
		gl.cullFace		(GL_BACK);
		gl.frontFace	(GL_CCW);
		gl.polygonOffset(0.0f, 0.0f);
		gl.disable		(GL_POLYGON_OFFSET_FILL);

		if (contextSupports(type, ApiType::es(3,0)))
			gl.disable(GL_RASTERIZER_DISCARD);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Rasterization state reset failed");
	}

	// Multisampling state
	{
		gl.disable			(GL_SAMPLE_ALPHA_TO_COVERAGE);
		gl.disable			(GL_SAMPLE_COVERAGE);
		gl.sampleCoverage	(1.0f, GL_FALSE);

		if (contextSupports(type, ApiType::es(3,1)))
		{
			int numSampleMaskWords = 0;
			gl.getIntegerv(GL_MAX_SAMPLE_MASK_WORDS, &numSampleMaskWords);

			gl.disable(GL_SAMPLE_MASK);

			for (int ndx = 0; ndx < numSampleMaskWords; ndx++)
				gl.sampleMaski(ndx, ~0u);
		}

		GLU_EXPECT_NO_ERROR(gl.getError(), "Multisampling state reset failed");
	}

	// Texture state.
	// \todo [2013-04-08 pyry] Reset all levels?
	{
		const float	borderColor[]		= { 0.0f, 0.0f, 0.0f, 0.0f };
		int			numTexUnits			= 0;
		const bool	supportsBorderClamp	= ctxInfo.isExtensionSupported("GL_EXT_texture_border_clamp") || contextSupports(type, ApiType::es(3,2));

		gl.getIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &numTexUnits);

		for (int ndx = 0; ndx < numTexUnits; ndx++)
		{
			gl.activeTexture(GL_TEXTURE0 + ndx);

			// Reset 2D texture.
			gl.bindTexture(GL_TEXTURE_2D, 0);
			gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,		GL_NEAREST_MIPMAP_LINEAR);
			gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,		GL_LINEAR);
			gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,			GL_REPEAT);
			gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,			GL_REPEAT);

			if (contextSupports(type, ApiType::es(3,0)))
			{
				gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R,		GL_RED);
				gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G,		GL_GREEN);
				gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B,		GL_BLUE);
				gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A,		GL_ALPHA);
				gl.texParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD,			-1000.0f);
				gl.texParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD,			1000.0f);
				gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL,		0);
				gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL,		1000);
				gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE,	GL_NONE);
				gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC,	GL_LEQUAL);
			}

			if (contextSupports(type, ApiType::es(3,1)))
				gl.texParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);

			if (supportsBorderClamp)
				gl.texParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, &borderColor[0]);

			// Reset cube map texture.
			gl.bindTexture(GL_TEXTURE_CUBE_MAP, 0);
			gl.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,	GL_NEAREST_MIPMAP_LINEAR);
			gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER,	GL_LINEAR);
			gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S,		GL_REPEAT);
			gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T,		GL_REPEAT);

			if (contextSupports(type, ApiType::es(3,0)))
			{
				gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_R,		GL_RED);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_G,		GL_GREEN);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_B,		GL_BLUE);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_A,		GL_ALPHA);
				gl.texParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_LOD,		-1000.0f);
				gl.texParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LOD,		1000.0f);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL,	0);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL,		1000);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE,	GL_NONE);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC,	GL_LEQUAL);
			}

			if (contextSupports(type, ApiType::es(3,1)))
				gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);

			if (supportsBorderClamp)
				gl.texParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, &borderColor[0]);

			if (contextSupports(type, ApiType::es(3,0)))
			{
				// Reset 2D array texture.
				gl.bindTexture(GL_TEXTURE_2D_ARRAY, 0);
				gl.texImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 0, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
				gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER,	GL_NEAREST_MIPMAP_LINEAR);
				gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER,	GL_LINEAR);
				gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S,		GL_REPEAT);
				gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T,		GL_REPEAT);
				gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_SWIZZLE_R,		GL_RED);
				gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_SWIZZLE_G,		GL_GREEN);
				gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_SWIZZLE_B,		GL_BLUE);
				gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_SWIZZLE_A,		GL_ALPHA);
				gl.texParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_LOD,		-1000.0f);
				gl.texParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LOD,		1000.0f);
				gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BASE_LEVEL,	0);
				gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL,		1000);
				gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE,	GL_NONE);
				gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_FUNC,	GL_LEQUAL);

				if (supportsBorderClamp)
					gl.texParameterfv(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BORDER_COLOR, &borderColor[0]);
			}

			if (contextSupports(type, ApiType::es(3,1)))
				gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);

			if (contextSupports(type, ApiType::es(3,0)))
			{
				// Reset 3D texture.
				gl.bindTexture(GL_TEXTURE_3D, 0);
				gl.texImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 0, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
				gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER,		GL_NEAREST_MIPMAP_LINEAR);
				gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER,		GL_LINEAR);
				gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S,			GL_REPEAT);
				gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T,			GL_REPEAT);
				gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R,			GL_REPEAT);
				gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_R,		GL_RED);
				gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_G,		GL_GREEN);
				gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_B,		GL_BLUE);
				gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_A,		GL_ALPHA);
				gl.texParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_LOD,			-1000.0f);
				gl.texParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAX_LOD,			1000.0f);
				gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_BASE_LEVEL,		0);
				gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL,		1000);
				gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_COMPARE_MODE,	GL_NONE);
				gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_COMPARE_FUNC,	GL_LEQUAL);

				if (supportsBorderClamp)
					gl.texParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, &borderColor[0]);
			}

			if (contextSupports(type, ApiType::es(3,1)))
				gl.texParameteri(GL_TEXTURE_3D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);

			if (contextSupports(type, ApiType::es(3,1)))
			{
				// Reset multisample textures.
				gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
				gl.texParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_SWIZZLE_R,	GL_RED);
				gl.texParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_SWIZZLE_G,	GL_GREEN);
				gl.texParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_SWIZZLE_B,	GL_BLUE);
				gl.texParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_SWIZZLE_A,	GL_ALPHA);
				gl.texParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_BASE_LEVEL,	0);
				gl.texParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MAX_LEVEL,	1000);
			}

			if (ctxInfo.isExtensionSupported("GL_OES_texture_storage_multisample_2d_array"))
			{
				gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 0);
				gl.texParameteri(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, GL_TEXTURE_SWIZZLE_R,		GL_RED);
				gl.texParameteri(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, GL_TEXTURE_SWIZZLE_G,		GL_GREEN);
				gl.texParameteri(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, GL_TEXTURE_SWIZZLE_B,		GL_BLUE);
				gl.texParameteri(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, GL_TEXTURE_SWIZZLE_A,		GL_ALPHA);
				gl.texParameteri(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, GL_TEXTURE_BASE_LEVEL,	0);
				gl.texParameteri(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, GL_TEXTURE_MAX_LEVEL,		1000);
			}

			if (ctxInfo.isExtensionSupported("GL_EXT_texture_cube_map_array"))
			{
				// Reset cube array texture.
				gl.bindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, 0);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER,		GL_NEAREST_MIPMAP_LINEAR);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER,		GL_LINEAR);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S,			GL_REPEAT);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T,			GL_REPEAT);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_SWIZZLE_R,		GL_RED);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_SWIZZLE_G,		GL_GREEN);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_SWIZZLE_B,		GL_BLUE);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_SWIZZLE_A,		GL_ALPHA);
				gl.texParameterf(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_LOD,			-1000.0f);
				gl.texParameterf(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAX_LOD,			1000.0f);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_BASE_LEVEL,		0);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAX_LEVEL,		1000);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_COMPARE_MODE,	GL_NONE);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_COMPARE_FUNC,	GL_LEQUAL);

				if (supportsBorderClamp)
					gl.texParameterfv(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_BORDER_COLOR, &borderColor[0]);
			}
		}

		gl.activeTexture(GL_TEXTURE0);

		if (contextSupports(type, ApiType::es(3,0)))
		{
			for (int ndx = 0; ndx < numTexUnits; ndx++)
				gl.bindSampler(ndx, 0);
		}

		GLU_EXPECT_NO_ERROR(gl.getError(), "Texture state reset failed");
	}

	// Resetting state using non-indexed variants should be enough, but some
	// implementations have bugs so we need to make sure indexed state gets
	// set back to initial values.
	if (ctxInfo.isExtensionSupported("GL_EXT_draw_buffers_indexed"))
	{
		int numDrawBuffers = 0;

		gl.getIntegerv(GL_MAX_DRAW_BUFFERS, &numDrawBuffers);

		for (int drawBufferNdx = 0; drawBufferNdx < numDrawBuffers; drawBufferNdx++)
		{
			gl.disablei			(GL_BLEND, drawBufferNdx);
			gl.blendFunci		(drawBufferNdx, GL_ONE, GL_ZERO);
			gl.blendEquationi	(drawBufferNdx, GL_FUNC_ADD);
			gl.colorMaski		(drawBufferNdx, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
		}

		GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to reset indexed draw buffer state");
	}

	// Pixel operations.
	{
		const tcu::RenderTarget& renderTarget = renderCtx.getRenderTarget();

		gl.disable		(GL_SCISSOR_TEST);
		gl.scissor		(0, 0, renderTarget.getWidth(), renderTarget.getHeight());

		gl.disable		(GL_STENCIL_TEST);
		gl.stencilFunc	(GL_ALWAYS, 0, ~0u);
		gl.stencilOp	(GL_KEEP, GL_KEEP, GL_KEEP);

		gl.disable		(GL_DEPTH_TEST);
		gl.depthFunc	(GL_LESS);

		gl.disable		(GL_BLEND);
		gl.blendFunc	(GL_ONE, GL_ZERO);
		gl.blendEquation(GL_FUNC_ADD);
		gl.blendColor	(0.0f, 0.0f, 0.0f, 0.0f);

		gl.enable		(GL_DITHER);

		if (ctxInfo.isExtensionSupported("GL_EXT_sRGB_write_control"))
		{
			gl.enable		(GL_FRAMEBUFFER_SRGB);
		}

		GLU_EXPECT_NO_ERROR(gl.getError(), "Pixel operation state reset failed");
	}

	// Framebuffer control.
	{
		gl.colorMask		(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
		gl.depthMask		(GL_TRUE);
		gl.stencilMask		(~0u);

		gl.clearColor		(0.0f, 0.0f, 0.0f, 0.0f);
		gl.clearDepthf		(1.0f);
		gl.clearStencil		(0);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Framebuffer control state reset failed");
	}

	// Framebuffer state.
	{
		// \note Actually spec explictly says 0 but on some platforms (iOS) no default framebuffer exists.
		const deUint32		defaultFbo		= renderCtx.getDefaultFramebuffer();
		const deUint32		drawBuffer		= defaultFbo != 0 ? GL_COLOR_ATTACHMENT0 : GL_BACK;
		const deUint32		readBuffer		= defaultFbo != 0 ? GL_COLOR_ATTACHMENT0 : GL_BACK;

		gl.bindFramebuffer(GL_FRAMEBUFFER, defaultFbo);

		if (contextSupports(type, ApiType::es(3,0)))
		{
			gl.drawBuffers	(1, &drawBuffer);
			gl.readBuffer	(readBuffer);
		}

		if (contextSupports(type, ApiType::es(3, 1)) && defaultFbo != 0)
		{
			gl.framebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH,					0);
			gl.framebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT,					0);
			gl.framebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_SAMPLES,				0);
			gl.framebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS,	GL_FALSE);
			GLU_EXPECT_NO_ERROR(gl.getError(), "Framebuffer default state reset failed");
		}

		GLU_EXPECT_NO_ERROR(gl.getError(), "Framebuffer state reset failed");
	}

	// Renderbuffer state.
	{
		gl.bindRenderbuffer(GL_RENDERBUFFER, 0);
		GLU_EXPECT_NO_ERROR(gl.getError(), "Renderbuffer state reset failed");
	}

	// Pixel transfer state.
	{
		gl.pixelStorei(GL_UNPACK_ALIGNMENT,		4);
		gl.pixelStorei(GL_PACK_ALIGNMENT,		4);

		if (contextSupports(type, ApiType::es(3,0)))
		{
			gl.pixelStorei(GL_UNPACK_IMAGE_HEIGHT,	0);
			gl.pixelStorei(GL_UNPACK_SKIP_IMAGES,	0);
			gl.pixelStorei(GL_UNPACK_ROW_LENGTH,	0);
			gl.pixelStorei(GL_UNPACK_SKIP_ROWS,		0);
			gl.pixelStorei(GL_UNPACK_SKIP_PIXELS,	0);

			gl.pixelStorei(GL_PACK_ROW_LENGTH,		0);
			gl.pixelStorei(GL_PACK_SKIP_ROWS,		0);
			gl.pixelStorei(GL_PACK_SKIP_PIXELS,		0);

			gl.bindBuffer(GL_PIXEL_PACK_BUFFER,		0);
			gl.bindBuffer(GL_PIXEL_UNPACK_BUFFER,	0);
		}

		GLU_EXPECT_NO_ERROR(gl.getError(), "Pixel transfer state reset failed");
	}

	// Program object state.
	{
		gl.useProgram(0);

		if (contextSupports(type, ApiType::es(3,0)))
		{
			int maxUniformBufferBindings = 0;
			gl.getIntegerv	(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings);
			gl.bindBuffer	(GL_UNIFORM_BUFFER,	0);

			for (int ndx = 0; ndx < maxUniformBufferBindings; ndx++)
				gl.bindBufferBase(GL_UNIFORM_BUFFER, ndx, 0);
		}

		if (contextSupports(type, ApiType::es(3,1)))
		{
			gl.bindProgramPipeline(0);

			{
				int maxAtomicCounterBufferBindings = 0;
				gl.getIntegerv	(GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, &maxAtomicCounterBufferBindings);
				gl.bindBuffer	(GL_ATOMIC_COUNTER_BUFFER, 0);

				for (int ndx = 0; ndx < maxAtomicCounterBufferBindings; ndx++)
					gl.bindBufferBase(GL_ATOMIC_COUNTER_BUFFER, ndx, 0);
			}

			{
				int maxShaderStorageBufferBindings = 0;
				gl.getIntegerv	(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &maxShaderStorageBufferBindings);
				gl.bindBuffer	(GL_SHADER_STORAGE_BUFFER, 0);

				for (int ndx = 0; ndx < maxShaderStorageBufferBindings; ndx++)
					gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, ndx, 0);
			}
		}

		GLU_EXPECT_NO_ERROR(gl.getError(), "Program object state reset failed");
	}

	// Vertex shader state.
	{
		int numVertexAttribArrays = 0;
		gl.getIntegerv(GL_MAX_VERTEX_ATTRIBS, &numVertexAttribArrays);

		for (int ndx = 0; ndx < numVertexAttribArrays; ndx++)
			gl.vertexAttrib4f(ndx, 0.0f, 0.0f, 0.0f, 1.0f);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Vertex shader state reset failed");
	}

	// Transform feedback state.
	if (contextSupports(type, ApiType::es(3,0)))
	{
		int				numTransformFeedbackSeparateAttribs	= 0;
		glw::GLboolean	transformFeedbackActive				= 0;
		gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,	&numTransformFeedbackSeparateAttribs);
		gl.getBooleanv(GL_TRANSFORM_FEEDBACK_ACTIVE,				&transformFeedbackActive);

		if (transformFeedbackActive)
			gl.endTransformFeedback();

		gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);

		for (int ndx = 0; ndx < numTransformFeedbackSeparateAttribs; ndx++)
			gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, ndx, 0);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Transform feedback state reset failed");
	}

	// Asynchronous query state.
	if (contextSupports(type, ApiType::es(3,0)))
	{
		static const deUint32 targets[] = { GL_ANY_SAMPLES_PASSED, GL_ANY_SAMPLES_PASSED_CONSERVATIVE, GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN };

		for (int i = 0; i < DE_LENGTH_OF_ARRAY(targets); i++)
		{
			int queryActive = 0;
			gl.getQueryiv(targets[i], GL_CURRENT_QUERY, &queryActive);

			if (queryActive != 0)
				gl.endQuery(targets[i]);
		}

		GLU_EXPECT_NO_ERROR(gl.getError(), "Asynchronous query state reset failed");
	}

	// Hints.
	{
		gl.hint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE);

		if (contextSupports(type, ApiType::es(3,0)))
			gl.hint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_DONT_CARE);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Hints reset failed");
	}

	// Compute.
	if (contextSupports(type, ApiType::es(3,1)))
	{
		gl.bindBuffer(GL_DISPATCH_INDIRECT_BUFFER, 0);
		GLU_EXPECT_NO_ERROR(gl.getError(), "Compute dispatch state reset failed");
	}

	// Buffer copy state.
	if (contextSupports(type, ApiType::es(3,0)))
	{
		gl.bindBuffer(GL_COPY_READ_BUFFER,	0);
		gl.bindBuffer(GL_COPY_WRITE_BUFFER,	0);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Buffer copy state reset failed");
	}

	// Images.
	if (contextSupports(type, ApiType::es(3,1)))
	{
		int numImageUnits = 0;
		gl.getIntegerv(GL_MAX_IMAGE_UNITS, &numImageUnits);

		for (int ndx = 0; ndx < numImageUnits; ndx++)
			gl.bindImageTexture(ndx, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32UI);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Image state reset failed");
	}

	// Sample shading state.
	if (contextSupports(type, ApiType::es(3,1)) && ctxInfo.isExtensionSupported("GL_OES_sample_shading"))
	{
		gl.minSampleShading(0.0f);
		gl.disable(GL_SAMPLE_SHADING);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Sample shading state reset failed");
	}

	// Debug state
	if (ctxInfo.isExtensionSupported("GL_KHR_debug"))
	{
		const bool entrypointsPresent =	gl.debugMessageControl	!= DE_NULL	&&
										gl.debugMessageCallback	!= DE_NULL	&&
										gl.popDebugGroup		!= DE_NULL;

		// some drivers advertise GL_KHR_debug but give out null pointers. Silently ignore.
		if (entrypointsPresent)
		{
			int stackDepth = 0;
			gl.getIntegerv(GL_DEBUG_GROUP_STACK_DEPTH, &stackDepth);
			for (int ndx = 1; ndx < stackDepth; ++ndx)
				gl.popDebugGroup();

			gl.debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, DE_NULL, true);
			gl.debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, DE_NULL, false);
			gl.debugMessageCallback(DE_NULL, DE_NULL);

			if (type.getFlags() & glu::CONTEXT_DEBUG)
				gl.enable(GL_DEBUG_OUTPUT);
			else
				gl.disable(GL_DEBUG_OUTPUT);
			gl.disable(GL_DEBUG_OUTPUT_SYNCHRONOUS);

			GLU_EXPECT_NO_ERROR(gl.getError(), "Debug state reset failed");
		}
	}

	// Primitive bounding box state.
	if (ctxInfo.isExtensionSupported("GL_EXT_primitive_bounding_box"))
	{
		gl.primitiveBoundingBox(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f);
		GLU_EXPECT_NO_ERROR(gl.getError(), "Primitive bounding box state reset failed");
	}

	// Tessellation state
	if (ctxInfo.isExtensionSupported("GL_EXT_tessellation_shader"))
	{
		gl.patchParameteri(GL_PATCH_VERTICES, 3);
		GLU_EXPECT_NO_ERROR(gl.getError(), "Tessellation patch vertices state reset failed");
	}

	// Advanced coherent blending
	if (ctxInfo.isExtensionSupported("GL_KHR_blend_equation_advanced_coherent"))
	{
		gl.enable(GL_BLEND_ADVANCED_COHERENT_KHR);
		GLU_EXPECT_NO_ERROR(gl.getError(), "Blend equation advanced coherent state reset failed");
	}

	// Texture buffer
	if (ctxInfo.isExtensionSupported("GL_EXT_texture_buffer"))
	{
		gl.bindTexture(GL_TEXTURE_BUFFER, 0);
		gl.bindBuffer(GL_TEXTURE_BUFFER, 0);
		GLU_EXPECT_NO_ERROR(gl.getError(), "Texture buffer state reset failed");
	}
}

void resetStateGLCore (const RenderContext& renderCtx, const ContextInfo& ctxInfo)
{
	const glw::Functions&	gl		= renderCtx.getFunctions();
	const ContextType		type	= renderCtx.getType();

	// Reset error state
	resetErrors(gl);

	// Primitives and vertices state
	{
		if (contextSupports(type, glu::ApiType::core(4, 0)))
		{
			const float defaultTessLevels[]						= { 1.0f, 1.0f, 1.0f, 1.0f };
			gl.patchParameteri(GL_PATCH_VERTICES_EXT,			3);
			gl.patchParameterfv(GL_PATCH_DEFAULT_INNER_LEVEL,	defaultTessLevels);
			gl.patchParameterfv(GL_PATCH_DEFAULT_OUTER_LEVEL,	defaultTessLevels);
		}

		GLU_EXPECT_NO_ERROR(gl.getError(), "Primitives and vertices state reset failed");
	}

	// Vertex attrib array state.
	{
		gl.bindVertexArray	(0);
		gl.bindBuffer		(GL_ARRAY_BUFFER,			0);
		gl.bindBuffer		(GL_ELEMENT_ARRAY_BUFFER,	0);

		if (contextSupports(type, ApiType::core(3,1)))
		{
			gl.disable				(GL_PRIMITIVE_RESTART);
			gl.primitiveRestartIndex(0);
		}

		GLU_EXPECT_NO_ERROR(gl.getError(), "Vertex attrib array state reset failed");
	}

	// Transformation state.
	{
		const tcu::RenderTarget&	renderTarget		= renderCtx.getRenderTarget();
		int							numUserClipPlanes	= 0;

		gl.getIntegerv(GL_MAX_CLIP_DISTANCES, &numUserClipPlanes);

		gl.viewport		(0, 0, renderTarget.getWidth(), renderTarget.getHeight());
		gl.depthRange	(0.0, 1.0);

		for (int ndx = 0; ndx < numUserClipPlanes; ndx++)
			gl.disable(GL_CLIP_DISTANCE0+ndx);

		if (contextSupports(type, ApiType::core(3,2)))
			gl.disable(GL_DEPTH_CLAMP);

		//gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Transformation state reset failed");
	}

	// Coloring
	{
		gl.clampColor(GL_CLAMP_READ_COLOR, GL_FIXED_ONLY);

		if (contextSupports(type, ApiType::core(3,2)))
			gl.provokingVertex(GL_LAST_VERTEX_CONVENTION);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Coloring state reset failed");
	}

	// Rasterization state
	{
		gl.disable			(GL_RASTERIZER_DISCARD);
		gl.pointSize		(1.0f);
		gl.pointParameterf	(GL_POINT_FADE_THRESHOLD_SIZE,	1.0f);
		gl.pointParameteri	(GL_POINT_SPRITE_COORD_ORIGIN,	GL_UPPER_LEFT);
		gl.lineWidth		(1.0f);
		gl.disable			(GL_LINE_SMOOTH);
		gl.disable			(GL_CULL_FACE);
		gl.cullFace			(GL_BACK);
		gl.frontFace		(GL_CCW);
		gl.disable			(GL_POLYGON_SMOOTH);
		gl.polygonOffset	(0.0f, 0.0f);
		gl.disable			(GL_POLYGON_OFFSET_POINT);
		gl.disable			(GL_POLYGON_OFFSET_LINE);
		gl.disable			(GL_POLYGON_OFFSET_FILL);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Rasterization state reset failed");
	}

	// Multisampling state
	{
		gl.enable			(GL_MULTISAMPLE);
		gl.disable			(GL_SAMPLE_ALPHA_TO_COVERAGE);
		gl.disable			(GL_SAMPLE_ALPHA_TO_ONE);
		gl.disable			(GL_SAMPLE_COVERAGE);
		gl.sampleCoverage	(1.0f, GL_FALSE);

		if (contextSupports(type, ApiType::core(3,2)))
		{
			int numSampleMaskWords = 0;
			gl.getIntegerv(GL_MAX_SAMPLE_MASK_WORDS, &numSampleMaskWords);

			gl.disable(GL_SAMPLE_MASK);

			for (int ndx = 0; ndx < numSampleMaskWords; ndx++)
				gl.sampleMaski(ndx, ~0u);
		}

		GLU_EXPECT_NO_ERROR(gl.getError(), "Multisampling state reset failed");
	}

	// Texture state.
	// \todo [2013-04-08 pyry] Reset all levels?
	{
		const float	borderColor[]	= { 0.0f, 0.0f, 0.0f, 0.0f };
		int			numTexUnits		= 0;
		gl.getIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &numTexUnits);

		gl.bindBuffer(GL_TEXTURE_BUFFER, 0);

		for (int ndx = 0; ndx < numTexUnits; ndx++)
		{
			gl.activeTexture(GL_TEXTURE0 + ndx);

			// Reset 1D texture.
			gl.bindTexture		(GL_TEXTURE_1D, 0);
			gl.texImage1D		(GL_TEXTURE_1D, 0, GL_RGBA, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texParameteri	(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER,		GL_LINEAR_MIPMAP_NEAREST);
			gl.texParameteri	(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER,		GL_LINEAR);
			gl.texParameterfv	(GL_TEXTURE_1D, GL_TEXTURE_BORDER_COLOR,	&borderColor[0]);
			gl.texParameteri	(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S,			GL_REPEAT);
			gl.texParameterf	(GL_TEXTURE_1D, GL_TEXTURE_MIN_LOD,			-1000.0f);
			gl.texParameterf	(GL_TEXTURE_1D, GL_TEXTURE_MAX_LOD,			1000.0f);
			gl.texParameteri	(GL_TEXTURE_1D, GL_TEXTURE_BASE_LEVEL,		0);
			gl.texParameteri	(GL_TEXTURE_1D, GL_TEXTURE_MAX_LEVEL,		1000);
			gl.texParameterf	(GL_TEXTURE_1D, GL_TEXTURE_LOD_BIAS,		0.0f);
			gl.texParameteri	(GL_TEXTURE_1D, GL_TEXTURE_COMPARE_MODE,	GL_NONE);
			gl.texParameteri	(GL_TEXTURE_1D, GL_TEXTURE_COMPARE_FUNC,	GL_LEQUAL);

			if (contextSupports(type, ApiType::core(3,3)))
			{
				gl.texParameteri(GL_TEXTURE_1D, GL_TEXTURE_SWIZZLE_R,		GL_RED);
				gl.texParameteri(GL_TEXTURE_1D, GL_TEXTURE_SWIZZLE_G,		GL_GREEN);
				gl.texParameteri(GL_TEXTURE_1D, GL_TEXTURE_SWIZZLE_B,		GL_BLUE);
				gl.texParameteri(GL_TEXTURE_1D, GL_TEXTURE_SWIZZLE_A,		GL_ALPHA);
			}

			// Reset 2D texture.
			gl.bindTexture		(GL_TEXTURE_2D, 0);
			gl.texImage2D		(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texParameteri	(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,		GL_LINEAR_MIPMAP_NEAREST);
			gl.texParameteri	(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,		GL_LINEAR);
			gl.texParameterfv	(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR,	&borderColor[0]);
			gl.texParameteri	(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,			GL_REPEAT);
			gl.texParameteri	(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,			GL_REPEAT);
			gl.texParameterf	(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD,			-1000.0f);
			gl.texParameterf	(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD,			1000.0f);
			gl.texParameteri	(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL,		0);
			gl.texParameteri	(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL,		1000);
			gl.texParameterf	(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS,		0.0f);
			gl.texParameteri	(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE,	GL_NONE);
			gl.texParameteri	(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC,	GL_LEQUAL);

			if (contextSupports(type, ApiType::core(3,3)))
			{
				gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R,		GL_RED);
				gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G,		GL_GREEN);
				gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B,		GL_BLUE);
				gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A,		GL_ALPHA);
			}

			// Reset cube map texture.
			gl.bindTexture		(GL_TEXTURE_CUBE_MAP, 0);
			gl.texImage2D		(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texImage2D		(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texImage2D		(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texImage2D		(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texImage2D		(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texImage2D		(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texParameteri	(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,	GL_LINEAR_MIPMAP_NEAREST);
			gl.texParameteri	(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER,	GL_LINEAR);
			gl.texParameterfv	(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR,	&borderColor[0]);
			gl.texParameteri	(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S,		GL_REPEAT);
			gl.texParameteri	(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T,		GL_REPEAT);
			gl.texParameterf	(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_LOD,		-1000.0f);
			gl.texParameterf	(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LOD,		1000.0f);
			gl.texParameteri	(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL,	0);
			gl.texParameteri	(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL,		1000);
			gl.texParameterf	(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_LOD_BIAS,		0.0f);
			gl.texParameteri	(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE,	GL_NONE);
			gl.texParameteri	(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC,	GL_LEQUAL);

			if (contextSupports(type, ApiType::core(3,3)))
			{
				gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_R,		GL_RED);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_G,		GL_GREEN);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_B,		GL_BLUE);
				gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_A,		GL_ALPHA);
			}

			// Reset 1D array texture.
			gl.bindTexture		(GL_TEXTURE_1D_ARRAY, 0);
			gl.texImage2D		(GL_TEXTURE_1D_ARRAY, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texParameteri	(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MIN_FILTER,	GL_LINEAR_MIPMAP_NEAREST);
			gl.texParameteri	(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAG_FILTER,	GL_LINEAR);
			gl.texParameterfv	(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_BORDER_COLOR,	&borderColor[0]);
			gl.texParameteri	(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_WRAP_S,		GL_REPEAT);
			gl.texParameterf	(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MIN_LOD,		-1000.0f);
			gl.texParameterf	(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAX_LOD,		1000.0f);
			gl.texParameteri	(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_BASE_LEVEL,	0);
			gl.texParameteri	(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAX_LEVEL,		1000);
			gl.texParameterf	(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_LOD_BIAS,		0.0f);
			gl.texParameteri	(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_COMPARE_MODE,	GL_NONE);
			gl.texParameteri	(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_COMPARE_FUNC,	GL_LEQUAL);

			if (contextSupports(type, ApiType::core(3,3)))
			{
				gl.texParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_SWIZZLE_R,		GL_RED);
				gl.texParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_SWIZZLE_G,		GL_GREEN);
				gl.texParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_SWIZZLE_B,		GL_BLUE);
				gl.texParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_SWIZZLE_A,		GL_ALPHA);
			}

			// Reset 2D array texture.
			gl.bindTexture		(GL_TEXTURE_2D_ARRAY, 0);
			gl.texImage3D		(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 0, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texParameteri	(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER,	GL_LINEAR_MIPMAP_NEAREST);
			gl.texParameteri	(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER,	GL_LINEAR);
			gl.texParameterfv	(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BORDER_COLOR,	&borderColor[0]);
			gl.texParameteri	(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S,		GL_REPEAT);
			gl.texParameteri	(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T,		GL_REPEAT);
			gl.texParameterf	(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_LOD,		-1000.0f);
			gl.texParameterf	(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LOD,		1000.0f);
			gl.texParameteri	(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BASE_LEVEL,	0);
			gl.texParameteri	(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL,		1000);
			gl.texParameterf	(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_LOD_BIAS,		0.0f);
			gl.texParameteri	(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE,	GL_NONE);
			gl.texParameteri	(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_FUNC,	GL_LEQUAL);

			if (contextSupports(type, ApiType::core(3,3)))
			{
				gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_SWIZZLE_R,		GL_RED);
				gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_SWIZZLE_G,		GL_GREEN);
				gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_SWIZZLE_B,		GL_BLUE);
				gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_SWIZZLE_A,		GL_ALPHA);
			}

			// Reset 3D texture.
			gl.bindTexture		(GL_TEXTURE_3D, 0);
			gl.texImage3D		(GL_TEXTURE_3D, 0, GL_RGBA, 0, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			gl.texParameteri	(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER,		GL_LINEAR_MIPMAP_NEAREST);
			gl.texParameteri	(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER,		GL_LINEAR);
			gl.texParameterfv	(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR,	&borderColor[0]);
			gl.texParameteri	(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S,			GL_REPEAT);
			gl.texParameteri	(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T,			GL_REPEAT);
			gl.texParameteri	(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R,			GL_REPEAT);
			gl.texParameterf	(GL_TEXTURE_3D, GL_TEXTURE_MIN_LOD,			-1000.0f);
			gl.texParameterf	(GL_TEXTURE_3D, GL_TEXTURE_MAX_LOD,			1000.0f);
			gl.texParameteri	(GL_TEXTURE_3D, GL_TEXTURE_BASE_LEVEL,		0);
			gl.texParameteri	(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL,		1000);
			gl.texParameterf	(GL_TEXTURE_3D, GL_TEXTURE_LOD_BIAS,		0.0f);
			gl.texParameteri	(GL_TEXTURE_3D, GL_TEXTURE_COMPARE_MODE,	GL_NONE);
			gl.texParameteri	(GL_TEXTURE_3D, GL_TEXTURE_COMPARE_FUNC,	GL_LEQUAL);

			if (contextSupports(type, ApiType::core(3,3)))
			{
				gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_R,		GL_RED);
				gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_G,		GL_GREEN);
				gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_B,		GL_BLUE);
				gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_A,		GL_ALPHA);
			}

			if (contextSupports(type, ApiType::core(3,1)))
			{
				// Reset rectangle texture.
				gl.bindTexture		(GL_TEXTURE_RECTANGLE, 0);
				gl.texImage2D		(GL_TEXTURE_RECTANGLE, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
				gl.texParameteri	(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER,	GL_LINEAR);
				gl.texParameteri	(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER,	GL_LINEAR);
				gl.texParameterfv	(GL_TEXTURE_RECTANGLE, GL_TEXTURE_BORDER_COLOR,	&borderColor[0]);
				gl.texParameteri	(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S,		GL_CLAMP_TO_EDGE);
				gl.texParameteri	(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T,		GL_CLAMP_TO_EDGE);
				gl.texParameteri	(GL_TEXTURE_RECTANGLE, GL_TEXTURE_BASE_LEVEL,	0);
				gl.texParameteri	(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAX_LEVEL,	1000);
				gl.texParameteri	(GL_TEXTURE_RECTANGLE, GL_TEXTURE_COMPARE_MODE,	GL_NONE);
				gl.texParameteri	(GL_TEXTURE_RECTANGLE, GL_TEXTURE_COMPARE_FUNC,	GL_LEQUAL);
				// \todo [2013-06-17 pyry] Drivers don't accept GL_MIN_LOD, GL_MAX_LOD for rectangle textures. Is that okay?

				if (contextSupports(type, ApiType::core(3,3)))
				{
					gl.texParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_SWIZZLE_R,	GL_RED);
					gl.texParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_SWIZZLE_G,	GL_GREEN);
					gl.texParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_SWIZZLE_B,	GL_BLUE);
					gl.texParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_SWIZZLE_A,	GL_ALPHA);
				}

				// Reset buffer texture.
				gl.bindTexture	(GL_TEXTURE_BUFFER, 0);
				gl.texBuffer	(GL_TEXTURE_BUFFER, GL_R8, 0);
				// \todo [2013-05-04 pyry] Which parameters apply to buffer textures?
			}

			if (contextSupports(type, ApiType::core(3,2)))
			{
				// Reset 2D multisample texture.
				gl.bindTexture				(GL_TEXTURE_2D_MULTISAMPLE, 0);
				gl.texImage2DMultisample	(GL_TEXTURE_2D_MULTISAMPLE, 1, GL_RGBA8, 0, 0, GL_TRUE);

				// Reset 2D multisample array texture.
				gl.bindTexture				(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 0);
				gl.texImage3DMultisample	(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 1, GL_RGBA8, 0, 0, 0, GL_TRUE);
			}
		}

		gl.activeTexture(GL_TEXTURE0);

		if (contextSupports(type, ApiType::core(3,3)))
		{
			for (int ndx = 0; ndx < numTexUnits; ndx++)
				gl.bindSampler(ndx, 0);

			gl.disable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
		}

		GLU_EXPECT_NO_ERROR(gl.getError(), "Texture state reset failed");
	}

	// Pixel operations.
	{
		const tcu::RenderTarget& renderTarget = renderCtx.getRenderTarget();

		gl.disable		(GL_SCISSOR_TEST);
		gl.scissor		(0, 0, renderTarget.getWidth(), renderTarget.getHeight());

		gl.disable		(GL_STENCIL_TEST);
		gl.stencilFunc	(GL_ALWAYS, 0, ~0u);
		gl.stencilOp	(GL_KEEP, GL_KEEP, GL_KEEP);

		gl.disable		(GL_DEPTH_TEST);
		gl.depthFunc	(GL_LESS);

		gl.disable		(GL_BLEND);
		gl.blendFunc	(GL_ONE, GL_ZERO);
		gl.blendEquation(GL_FUNC_ADD);
		gl.blendColor	(0.0f, 0.0f, 0.0f, 0.0f);

		gl.disable		(GL_FRAMEBUFFER_SRGB);
		gl.enable		(GL_DITHER);

		gl.disable		(GL_COLOR_LOGIC_OP);
		gl.logicOp		(GL_COPY);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Pixel operation state reset failed");
	}

	// Framebuffer control.
	{
		gl.colorMask		(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
		gl.depthMask		(GL_TRUE);
		gl.stencilMask		(~0u);

		gl.clearColor		(0.0f, 0.0f, 0.0f, 0.0f);
		gl.clearDepth		(1.0);
		gl.clearStencil		(0);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Framebuffer control state reset failed");
	}

	// Framebuffer state.
	{
		const deUint32	framebuffer	= renderCtx.getDefaultFramebuffer();

		gl.bindFramebuffer(GL_FRAMEBUFFER, framebuffer);

		if (framebuffer == 0)
		{
			gl.drawBuffer(GL_BACK);
			gl.readBuffer(GL_BACK);

			// This is a workaround for supporting single-buffered configurations.
			// Since there is no other place where we need to know if we are dealing
			// with single-buffered config, it is not worthwhile to add additional
			// state into RenderContext for that.
			if (gl.getError() != GL_NO_ERROR)
			{
				gl.drawBuffer(GL_FRONT);
				gl.readBuffer(GL_FRONT);
			}
		}
		else
		{
			gl.drawBuffer(GL_COLOR_ATTACHMENT0);
			gl.readBuffer(GL_COLOR_ATTACHMENT0);
		}

		GLU_EXPECT_NO_ERROR(gl.getError(), "Framebuffer state reset failed");
	}

	// Renderbuffer state.
	{
		gl.bindRenderbuffer(GL_RENDERBUFFER, 0);
		GLU_EXPECT_NO_ERROR(gl.getError(), "Renderbuffer state reset failed");
	}

	// Pixel transfer state.
	{
		gl.pixelStorei	(GL_UNPACK_SWAP_BYTES,		GL_FALSE);
		gl.pixelStorei	(GL_UNPACK_LSB_FIRST,		GL_FALSE);
		gl.pixelStorei	(GL_UNPACK_IMAGE_HEIGHT,	0);
		gl.pixelStorei	(GL_UNPACK_SKIP_IMAGES,		0);
		gl.pixelStorei	(GL_UNPACK_ROW_LENGTH,		0);
		gl.pixelStorei	(GL_UNPACK_SKIP_ROWS,		0);
		gl.pixelStorei	(GL_UNPACK_SKIP_PIXELS,		0);
		gl.pixelStorei	(GL_UNPACK_ALIGNMENT,		4);

		gl.pixelStorei	(GL_PACK_SWAP_BYTES,		GL_FALSE);
		gl.pixelStorei	(GL_PACK_LSB_FIRST,			GL_FALSE);
		gl.pixelStorei	(GL_PACK_IMAGE_HEIGHT,		0);
		gl.pixelStorei	(GL_PACK_SKIP_IMAGES,		0);
		gl.pixelStorei	(GL_PACK_ROW_LENGTH,		0);
		gl.pixelStorei	(GL_PACK_SKIP_ROWS,			0);
		gl.pixelStorei	(GL_PACK_SKIP_PIXELS,		0);
		gl.pixelStorei	(GL_PACK_ALIGNMENT,			4);

		gl.bindBuffer	(GL_PIXEL_PACK_BUFFER,		0);
		gl.bindBuffer	(GL_PIXEL_UNPACK_BUFFER,	0);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Pixel transfer state reset failed");
	}

	// Program object state.
	{
		gl.useProgram(0);

		if (contextSupports(type, ApiType::core(3,1)))
		{
			int maxUniformBufferBindings = 0;
			gl.getIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings);

			gl.bindBuffer(GL_UNIFORM_BUFFER, 0);

			for (int ndx = 0; ndx < maxUniformBufferBindings; ndx++)
				gl.bindBufferBase(GL_UNIFORM_BUFFER, ndx, 0);
		}

		GLU_EXPECT_NO_ERROR(gl.getError(), "Program object state reset failed");
	}

	// Vertex shader state.
	{
		int numVertexAttribArrays = 0;
		gl.getIntegerv(GL_MAX_VERTEX_ATTRIBS, &numVertexAttribArrays);

		for (int ndx = 0; ndx < numVertexAttribArrays; ndx++)
			gl.vertexAttrib4f(ndx, 0.0f, 0.0f, 0.0f, 1.0f);

		gl.disable(GL_VERTEX_PROGRAM_POINT_SIZE);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Vertex shader state reset failed");
	}

	// Transform feedback state.
	{
		int numTransformFeedbackSeparateAttribs = 0;
		gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &numTransformFeedbackSeparateAttribs);

		if (contextSupports(type, ApiType::core(4,0)))
		{
			glw::GLboolean transformFeedbackActive = 0;
			gl.getBooleanv(GL_TRANSFORM_FEEDBACK_ACTIVE, &transformFeedbackActive);

			if (transformFeedbackActive)
				gl.endTransformFeedback();
		}

		gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);

		for (int ndx = 0; ndx < numTransformFeedbackSeparateAttribs; ndx++)
			gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, ndx, 0);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Transform feedback state reset failed");
	}

	// Asynchronous query state.
	{
		deUint32	queryTargets[8];
		int			numTargets		= 0;

		queryTargets[numTargets++] = GL_PRIMITIVES_GENERATED;
		queryTargets[numTargets++] = GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN;
		queryTargets[numTargets++] = GL_SAMPLES_PASSED;

		DE_ASSERT(numTargets <= DE_LENGTH_OF_ARRAY(queryTargets));

		for (int i = 0; i < numTargets; i++)
		{
			int queryActive = 0;
			gl.getQueryiv(queryTargets[i], GL_CURRENT_QUERY, &queryActive);

			if (queryActive != 0)
				gl.endQuery(queryTargets[i]);
		}

		GLU_EXPECT_NO_ERROR(gl.getError(), "Asynchronous query state reset failed");
	}

	// Hints.
	{
		gl.hint(GL_LINE_SMOOTH_HINT,				GL_DONT_CARE);
		gl.hint(GL_POLYGON_SMOOTH_HINT,				GL_DONT_CARE);
		gl.hint(GL_TEXTURE_COMPRESSION_HINT,		GL_DONT_CARE);
		gl.hint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT,	GL_DONT_CARE);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Hints reset failed");
	}

	// Buffer copy state.
	if (contextSupports(type, ApiType::core(3,1)))
	{
		gl.bindBuffer(GL_COPY_READ_BUFFER,	0);
		gl.bindBuffer(GL_COPY_WRITE_BUFFER,	0);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Buffer copy state reset failed");
	}

	// Debug state
	if (ctxInfo.isExtensionSupported("GL_KHR_debug"))
	{
		const bool entrypointsPresent =	gl.debugMessageControl	!= DE_NULL	&&
										gl.debugMessageCallback	!= DE_NULL;

		// some drivers advertise GL_KHR_debug but give out null pointers. Silently ignore.
		if (entrypointsPresent)
		{
			gl.debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, DE_NULL, true);
			gl.debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, DE_NULL, false);
			gl.debugMessageCallback(DE_NULL, DE_NULL);

			if (type.getFlags() & glu::CONTEXT_DEBUG)
				gl.enable(GL_DEBUG_OUTPUT);
			else
				gl.disable(GL_DEBUG_OUTPUT);
		}
	}
}

void resetState (const RenderContext& renderCtx, const ContextInfo& ctxInfo)
{
	if (isContextTypeES(renderCtx.getType()))
		resetStateES(renderCtx, ctxInfo);
	else if (isContextTypeGLCore(renderCtx.getType()))
		resetStateGLCore(renderCtx, ctxInfo);
	else
		throw tcu::InternalError("State reset requested for unsupported context type");
}

} // glu