/*-------------------------------------------------------------------------
 * 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 Render context implementation that does no rendering.
 *//*--------------------------------------------------------------------*/

#include "tcuNullRenderContext.hpp"
#include "tcuTexture.hpp"
#include "tcuTextureUtil.hpp"
#include "deThreadLocal.hpp"
#include "gluRenderConfig.hpp"
#include "gluTextureUtil.hpp"
#include "glwEnums.hpp"

#include <string>
#include <vector>

namespace tcu
{
namespace null
{

using namespace glw;

#include "tcuNullRenderContextFuncs.inl"

using namespace glu;
using std::string;
using std::vector;

class ObjectManager
{
public:
	ObjectManager (void)
		: m_lastObject(0)
	{
	}

	deUint32 allocate (void)
	{
		deUint32 object = ++m_lastObject;
		if (object == 0)
			object = ++m_lastObject; // Just ignore overflow.
		return object;
	}

	void free (deUint32 object)
	{
		DE_UNREF(object);
	}

private:
	deUint32 m_lastObject;
};

class Context
{
public:
							Context				(ContextType ctxType_);
							~Context			(void);

private:
							Context				(const Context&);
	Context&				operator=			(const Context&);

	void					addExtension		(const char* name);

public:
	// GL state exposed to implementation functions.
	const ContextType		ctxType;

	string					vendor;
	string					version;
	string					renderer;
	string					shadingLanguageVersion;
	string					extensions;
	vector<string>			extensionList;
	vector<deUint32>		compressedTextureList;

	GLenum					lastError;

	int						pixelPackRowLength;
	int						pixelPackSkipRows;
	int						pixelPackSkipPixels;
	int						pixelPackAlignment;

	GLuint					pixelPackBufferBufferBinding;

	ObjectManager			shaders;
	ObjectManager			programs;
	ObjectManager			textures;
	ObjectManager			buffers;
	ObjectManager			renderbuffers;
	ObjectManager			framebuffers;
	ObjectManager			samplers;
	ObjectManager			vertexArrays;
	ObjectManager			queries;
	ObjectManager			transformFeedbacks;
	ObjectManager			programPipelines;
};

Context::Context (ContextType ctxType_)
	: ctxType						(ctxType_)
	, vendor						("drawElements")
	, renderer						("dummy")
	, lastError						(GL_NO_ERROR)
	, pixelPackRowLength			(0)
	, pixelPackSkipRows				(0)
	, pixelPackSkipPixels			(0)
	, pixelPackAlignment			(0)
	, pixelPackBufferBufferBinding	(0)
{
	using glu::ApiType;

	if (ctxType.getAPI() == ApiType::es(2, 0))
	{
		version					= "OpenGL ES 2.0";
		shadingLanguageVersion	= "OpenGL ES GLSL ES 1.0";
	}
	else if (ctxType.getAPI() == ApiType::es(3, 0))
	{
		version					= "OpenGL ES 3.0";
		shadingLanguageVersion	= "OpenGL ES GLSL ES 3.0";
	}
	else if (ctxType.getAPI() == ApiType::es(3, 1))
	{
		version					= "OpenGL ES 3.1";
		shadingLanguageVersion	= "OpenGL ES GLSL ES 3.1";
		addExtension("GL_OES_texture_stencil8");
		addExtension("GL_OES_sample_shading");
		addExtension("GL_OES_sample_variables");
		addExtension("GL_OES_shader_multisample_interpolation");
		addExtension("GL_OES_shader_image_atomic");
		addExtension("GL_OES_texture_storage_multisample_2d_array");
		addExtension("GL_KHR_blend_equation_advanced");
		addExtension("GL_KHR_blend_equation_advanced_coherent");
		addExtension("GL_EXT_shader_io_blocks");
		addExtension("GL_EXT_geometry_shader");
		addExtension("GL_EXT_geometry_point_size");
		addExtension("GL_EXT_tessellation_shader");
		addExtension("GL_EXT_tessellation_point_size");
		addExtension("GL_EXT_gpu_shader5");
		addExtension("GL_EXT_shader_implicit_conversions");
		addExtension("GL_EXT_texture_buffer");
		addExtension("GL_EXT_texture_cube_map_array");
		addExtension("GL_EXT_draw_buffers_indexed");
		addExtension("GL_EXT_texture_sRGB_decode");
		addExtension("GL_EXT_texture_border_clamp");
		addExtension("GL_KHR_debug");
		addExtension("GL_EXT_primitive_bounding_box");
		addExtension("GL_ANDROID_extension_pack_es31a");
		addExtension("GL_EXT_copy_image");
	}
	else if (glu::isContextTypeGLCore(ctxType) && ctxType.getMajorVersion() == 3)
	{
		version					= "3.3.0";
		shadingLanguageVersion	= "3.30";
	}
	else if (glu::isContextTypeGLCore(ctxType) && ctxType.getMajorVersion() == 4 && ctxType.getMinorVersion() <= 4)
	{
		version					= "4.4.0";
		shadingLanguageVersion	= "4.40";
	}
	else
		throw tcu::NotSupportedError("Unsupported GL version", "", __FILE__, __LINE__);

	if (isContextTypeES(ctxType))
	{
		addExtension("GL_EXT_color_buffer_float");
		addExtension("GL_EXT_color_buffer_half_float");
	}

	// support compressed formats
	{
		static deUint32 compressedFormats[] =
		{
			GL_ETC1_RGB8_OES,
			GL_COMPRESSED_R11_EAC,
			GL_COMPRESSED_SIGNED_R11_EAC,
			GL_COMPRESSED_RG11_EAC,
			GL_COMPRESSED_SIGNED_RG11_EAC,
			GL_COMPRESSED_RGB8_ETC2,
			GL_COMPRESSED_SRGB8_ETC2,
			GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
			GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,
			GL_COMPRESSED_RGBA8_ETC2_EAC,
			GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,
			GL_COMPRESSED_RGBA_ASTC_4x4_KHR,
			GL_COMPRESSED_RGBA_ASTC_5x4_KHR,
			GL_COMPRESSED_RGBA_ASTC_5x5_KHR,
			GL_COMPRESSED_RGBA_ASTC_6x5_KHR,
			GL_COMPRESSED_RGBA_ASTC_6x6_KHR,
			GL_COMPRESSED_RGBA_ASTC_8x5_KHR,
			GL_COMPRESSED_RGBA_ASTC_8x6_KHR,
			GL_COMPRESSED_RGBA_ASTC_8x8_KHR,
			GL_COMPRESSED_RGBA_ASTC_10x5_KHR,
			GL_COMPRESSED_RGBA_ASTC_10x6_KHR,
			GL_COMPRESSED_RGBA_ASTC_10x8_KHR,
			GL_COMPRESSED_RGBA_ASTC_10x10_KHR,
			GL_COMPRESSED_RGBA_ASTC_12x10_KHR,
			GL_COMPRESSED_RGBA_ASTC_12x12_KHR,
			GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR,
			GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR,
			GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR,
			GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR,
			GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR,
			GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR,
			GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR,
			GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR,
			GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR,
			GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR,
			GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR,
			GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR,
			GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR,
			GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR,
		};

		addExtension("GL_KHR_texture_compression_astc_hdr");
		addExtension("GL_KHR_texture_compression_astc_ldr");
		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(compressedFormats); ++ndx)
			compressedTextureList.push_back(compressedFormats[ndx]);
	}
}

Context::~Context (void)
{
}

void Context::addExtension (const char* name)
{
	if (!extensions.empty())
		extensions += " ";
	extensions += name;

	extensionList.push_back(name);
}

static de::ThreadLocal s_currentCtx;

void setCurrentContext (Context* context)
{
	s_currentCtx.set((void*)context);
}

Context* getCurrentContext (void)
{
	return (Context*)s_currentCtx.get();
}

GLW_APICALL GLenum GLW_APIENTRY glGetError (void)
{
	Context* const	ctx		= getCurrentContext();
	const GLenum	lastErr	= ctx->lastError;

	ctx->lastError = GL_NO_ERROR;

	return lastErr;
}

GLW_APICALL void GLW_APIENTRY glGetIntegerv (GLenum pname, GLint* params)
{
	Context* const ctx = getCurrentContext();

	switch (pname)
	{
		case GL_NUM_EXTENSIONS:
			*params = (int)ctx->extensionList.size();
			break;

		case GL_MAX_VERTEX_ATTRIBS:
			*params = 32;
			break;

		case GL_MAX_DRAW_BUFFERS:
		case GL_MAX_COLOR_ATTACHMENTS:
			*params = 8;
			break;

		case GL_MAX_TEXTURE_IMAGE_UNITS:
		case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
		case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
		case GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS:
		case GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS:
		case GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS:
			*params = 32;
			break;

		case GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS:
		case GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS:
		case GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS:
		case GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS:
		case GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS:
		case GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS:
			*params = 8;
			break;

		case GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS:
		case GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS:
		case GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS:
		case GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS:
		case GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS:
		case GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS:
			*params = 8;
			break;

		case GL_MAX_SHADER_STORAGE_BLOCK_SIZE:
			*params = 1u << 25;
			break;

		case GL_MAX_GEOMETRY_OUTPUT_VERTICES:
			*params = 256;
			break;

		case GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS:
			*params = 2048;
			break;

		case GL_MAX_GEOMETRY_SHADER_INVOCATIONS:
			*params = 4;
			break;

		case GL_MAX_COLOR_TEXTURE_SAMPLES:
			*params = 8;
			break;

		case GL_MAX_TEXTURE_SIZE:
		case GL_MAX_CUBE_MAP_TEXTURE_SIZE:
		case GL_MAX_3D_TEXTURE_SIZE:
		case GL_MAX_RENDERBUFFER_SIZE:
		case GL_MAX_TEXTURE_BUFFER_SIZE:
			*params = 2048;
			break;

		case GL_MAX_ARRAY_TEXTURE_LAYERS:
			*params = 128;
			break;

		case GL_NUM_SHADER_BINARY_FORMATS:
			*params = 0;
			break;

		case GL_NUM_COMPRESSED_TEXTURE_FORMATS:
			*params = (int)ctx->compressedTextureList.size();
			break;

		case GL_COMPRESSED_TEXTURE_FORMATS:
			deMemcpy(params, &ctx->compressedTextureList[0], ctx->compressedTextureList.size()*sizeof(deUint32));
			break;

		case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS:
			*params = 16;
			break;

		case GL_MAX_UNIFORM_BUFFER_BINDINGS:
			*params = 32;
			break;

		case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT:
			*params = 16;
			break;

		case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
			*params = GL_RGBA;
			break;

		case GL_IMPLEMENTATION_COLOR_READ_TYPE:
			*params = GL_UNSIGNED_BYTE;
			break;

		case GL_SAMPLE_BUFFERS:
			*params = 0;
			break;

		default:
			break;
	}
}

GLW_APICALL void GLW_APIENTRY glGetBooleanv (GLenum pname, GLboolean* params)
{
	switch (pname)
	{
		case GL_SHADER_COMPILER:
			*params = GL_TRUE;
			break;

		default:
			break;
	}
}

GLW_APICALL void GLW_APIENTRY glGetFloatv (GLenum pname, GLfloat* params)
{
	switch (pname)
	{
		case GL_ALIASED_LINE_WIDTH_RANGE:
		case GL_ALIASED_POINT_SIZE_RANGE:
			params[0] = 0.0f;
			params[1] = 64.0f;
			break;

		default:
			break;
	}
}

GLW_APICALL void GLW_APIENTRY glGetInternalformativ (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint* params)
{
	static const int s_sampleCounts[] = { 16, 8, 4, 2, 1 };

	DE_UNREF(internalformat);
	DE_UNREF(target);

	switch (pname)
	{
		case GL_NUM_SAMPLE_COUNTS:
			if (bufSize >= 1)
				*params = DE_LENGTH_OF_ARRAY(s_sampleCounts);
			break;

		case GL_SAMPLES:
			deMemcpy(params, s_sampleCounts, de::min(bufSize, DE_LENGTH_OF_ARRAY(s_sampleCounts)));
			break;

		default:
			break;
	}
}

GLW_APICALL const glw::GLubyte* GLW_APIENTRY glGetString (GLenum name)
{
	Context* const ctx = getCurrentContext();

	switch (name)
	{
		case GL_VENDOR:						return (const glw::GLubyte*)ctx->vendor.c_str();
		case GL_VERSION:					return (const glw::GLubyte*)ctx->version.c_str();
		case GL_RENDERER:					return (const glw::GLubyte*)ctx->renderer.c_str();
		case GL_SHADING_LANGUAGE_VERSION:	return (const glw::GLubyte*)ctx->shadingLanguageVersion.c_str();
		case GL_EXTENSIONS:					return (const glw::GLubyte*)ctx->extensions.c_str();
		default:
			ctx->lastError = GL_INVALID_ENUM;
			return DE_NULL;
	}
}

GLW_APICALL const glw::GLubyte* GLW_APIENTRY glGetStringi (GLenum name, GLuint index)
{
	Context* const ctx = getCurrentContext();

	if (name == GL_EXTENSIONS)
	{
		if ((size_t)index < ctx->extensionList.size())
			return (const glw::GLubyte*)ctx->extensionList[index].c_str();
		else
		{
			ctx->lastError = GL_INVALID_VALUE;
			return DE_NULL;
		}
	}
	else
	{
		ctx->lastError = GL_INVALID_ENUM;
		return DE_NULL;
	}
}

GLW_APICALL GLuint GLW_APIENTRY glCreateProgram ()
{
	Context* const ctx = getCurrentContext();
	return (GLuint)ctx->programs.allocate();
}

GLW_APICALL GLuint GLW_APIENTRY glCreateShader (GLenum type)
{
	Context* const ctx = getCurrentContext();
	DE_UNREF(type);
	return (GLuint)ctx->shaders.allocate();
}

GLW_APICALL void GLW_APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint* params)
{
	DE_UNREF(shader);

	if (pname == GL_COMPILE_STATUS)
		*params = GL_TRUE;
}

GLW_APICALL void GLW_APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint* params)
{
	DE_UNREF(program);

	if (pname == GL_LINK_STATUS)
		*params = GL_TRUE;
}

GLW_APICALL void GLW_APIENTRY glGenTextures (GLsizei n, GLuint* textures)
{
	Context* const ctx = getCurrentContext();

	if (textures)
	{
		for (int ndx = 0; ndx < n; ndx++)
			textures[ndx] = ctx->textures.allocate();
	}
}

GLW_APICALL void GLW_APIENTRY glGenQueries (GLsizei n, GLuint* ids)
{
	Context* const ctx = getCurrentContext();

	if (ids)
	{
		for (int ndx = 0; ndx < n; ndx++)
			ids[ndx] = ctx->queries.allocate();
	}
}

GLW_APICALL void GLW_APIENTRY glGenBuffers (GLsizei n, GLuint* buffers)
{
	Context* const ctx = getCurrentContext();

	if (buffers)
	{
		for (int ndx = 0; ndx < n; ndx++)
			buffers[ndx] = ctx->buffers.allocate();
	}
}

GLW_APICALL void GLW_APIENTRY glGenRenderbuffers (GLsizei n, GLuint* renderbuffers)
{
	Context* const ctx = getCurrentContext();

	if (renderbuffers)
	{
		for (int ndx = 0; ndx < n; ndx++)
			renderbuffers[ndx] = ctx->renderbuffers.allocate();
	}
}

GLW_APICALL void GLW_APIENTRY glGenFramebuffers (GLsizei n, GLuint* framebuffers)
{
	Context* const ctx = getCurrentContext();

	if (framebuffers)
	{
		for (int ndx = 0; ndx < n; ndx++)
			framebuffers[ndx] = ctx->framebuffers.allocate();
	}
}

GLW_APICALL void GLW_APIENTRY glGenVertexArrays (GLsizei n, GLuint* arrays)
{
	Context* const ctx = getCurrentContext();

	if (arrays)
	{
		for (int ndx = 0; ndx < n; ndx++)
			arrays[ndx] = ctx->vertexArrays.allocate();
	}
}

GLW_APICALL void GLW_APIENTRY glGenSamplers (GLsizei count, GLuint* samplers)
{
	Context* const ctx = getCurrentContext();

	if (samplers)
	{
		for (int ndx = 0; ndx < count; ndx++)
			samplers[ndx] = ctx->samplers.allocate();
	}
}

GLW_APICALL void GLW_APIENTRY glGenTransformFeedbacks (GLsizei n, GLuint* ids)
{
	Context* const ctx = getCurrentContext();

	if (ids)
	{
		for (int ndx = 0; ndx < n; ndx++)
			ids[ndx] = ctx->transformFeedbacks.allocate();
	}
}

GLW_APICALL void GLW_APIENTRY glGenProgramPipelines (GLsizei n, GLuint* pipelines)
{
	Context* const ctx = getCurrentContext();

	if (pipelines)
	{
		for (int ndx = 0; ndx < n; ndx++)
			pipelines[ndx] = ctx->programPipelines.allocate();
	}
}

GLW_APICALL GLvoid* GLW_APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)
{
	Context* const ctx = getCurrentContext();

	DE_UNREF(target);
	DE_UNREF(offset);
	DE_UNREF(length);
	DE_UNREF(access);

	if (ctx->lastError == GL_NO_ERROR)
		ctx->lastError = GL_INVALID_OPERATION;

	return (GLvoid*)0;
}

GLW_APICALL GLenum GLW_APIENTRY glCheckFramebufferStatus (GLenum target)
{
	DE_UNREF(target);
	return GL_FRAMEBUFFER_COMPLETE;
}

GLW_APICALL void GLW_APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels)
{
	DE_UNREF(x);
	DE_UNREF(y);

	Context* const					ctx					= getCurrentContext();
	const tcu::Vec4					clearColor			(0.0f, 0.0f, 0.0f, 1.0f); // black
	const tcu::TextureFormat		transferFormat		= glu::mapGLTransferFormat(format, type);

	// invalid formats
	if (transferFormat.order == TextureFormat::CHANNELORDER_LAST || transferFormat.type == TextureFormat::CHANNELTYPE_LAST)
	{
		if (ctx->lastError == GL_NO_ERROR)
			ctx->lastError = GL_INVALID_ENUM;
		return;
	}

	// unsupported formats
	if (!(format == GL_RGBA			&& type == GL_UNSIGNED_BYTE)	&&
		!(format == GL_RGBA_INTEGER	&& type == GL_INT)				&&
		!(format == GL_RGBA_INTEGER	&& type == GL_UNSIGNED_INT)		&&
		!(format == GL_RGBA			&& type == GL_FLOAT))
	{
		if (ctx->lastError == GL_NO_ERROR)
			ctx->lastError = GL_INVALID_ENUM;
		return;
	}

	// invalid arguments
	if (width < 0 || height < 0)
	{
		if (ctx->lastError == GL_NO_ERROR)
			ctx->lastError = GL_INVALID_OPERATION;
		return;
	}

	// read to buffer
	if (ctx->pixelPackBufferBufferBinding)
		return;

	// read to use pointer
	{
		const int						targetRowLength		= (ctx->pixelPackRowLength != 0) ? (ctx->pixelPackRowLength) : (width);
		const int						targetSkipRows		= ctx->pixelPackSkipRows;
		const int						targetSkipPixels	= ctx->pixelPackSkipPixels;
		const int						infiniteHeight		= targetSkipRows + height; // as much as needed
		const int						targetRowPitch		= (ctx->pixelPackAlignment == 0) ? (targetRowLength * transferFormat.getPixelSize()) : (deAlign32(targetRowLength * transferFormat.getPixelSize(), ctx->pixelPackAlignment));

		// Create access to the whole copy target
		const tcu::PixelBufferAccess	targetAccess		(transferFormat, targetRowLength, infiniteHeight, 1, targetRowPitch, 0, pixels);

		// Select (skip_pixels, skip_rows, width, height) subregion from it. Clip to horizontal boundaries
		const tcu::PixelBufferAccess	targetRectAccess	= tcu::getSubregion(targetAccess,
																				de::clamp(targetSkipPixels, 0, targetAccess.getWidth()-1),
																				targetSkipRows,
																				de::clamp(width, 0, de::max(0, targetAccess.getWidth() - targetSkipPixels)),
																				height);

		tcu::clear(targetRectAccess, clearColor);
	}
}

GLW_APICALL void GLW_APIENTRY glBindBuffer (GLenum target, GLuint buffer)
{
	Context* const ctx = getCurrentContext();

	if (target == GL_PIXEL_PACK_BUFFER)
		ctx->pixelPackBufferBufferBinding = buffer;
}

GLW_APICALL void GLW_APIENTRY glDeleteBuffers (GLsizei n, const GLuint* buffers)
{
	Context* const ctx = getCurrentContext();

	for (GLsizei ndx = 0; ndx < n; ++ndx)
		if (buffers[ndx] && buffers[ndx] == ctx->pixelPackBufferBufferBinding)
			ctx->pixelPackBufferBufferBinding = 0;
}

GLW_APICALL GLint GLW_APIENTRY glGetAttribLocation (GLuint program, const GLchar* name)
{
	DE_UNREF(program);
	return (GLint)(deStringHash(name) & 0x7FFFFFFF);
}

void initFunctions (glw::Functions* gl)
{
#	include "tcuNullRenderContextInitFuncs.inl"
}

static tcu::RenderTarget toRenderTarget (const RenderConfig& renderCfg)
{
	const int		width			= getValueOrDefault(renderCfg, &RenderConfig::width,		256);
	const int		height			= getValueOrDefault(renderCfg, &RenderConfig::height,		256);
	const int		redBits			= getValueOrDefault(renderCfg, &RenderConfig::redBits,		8);
	const int		greenBits		= getValueOrDefault(renderCfg, &RenderConfig::greenBits,	8);
	const int		blueBits		= getValueOrDefault(renderCfg, &RenderConfig::blueBits,		8);
	const int		alphaBits		= getValueOrDefault(renderCfg, &RenderConfig::alphaBits,	8);
	const int		depthBits		= getValueOrDefault(renderCfg, &RenderConfig::depthBits,	24);
	const int		stencilBits		= getValueOrDefault(renderCfg, &RenderConfig::stencilBits,	8);
	const int		numSamples		= getValueOrDefault(renderCfg, &RenderConfig::numSamples,	0);

	return tcu::RenderTarget(width, height, tcu::PixelFormat(redBits, greenBits, blueBits, alphaBits), depthBits, stencilBits, numSamples);
}

RenderContext::RenderContext (const RenderConfig& renderCfg)
	: m_ctxType			(renderCfg.type)
	, m_renderTarget	(toRenderTarget(renderCfg))
	, m_context			(DE_NULL)
{
	m_context = new Context(m_ctxType);

	initFunctions(&m_functions);
	setCurrentContext(m_context);
}

RenderContext::~RenderContext (void)
{
	setCurrentContext(DE_NULL);
	delete m_context;
}

void RenderContext::postIterate (void)
{
}

} // null
} // tcu