/*
								+--------------------------------+
								|                                |
								|  ***   OpenGL ES shader   ***  |
								|                                |
								|  Copyright © -tHE SWINe- 2013  |
								|                                |
								|           Shader.cpp           |
								|                                |
								+--------------------------------+
*/

/**
 *	@file gles2/Shader.cpp
 *	@author -tHE SWINe-
 *	@date 2013
 *	@brief OpenGL ES 2.0 shaders
 */

#include "../NewFix.h"
#include "../CallStack.h"
#include "Shader.h"
#include "../StlUtils.h"

/*
 *								=== CGLESShaderStringTable ===
 */

class CGLESShaderStringTable {
public:
	static const char *p_s_Core_Shader_String()
	{
		return "=== shader (OpenGL ES core) ===\n";
	}

	static const char *p_s_Core_VertexShader_String()
	{
		return "=== vertex shader (OpenGL ES core) ===\n";
	}

	static const char *p_s_Core_TesselationControlShader_String()
	{
		return "=== tesselation control shader (OpenGL ARB) ===\n";
	}

	static const char *p_s_Core_TesselationEvaluationShader_String()
	{
		return "=== tesselation evaluation shader (OpenGL ARB) ===\n";
	}

	static const char *p_s_Core_GeometryShader_String()
	{
		return "=== geometry shader (OpenGL ARB) ===\n";
	}

	static const char *p_s_Core_FragmentShader_String()
	{
		return "=== fragment shader (OpenGL ES core) ===\n";
	}
};

/*
 *								=== ~CGLESShaderStringTable ===
 */

/*
 *								=== CGLESShader ===
 */

CGLESShader::CGLESShader()
	:m_n_program_object(0)

#ifdef __GLES20SHADER_HOLD_SHADER_OBJECTS
	, m_n_vs_object(0), m_n_tcs_object(0), m_n_tes_object(0),
	m_n_gs_object(0), m_n_fs_object(0)
#endif // __GLES20SHADER_HOLD_SHADER_OBJECTS

#ifdef __GLES20SHADER_HOLD_STATUS_INFO
	, m_b_compiled(false), m_b_linked(false)
#endif // __GLES20SHADER_HOLD_STATUS_INFO
{}

CGLESShader::~CGLESShader()
{
	Delete();
}

bool CGLESShader::Compile(const char *p_s_vertex_shader,
	/*const char *p_s_tess_control_shader, const char *p_s_tess_evalutaion_shader,
	const char *p_s_geometry_shader,*/ const char *p_s_fragment_shader,
	std::string &r_s_info_log)
{
	r_s_info_log.erase();

	//Delete(); // don't delete, the program object may contain some config already

	if(b_Initialized()) {
#ifdef __GLES20SHADER_HOLD_SHADER_OBJECTS
#ifdef _DEBUG
		int n_temp;
		if(m_n_vs_object) {
			glGetShaderiv(m_n_vs_object, GL_DELETE_STATUS, &n_temp);
			_ASSERTE(n_temp == GL_TRUE);
		}
		/*if(m_n_tcs_object) {
			glGetShaderiv(m_n_tcs_object, GL_DELETE_STATUS, &n_temp);
			_ASSERTE(n_temp == GL_TRUE);
		}
		if(m_n_tes_object) {
			glGetShaderiv(m_n_tes_object, GL_DELETE_STATUS, &n_temp);
			_ASSERTE(n_temp == GL_TRUE);
		}
		if(m_n_gs_object) {
			glGetShaderiv(m_n_gs_object, GL_DELETE_STATUS, &n_temp);
			_ASSERTE(n_temp == GL_TRUE);
		}*/
		if(m_n_fs_object) {
			glGetShaderiv(m_n_fs_object, GL_DELETE_STATUS, &n_temp);
			_ASSERTE(n_temp == GL_TRUE);
		}
#endif // _DEBUG
		// check first (before they are deleted)

		if(m_n_vs_object)
			glDetachShader(m_n_program_object, m_n_vs_object);
		/*if(m_n_tcs_object)
			glDetachShader(m_n_program_object, m_n_tcs_object);
		if(m_n_tes_object)
			glDetachShader(m_n_program_object, m_n_tes_object);
		if(m_n_gs_object)
			glDetachShader(m_n_program_object, m_n_gs_object);*/
		if(m_n_fs_object)
			glDetachShader(m_n_program_object, m_n_fs_object);
		// detach

		m_n_vs_object = 0;
		/*m_n_tcs_object = 0;
		m_n_tes_object = 0;
		m_n_gs_object = 0;*/
		m_n_fs_object = 0;
		// clear the members
#else // __GLES20SHADER_HOLD_SHADER_OBJECTS
		std::vector<GLuint> shader_list;
		if(!Get_ShaderList(shader_list))
			return false;
		// get shader list

		for(size_t i = 0, n = shader_list.size(); i < n; ++ i) {
			int n_deletion_marker;
			glGetShaderiv(shader_list[i], GL_DELETE_STATUS, &n_deletion_marker);
			_ASSERTE(n_deletion_marker == GL_TRUE);
			// makes sure the shader will be deleted eventually

			glDetachShader(m_n_program_object, shader_list[i]);
			// detach
		}
		// get shaders for a program
#endif // __GLES20SHADER_HOLD_SHADER_OBJECTS
	}
	// detach the shaders instead

	if(!b_Initialized() && !(m_n_program_object = glCreateProgram()))
		return false;
	// there should've been (an empty) program object

	const GLenum p_target[] = {
		GL_VERTEX_SHADER,
		//GL_TESS_CONTROL_SHADER,
		//GL_TESS_EVALUATION_SHADER,
		//GL_GEOMETRY_SHADER,
		GL_FRAGMENT_SHADER
	};
	const char *p_source_list[] = {
		p_s_vertex_shader,
		//p_s_tess_control_shader,
		//p_s_tess_evalutaion_shader,
		//p_s_geometry_shader,
		p_s_fragment_shader
	};
	const char *p_header_list[] = {
		CGLESShaderStringTable::p_s_Core_VertexShader_String(),
		//CGLESShaderStringTable::p_s_Core_TesselationControlShader_String(),
		//CGLESShaderStringTable::p_s_Core_TesselationEvaluationShader_String(),
		//CGLESShaderStringTable::p_s_Core_GeometryShader_String(),
		CGLESShaderStringTable::p_s_Core_FragmentShader_String()
	};

#ifndef __GLES20SHADER_HOLD_SHADER_OBJECTS
	GLuint m_n_vs_object, /*m_n_tcs_object,
		m_n_tes_object, m_n_gs_object,*/ m_n_fs_object;
#endif // !__GLES20SHADER_HOLD_SHADER_OBJECTS

	GLuint *p_shader_object[] = {
		&m_n_vs_object,
		/*&m_n_tcs_object,
		&m_n_tes_object,
		&m_n_gs_object,*/
		&m_n_fs_object
	};
	const size_t n_programmable_stage_num = sizeof(p_target) / sizeof(p_target[0]);
	_ASSERTE(sizeof(p_source_list) / sizeof(p_source_list[0]) == n_programmable_stage_num);
	_ASSERTE(sizeof(p_header_list) / sizeof(p_header_list[0]) == n_programmable_stage_num);
	_ASSERTE(sizeof(p_shader_object) / sizeof(p_shader_object[0]) == n_programmable_stage_num);

	for(size_t n_unit = 0; n_unit < n_programmable_stage_num; ++ n_unit) {
		const char *p_s_source = p_source_list[n_unit];
		GLenum n_target = p_target[n_unit];
		if(*p_shader_object[n_unit] = (p_s_source)? glCreateShader(n_target) : 0) {
			GLuint n_shader_object = *p_shader_object[n_unit];
			int n_src_length = int(strlen(p_s_source));
			glShaderSource(n_shader_object, 1, &p_s_source, &n_src_length);
			glCompileShader(n_shader_object);
			// compile ...

			int n_tmp;
			glGetShaderiv(n_shader_object, GL_COMPILE_STATUS, &n_tmp);
			bool b_compiled = n_tmp == GL_TRUE;
			int n_log_length;
			glGetShaderiv(n_shader_object, GL_INFO_LOG_LENGTH, &n_log_length);
			// query status ...

			if(n_log_length > 1) {
				char *p_s_temp_info_log;
				if(!(p_s_temp_info_log = new(std::nothrow) char[n_log_length + 1]))
					return false;
				// alloc temp storage for log string

				glGetShaderInfoLog(n_shader_object, n_log_length, &n_log_length, p_s_temp_info_log);
				// get log string

				if((!r_s_info_log.empty() && !stl_ut::AppendCStr(r_s_info_log, "\n\n")) ||
				   !stl_ut::AppendCStr(r_s_info_log, p_header_list[n_unit]) ||
				   !stl_ut::AppendCStr(r_s_info_log, p_s_temp_info_log)) {
					delete[] p_s_temp_info_log;
					return false;
				}
				// append log string

				delete[] p_s_temp_info_log;
				// delete temp storage
			}
			// get/concat info-log

			{
				glAttachShader(m_n_program_object, n_shader_object);
				// attach shaders to the program object

				glDeleteShader(n_shader_object);
				// mark the shaders for deletion (the shaders aren't deleted until the program object is)
			}
			// do this right away so if there are compile errors, it still deletes properly

			if(!b_compiled)
				return false;
		}
		// compile shader
	}
	// compile shaders for different units in loop (should be less error prone)

#ifdef __GLES20SHADER_HOLD_STATUS_INFO
	m_b_compiled = true;
#else // __GLES20SHADER_HOLD_STATUS_INFO
	_ASSERTE(b_Compiled());
#endif // __GLES20SHADER_HOLD_STATUS_INFO

	return true;
}

bool CGLESShader::CompileConfigureLink(const char *p_s_vertex_shader,
	//const char *p_s_tesselation_control_shader,
	//const char *p_s_tesselation_evaluation_shader,
	//const char *p_s_geometry_shader,
	const char *p_s_fragment_shader,
	const char *p_s_config, //bool b_want_separable_program,
	//bool b_want_retrievable_binary,
	std::string &r_s_compile_log,
	std::string &r_s_configure_log, std::string &r_s_link_log,
	bool b_stderr_output)
{
	r_s_compile_log.erase();
	r_s_configure_log.erase();
	r_s_link_log.erase();

	//Delete(); // don't delete, the program object may contain some config already; Compile() will do the job

	if(!Compile(p_s_vertex_shader, /*p_s_tesselation_control_shader,
	   p_s_tesselation_evaluation_shader, p_s_geometry_shader,*/
	   p_s_fragment_shader, r_s_compile_log)) {
		if(b_stderr_output)
			fprintf(stderr, "error: while compiling shader:\n%s\n", r_s_compile_log.c_str());
		return false;
	}
#ifdef _DEBUG
	if(b_stderr_output && !r_s_compile_log.empty())
		fprintf(stderr, "warning: while compiling shader:\n%s\n", r_s_compile_log.c_str());
#endif // _DEBUG
	// call the compiler

	if(p_s_config) {
		if(!Configure(p_s_config, r_s_configure_log)) {
			if(b_stderr_output)
				fprintf(stderr, "error: while configuring shader:\n%s\n", r_s_configure_log.c_str());
			return false;
		}
#ifdef _DEBUG
		if(b_stderr_output && !r_s_configure_log.empty())
			fprintf(stderr, "warning: while configuring shader:\n%s\n", r_s_configure_log.c_str());
#endif // _DEBUG
	}
	// configure the shader

#if 0 // not (yet) in OpenGL ES 2.0
	if(b_want_separable_program != b_Get_Program_Separable()) {
		if(!Set_Program_Separable(b_want_separable_program))
			return false;
	}
	if(b_want_retrievable_binary != b_Get_Program_BinaryRetrievable()) {
		if(!Set_Program_BinaryRetrievable(b_want_retrievable_binary))
			return false;
	}
#endif // 0
	// set separable / retrievable binary flags

	if(!Link(r_s_link_log)) {
		if(b_stderr_output)
			fprintf(stderr, "error: while linking shader:\n%s\n", r_s_link_log.c_str());
		return false;
	}
#ifdef _DEBUG
	if(b_stderr_output && !r_s_link_log.empty())
		fprintf(stderr, "warning: while linking shader:\n%s\n", r_s_link_log.c_str());
#endif // _DEBUG
	// call the linker

	return true;
}

bool CGLESShader::Configure(const char *p_s_config, std::string &r_s_configure_log)
{
	if(!b_Initialized() && !(m_n_program_object = glCreateProgram()))
		return false;

	//r_s_configure_log.erase(); // not necessary
	stl_ut::AssignCStr(r_s_configure_log, CGLESShaderStringTable::p_s_Core_Shader_String());
	// begin with shader string

	std::string s_temp;
	if(strstr(p_s_config, "//") || strstr(p_s_config, "/*")) {
		try {
			s_temp = p_s_config;
			// copy the original string

			{
				size_t n_pos = 0;
				while((n_pos = s_temp.find("//", n_pos)) != std::string::npos) {
					size_t n_end = s_temp.find("\n", n_pos + 2);
					if(n_end == std::string::npos) {
						stl_ut::AppendCStr(r_s_configure_log,
							"warning: config string: missing the newline at the end\n");
						s_temp.erase(n_pos);
					} else
						s_temp.erase(n_pos, n_end - n_pos); // keep the newline
					++ n_pos;
				}
			}
			// strip the single-line comments

			{
				size_t n_pos = 0;
				while((n_pos = s_temp.find("/*", n_pos)) != std::string::npos) {
					size_t n_end = s_temp.find("*/", n_pos + 2);
					if(n_end == std::string::npos) {
						stl_ut::AppendCStr(r_s_configure_log,
							"warning: config string: end of commend not found\n");
						s_temp.erase(n_pos);
					} else
						s_temp.erase(n_pos, n_end + 2 - n_pos);
					++ n_pos;
				}
			}
			// strip the multi-line comments

			p_s_config = s_temp.c_str();
			// replace the original string by s_temp
		} catch(std::bad_alloc&) {
			return false;
		}
	}
	// a preprocessing step to remove comments (if present)

	size_t b = 0, e = strlen(p_s_config);
	for(;;) {
		while(b < e && isspace(p_s_config[b]))
			++ b;
		// skip space

		if(b == e)
			break;
		// empty config / the end of the config

		size_t n_stage = size_t(-1);
		const char *p_stage_name_list[] = {
			"vertex", "geometry", "fragment", "tesselation-control", "tesselation-evaluation"
		};
		const size_t n_stage_name_num = sizeof(p_stage_name_list) / sizeof(p_stage_name_list[0]);
		for(size_t i = 0; i < n_stage_name_num; ++ i) {
			if(b + strlen(p_stage_name_list[i]) <= e &&
			   !memcmp(p_stage_name_list[i], p_s_config + b,
			   strlen(p_stage_name_list[i]) * sizeof(char))) {
				n_stage = i;
				b += strlen(p_stage_name_list[i]); // skip stage specifier
				break;
			}
		}
		if(n_stage == size_t(-1)) {
			stl_ut::AppendCStr(r_s_configure_log, "error: unexpected token before stage specifier\n");
			return false;
		}
		// find the stage specifier

		while(b < e && isspace(p_s_config[b]))
			++ b;
		// skip space

		if(b == e || p_s_config[b] != '{') {
			stl_ut::AppendCStr(r_s_configure_log, "error: unexpected token before '{'\n");
			return false;
		}
		++ b;
		// skip '{'

		for(;;) {
			while(b < e && isspace(p_s_config[b]))
				++ b;
			// skip space

			if(b == e || !b_IsIdentChar(p_s_config[b], true))
				break;
			// handle unreadable idents / end of section

			size_t n_name_begin = b;
			while(b < e && b_IsIdentChar(p_s_config[b], b == n_name_begin))
				++ b;
			size_t n_name_end = b;
			// skip token name

			while(b < e && isspace(p_s_config[b]))
				++ b;
			// skip space

			if(b == e || p_s_config[b] != ':') {
				stl_ut::AppendCStr(r_s_configure_log, "error: unexpected token before ':'\n");
				return false;
			}
			++ b;
			// skip ':'

			while(b < e && isspace(p_s_config[b]))
				++ b;
			// skip space

			size_t n_value_begin = b;
			while(b < e && b_IsValueChar(p_s_config[b], b == n_name_begin))
				++ b;
			size_t n_value_end = b;
			// skip token value

			while(b < e && isspace(p_s_config[b]))
				++ b;
			// skip space

			if(b == e || p_s_config[b] != ';') {
				stl_ut::AppendCStr(r_s_configure_log, "error: unexpected token before ';'\n");
				return false;
			}
			++ b;
			// skip ';'

			std::string s_name, s_value;
			s_name.insert(s_name.begin(), p_s_config + n_name_begin, p_s_config + n_name_end);
			s_value.insert(s_value.begin(), p_s_config + n_value_begin, p_s_config + n_value_end);
			// make strings

			switch(n_stage) {
			case 0: // vertex
				if(!b_IsInteger(s_value)) {
					stl_ut::AppendCStr(r_s_configure_log,
						"error: expected integer value after vertex attribute name\n");
					return false;
				}
				if(!Bind_Attribute_Location(atoi(s_value.c_str()), s_name.c_str())) {
					stl_ut::AppendCStr(r_s_configure_log, "error: BindAttribLocation(");
					stl_ut::AppendCStr(r_s_configure_log, s_value.c_str());
					stl_ut::AppendCStr(r_s_configure_log, ", \"");
					stl_ut::AppendCStr(r_s_configure_log, s_name.c_str());
					stl_ut::AppendCStr(r_s_configure_log, "\") failed\n");
					return false;
				}
				// bind attrib location
				break;

			case 1: // geometry
#if 0 // not (yet) in OpenGL ES 2.0
				{
					bool b_input;
					if((b_input = !s_name.compare("input-type")) || !s_name.compare("output-type")) {
						GLenum n_value = 0;
						const struct {
							const char *p_s_value;
							GLenum n_value;
						} p_glenum_table[] = {
							{"GL_POINTS", GL_POINTS},
							{"GL_LINES", GL_LINES},
							{"GL_LINES_ADJACENCY_EXT", GL_LINES_ADJACENCY},
							{"GL_LINES_ADJACENCY", GL_LINES_ADJACENCY},
							{"GL_TRIANGLES", GL_TRIANGLES},
							{"GL_TRIANGLES_ADJACENCY_EXT", GL_TRIANGLES_ADJACENCY},
							{"GL_TRIANGLES_ADJACENCY", GL_TRIANGLES_ADJACENCY},
							{"GL_LINE_STRIP", GL_LINE_STRIP},
							{"GL_TRIANGLE_STRIP", GL_TRIANGLE_STRIP}
						};
						const size_t n_glenum_table_size = sizeof(p_glenum_table) / sizeof(p_glenum_table[0]);
						for(size_t i = 0; i < n_glenum_table_size; ++ i) {
							if(!s_value.compare(p_glenum_table[i].p_s_value)) {
								n_value = p_glenum_table[i].n_value;
								_ASSERTE(n_value); // makes sure 0 is not used in the table (sentinell value)
								break;
							}
						}
						if(!n_value) {
							stl_ut::AppendCStr(r_s_configure_log, "error: unknown primitive type: ");
							stl_ut::AppendCStr(r_s_configure_log, s_value.c_str());
							stl_ut::AppendCStr(r_s_configure_log, " after ");
							stl_ut::AppendCStr(r_s_configure_log, s_name.c_str());
							stl_ut::AppendCStr(r_s_configure_log, "\n");
							return false;
						}
						// parse primitive type

						if((b_input && !Set_Geometry_InputType(n_value)) ||
						   (!b_input && !Set_Geometry_OutputType(n_value))) {
							stl_ut::AppendCStr(r_s_configure_log, "error: ");
							stl_ut::AppendCStr(r_s_configure_log, (b_input)?
								"SetGeometry_InputType" : "SetGeometry_OutputType");
							stl_ut::AppendCStr(r_s_configure_log, "(");
							stl_ut::AppendCStr(r_s_configure_log, s_value.c_str());
							stl_ut::AppendCStr(r_s_configure_log, ") failed\n");
							return false;
						}
						// set geometry type
					} else if(!s_name.compare("max-vertices")) {
						if(!b_IsInteger(s_value)) {
							stl_ut::AppendCStr(r_s_configure_log,
								"error: expected integer value after max-verts-out\n");
							return false;
						}
						if(!Set_Geometry_VerticesOut(atoi(s_value.c_str()))) {
							stl_ut::AppendCStr(r_s_configure_log, "error: SetGeometry_VerticesOut(");
							stl_ut::AppendCStr(r_s_configure_log, s_value.c_str());
							stl_ut::AppendCStr(r_s_configure_log, ") failed\n");
							return false;
						}
						// set max_vertices
					} else if(!s_name.compare("invocations")) {
						if(!b_IsInteger(s_value)) {
							stl_ut::AppendCStr(r_s_configure_log,
								"error: expected integer value after invocations\n");
							return false;
						}
						if(atoi(s_value.c_str()) != 1) {
							stl_ut::AppendCStr(r_s_configure_log, "error: invocations must be 1: is \n");
							stl_ut::AppendCStr(r_s_configure_log, s_value.c_str());
							stl_ut::AppendCStr(r_s_configure_log, "\n");
							return false;
						}
						// @todo - adjust the number of invocations to the program object once it's possible
						// set invocations
					} else {
						stl_ut::AppendCStr(r_s_configure_log, "error: unexpected token \'");
						stl_ut::AppendCStr(r_s_configure_log, s_name.c_str());
						stl_ut::AppendCStr(r_s_configure_log, "\' before geometry stage specifier\n");
						return false;
					}
				}
#endif // 0
				// geometry specification ignoret in OpenGL ES at the moment
				break;

			case 2: // fragment
#if 0 // not (yet) in OpenGL ES 2.0
				if(!b_IsInteger(s_value)) {
					stl_ut::AppendCStr(r_s_configure_log,
						"error: expected integer value after fragment output name\n");
					return false;
				}
				if(!Bind_FragData_Location(atoi(s_value.c_str()), s_name.c_str())) {
					stl_ut::AppendCStr(r_s_configure_log, "error: BindFragDataLocation(");
					stl_ut::AppendCStr(r_s_configure_log, s_value.c_str());
					stl_ut::AppendCStr(r_s_configure_log, ", \"");
					stl_ut::AppendCStr(r_s_configure_log, s_name.c_str());
					stl_ut::AppendCStr(r_s_configure_log, "\") failed\n");
					return false;
				}
				// bind fragment data location (unable to bind index here (GL_ARB_blend_func_extended): @todo)
#endif // 0
				break;

			case 3: // tesselation-control
#if 0 // not (yet) in OpenGL ES 2.0
				if(!s_name.compare("vertices-out")) {
					stl_ut::AppendCStr(r_s_configure_log, "error: tess-control vertices out must be specified in shader code\n");
					// otherwise it would be an integer
				} else if(!s_name.compare("mode")) {
					stl_ut::AppendCStr(r_s_configure_log, "error: tess-control mode must be specified in shader code\n");
					// otherwise it would be one of GL_POINTS|GL_LINES|GL_TRIANGLES
				} else {
					stl_ut::AppendCStr(r_s_configure_log, "error: unknown tess-control token: \'");
					stl_ut::AppendCStr(r_s_configure_log, s_name.c_str());
					stl_ut::AppendCStr(r_s_configure_log, "\'\n");
				}
				return false;
#endif // 0
				break;

			case 4: // tesselation-evaluation
#if 0 // not (yet) in OpenGL ES 2.0
				if(!s_name.compare("spacing")) {
					stl_ut::AppendCStr(r_s_configure_log, "error: tess-evaluation spacing must be specified in shader code\n");
					// otherwise it would be one of GL_EQUAL|GL_FRACTIONAL_EVEN|GL_FRACTIONAL_ODD
				} else if(!s_name.compare("vertex-order")) {
					stl_ut::AppendCStr(r_s_configure_log, "error: tess-evaluation vertex-order must be specified in shader code\n");
					// otherwise it would be one of GL_CW|GL_CCW
				} else if(!s_name.compare("point-mode")) {
					stl_ut::AppendCStr(r_s_configure_log, "error: tess-evaluation point-mode must be specified in shader code\n");
					// otherwise it would be one of GL_TRUE|GL_FALSE
				} else {
					stl_ut::AppendCStr(r_s_configure_log, "error: unknown tess-evaluation token: \'");
					stl_ut::AppendCStr(r_s_configure_log, s_name.c_str());
					stl_ut::AppendCStr(r_s_configure_log, "\'\n");
				}
				return false;
#endif // 0
				break;

			default:
				_ASSERTE(n_stage == -1); // should not happen (undefined stage number)
				break;
			}
		}
		// process all the "name: value;" pairs

		if(b == e || p_s_config[b] != '}') {
			stl_ut::AppendCStr(r_s_configure_log, "error: unexpected token before '}'\n");
			return false;
		}
		++ b;
		// skip '}'
	}

	if(!r_s_configure_log.compare(CGLESShaderStringTable::p_s_Core_Shader_String()))
		r_s_configure_log.erase();
	// in case there is the header only, erase it

	return true;
}

#if 0 // not (yet) in OpenGL ES 2.0
bool CGLESShader::FromBinary(size_t n_size, GLenum n_binary_format,
	const void *p_binary, std::string &r_s_info_log)
{
	_ASSERTE(GLEH_ARB_get_program_binary);

	r_s_info_log.erase();

	Delete(); // can delete, the program object config will be overwritten in glProgramBinary()

	if(!(m_n_program_object = glCreateProgram()))
		return false;

	_ASSERTE(n_size <= CMaxIntValue<GLsizei>::result());
	glProgramBinary(m_n_program_object, n_binary_format, p_binary, GLsizei(n_size));
	// create the program from binary

	int n_tmp;
	glGetProgramiv(m_n_program_object, GL_LINK_STATUS, &n_tmp);
#ifdef __GLES20SHADER_HOLD_STATUS_INFO
	//m_b_compiled = m_b_linked = n_tmp == GL_TRUE;
#else // __GLES20SHADER_HOLD_STATUS_INFO
	bool m_b_linked = n_tmp == GL_TRUE;
#endif // __GLES20SHADER_HOLD_STATUS_INFO
	// get link status

	int n_length;
	glGetProgramiv(m_n_program_object, GL_INFO_LOG_LENGTH, &n_length);
	if(n_length > 1) {
		char *p_s_temp_info_log;
		if(!(p_s_temp_info_log = new(std::nothrow) char[n_length + 1]))
			return false;
		// alloc temp log

		glGetProgramInfoLog(m_n_program_object, n_length, &n_length, p_s_temp_info_log);
		// get info log

		const char *p_s_header = CGLESShaderStringTable::p_s_Core_Shader_String();
		if(!stl_ut::AssignCStr(r_s_info_log, p_s_header) ||
		   !stl_ut::AppendCStr(r_s_info_log, p_s_temp_info_log)) {
			delete[] p_s_temp_info_log;
			return false;
		}
		// concatenate strings

		delete[] p_s_temp_info_log;
		// release temp log
	}
	// get info-log

	std::vector<GLuint> shader_list;
	if(!Get_ShaderList(shader_list))
		return false;
	// get shader list and shader count

	for(size_t i = 0, n = shader_list.size(); i < n; ++ i) {
		GLuint n_shader = shader_list[i];
		// get a shader

#ifdef __GLES20SHADER_HOLD_SHADER_OBJECTS
		int n_type;
		glGetShaderiv(n_shader, GL_SHADER_TYPE, &n_type);
		// determine it's type

		switch(n_type) {
		case GL_VERTEX_SHADER:
			_ASSERTE(!m_n_vs_object);
			m_n_vs_object = n_shader;
			break;
		case GL_TESS_CONTROL_SHADER:
			_ASSERTE(!m_n_tcs_object);
			m_n_tcs_object = n_shader;
			break;
		case GL_TESS_EVALUATION_SHADER:
			_ASSERTE(!m_n_tes_object);
			m_n_tes_object = n_shader;
			break;
		case GL_GEOMETRY_SHADER:
			_ASSERTE(!m_n_gs_object);
			m_n_gs_object = n_shader;
			break;
		case GL_FRAGMENT_SHADER:
			_ASSERTE(!m_n_fs_object);
			m_n_fs_object = n_shader;
			break;
		}
		// assign it to a member variable (if stored)
#endif // __GLES20SHADER_HOLD_SHADER_OBJECTS

		int n_deletion_marker;
		glGetShaderiv(n_shader, GL_DELETE_STATUS, &n_deletion_marker);
		if(n_deletion_marker != GL_TRUE)
			glDeleteShader(n_shader);
		// makes sure the shader will be deleted eventually
	}
	// get shaders for a program

	return m_b_linked;
}

size_t CGLESShader::n_Get_Program_Binary_Size() const
{
	_ASSERTE(b_Compiled() && b_Linked());

	return n_Get_Quantity(GL_PROGRAM_BINARY_LENGTH);
}

void CGLESShader::Get_Program_Binary(size_t &r_n_size, GLenum &r_n_binary_format, void *p_binary)
{
	_ASSERTE(GLEH_ARB_get_program_binary);
	_ASSERTE(b_Initialized() && b_Compiled() && b_Linked());

	GLsizei n_size;
	_ASSERTE(r_n_size <= CMaxIntValue<GLsizei>::result());
	glGetProgramBinary(m_n_program_object, GLsizei(r_n_size), &n_size, &r_n_binary_format, p_binary);
	_ASSERTE(n_size <= SIZE_MAX);
	r_n_size = n_size;
}

bool CGLESShader::Set_Program_Separable(bool b_separable)
{
	if(!b_Initialized() && !(m_n_program_object = glCreateProgram()))
		return false;

	glProgramParameteri(m_n_program_object, GL_PROGRAM_SEPARABLE,
		(b_separable)? GL_TRUE : GL_FALSE);

	return true;
}

bool CGLESShader::Set_Program_BinaryRetrievable(bool b_retrievable)
{
	if(!b_Initialized() && !(m_n_program_object = glCreateProgram()))
		return false;

	glProgramParameteri(m_n_program_object, GL_PROGRAM_BINARY_RETRIEVABLE_HINT,
		(b_retrievable)? GL_TRUE : GL_FALSE);

	return true;
}

bool CGLESShader::b_Get_Program_Separable() const
{
	return b_Get_State(GL_PROGRAM_SEPARABLE);
}

bool CGLESShader::b_Get_Program_BinaryRetrievable() const
{
	return b_Get_State(GL_PROGRAM_BINARY_RETRIEVABLE_HINT);
}
#endif // 0

bool CGLESShader::Bind_Attribute_Location(int n_location, const char *p_s_attrib_name)
{
	if(!b_Initialized() && !(m_n_program_object = glCreateProgram()))
		return false;
	// make sure there is a program object

	glBindAttribLocation(m_n_program_object, n_location, p_s_attrib_name);

	return true;//glGetError() == GL_NO_ERROR;
}

#if 0 // not (yet) in OpenGL ES 2.0
bool CGLESShader::Bind_FragData_Location(int n_color_number, const char *p_s_output_name)
{
	if(!b_Initialized() && !(m_n_program_object = glCreateProgram()))
		return false;
	// make sure there is a program object

	glBindFragDataLocation(m_n_program_object, n_color_number, p_s_output_name);

	return true;//glGetError() == GL_NO_ERROR;
}

bool CGLESShader::Bind_FragData_Location(int n_color_number, int n_index, const char *p_s_output_name)
{
	_ASSERTE(GLEH_ARB_blend_func_extended);
	if(!b_Initialized() && !(m_n_program_object = glCreateProgram()))
		return false;
	// make sure there is a program object

	glBindFragDataLocationIndexed(m_n_program_object, n_color_number, n_index, p_s_output_name);

	return true;//glGetError() == GL_NO_ERROR;
}

bool CGLESShader::Set_Geometry_InputType(GLenum n_input_type)
{
	if(!b_Initialized() && !(m_n_program_object = glCreateProgram()))
		return false;
	// make sure there is a program object

	glProgramParameteri(m_n_program_object, GL_GEOMETRY_INPUT_TYPE, n_input_type);

	return true;//glGetError() == GL_NO_ERROR;
}

bool CGLESShader::Set_Geometry_OutputType(GLenum n_output_type)
{
	if(!b_Initialized() && !(m_n_program_object = glCreateProgram()))
		return false;
	// make sure there is a program object

	glProgramParameteri(m_n_program_object, GL_GEOMETRY_OUTPUT_TYPE, n_output_type);

	return true;//glGetError() == GL_NO_ERROR;
}

bool CGLESShader::Set_Geometry_VerticesOut(int n_max_vertex_num)
{
	if(!b_Initialized() && !(m_n_program_object = glCreateProgram()))
		return false;
	// make sure there is a program object

	glProgramParameteri(m_n_program_object, GL_GEOMETRY_VERTICES_OUT, n_max_vertex_num);

	return true;//glGetError() == GL_NO_ERROR;
}
#endif // 0

bool CGLESShader::Link(std::string &r_s_info_log)
{
	r_s_info_log.erase();

	_ASSERTE(b_Compiled() /*&& !b_Linked()*/); // can want to relink

	glLinkProgram(m_n_program_object);
	int n_tmp;
	glGetProgramiv(m_n_program_object, GL_LINK_STATUS, &n_tmp);
#ifdef __GLES20SHADER_HOLD_STATUS_INFO
	m_b_linked = n_tmp == GL_TRUE;
#else // __GLES20SHADER_HOLD_STATUS_INFO
	bool m_b_linked = n_tmp == GL_TRUE;
#endif // __GLES20SHADER_HOLD_STATUS_INFO
	// link

	int n_length;
	glGetProgramiv(m_n_program_object, GL_INFO_LOG_LENGTH, &n_length);
	if(n_length > 1) {
		char *p_s_temp_info_log;
		if(!(p_s_temp_info_log = new(std::nothrow) char[n_length + 1]))
			return false;
		// alloc temp log

		glGetProgramInfoLog(m_n_program_object, n_length, &n_length, p_s_temp_info_log);
		// get info log

		const char *p_s_header = CGLESShaderStringTable::p_s_Core_Shader_String();
		if(!stl_ut::AssignCStr(r_s_info_log, p_s_header) ||
		   !stl_ut::AppendCStr(r_s_info_log, p_s_temp_info_log)) {
			delete[] p_s_temp_info_log;
			return false;
		}
		// concatenate strings

		delete[] p_s_temp_info_log;
		// release temp log
	}
	// get info-log

	return m_b_linked;
}

/*int CGLESShader::n_Uniform_Num() const
{
	_ASSERTE(b_Compiled() && b_Linked()); // must be compiled and linked
	_ASSERTE(b_Initialized()); // should be there by now

	int n_uniform_num;
	glGetProgramiv(m_n_program_object, GL_ACTIVE_UNIFORMS, &n_uniform_num);

	return /*(glGetError() == GL_NO_ERROR)?* / n_uniform_num /*: -1* /;
}

char *CGLESShader::p_s_Uniform_Name(int n_index, GLenum &r_n_content_type, int &r_n_size) const
{
	_ASSERTE(b_Compiled() && b_Linked()); // must be compiled and linked
	_ASSERTE(b_Initialized()); // should be there by now

	int n_max_name_length;
	glGetProgramiv(m_n_program_object,
		GL_ACTIVE_UNIFORM_MAX_LENGTH, &n_max_name_length);

	char *p_s_temp_name;
	if(!(p_s_temp_name = new(std::nothrow) char[n_max_name_length + 1]))
		return 0;

	int n_length;
	int n_size;
	GLenum n_type;
	glGetActiveUniform(m_n_program_object, n_index, n_max_name_length, &n_length,
		&n_size, &n_type, p_s_temp_name);

	r_n_size = n_size;
	r_n_content_type = n_type;

	/*if(glGetError() != GL_NO_ERROR) {
		delete[] p_s_temp_name;
		return 0;
	}* /

	if(strlen(p_s_temp_name) + 1 == n_max_name_length)
		return p_s_temp_name;
	else {
		char *p_s_name;
		if(!(p_s_name = new(std::nothrow) char[strlen(p_s_temp_name) + 1]))
			return p_s_temp_name;
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
		strcpy_s(p_s_name, (strlen(p_s_temp_name) + 1) * sizeof(char), p_s_temp_name);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		strcpy(p_s_name, p_s_temp_name);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		delete[] p_s_temp_name;
		return p_s_name;
	}
}*/

size_t CGLESShader::n_Get_Attribute_Num() const
{
	return n_Get_Quantity(GL_ACTIVE_ATTRIBUTES);
}

bool CGLESShader::Get_Attribute(GLuint n_index, std::string &r_s_name,
	GLenum &r_n_data_type, int &r_n_size) const
{
	_ASSERTE(b_Initialized());

	return Get_VariableName(n_index, r_s_name, r_n_data_type, r_n_size,
		GLFuncPtrName(glGetActiveAttrib), GL_ACTIVE_ATTRIBUTE_MAX_LENGTH);
}

char *CGLESShader::p_s_Get_Attribute(GLuint n_index, GLenum &r_n_data_type, int &r_n_size) const
{
	_ASSERTE(b_Initialized());

	std::string s_temp;
	if(!Get_Attribute(n_index, s_temp, r_n_data_type, r_n_size))
		return 0;
	return _strdup(s_temp.c_str());
}

size_t CGLESShader::n_Get_Uniform_Num() const
{
	return n_Get_Quantity(GL_ACTIVE_UNIFORMS);
}

bool CGLESShader::Get_Uniform(GLuint n_index, std::string &r_s_name,
	GLenum &r_n_data_type, int &r_n_size) const
{
	_ASSERTE(b_Initialized());

	return Get_VariableName(n_index, r_s_name, r_n_data_type, r_n_size,
		GLFuncPtrName(glGetActiveUniform), GL_ACTIVE_UNIFORM_MAX_LENGTH);
}

char *CGLESShader::p_s_Get_Uniform(GLuint n_index, GLenum &r_n_data_type, int &r_n_size) const
{
	_ASSERTE(b_Initialized());

	std::string s_temp;
	if(!Get_Uniform(n_index, s_temp, r_n_data_type, r_n_size))
		return 0;
	return _strdup(s_temp.c_str());
}

int CGLESShader::n_Get_Uniform_Location(const char *p_s_uniform_name) const
{
	_ASSERTE(b_Compiled() && b_Linked()); // must be compiled and linked
	_ASSERTE(b_Initialized()); // should be there by now

	return glGetUniformLocation(m_n_program_object, p_s_uniform_name);
}

#if 0 // not (yet) in OpenGL ES 2.0
int CGLESShader::n_Get_FragData_Location(const char *p_s_output_name) const
{
	_ASSERTE(b_Initialized()); // needs the program object (compile / link status doesn't matter)

	return glGetFragDataLocation(m_n_program_object, p_s_output_name);
}

int CGLESShader::n_Get_FragData_Index(const char *p_s_output_name) const
{
	_ASSERTE(GLEH_ARB_blend_func_extended);
	_ASSERTE(b_Initialized()); // needs the program object (compile / link status doesn't matter)

	return glGetFragDataIndex(m_n_program_object, p_s_output_name);
}

int CGLESShader::n_Get_Geometry_Invocation_Num() const
{
	_ASSERTE(b_Initialized()); // needs the program object (compile / link status doesn't matter)

	return int(n_Get_Quantity(GL_GEOMETRY_SHADER_INVOCATIONS));
}

int CGLESShader::n_Get_Geometry_OutVertex_Num() const
{
	_ASSERTE(b_Initialized()); // needs the program object (compile / link status doesn't matter)

	return int(n_Get_Quantity(GL_GEOMETRY_VERTICES_OUT));
}

GLenum CGLESShader::n_Get_Geometry_InType() const
{
	_ASSERTE(b_Initialized()); // needs the program object (compile / link status doesn't matter)

	return n_Get_Mode(GL_GEOMETRY_INPUT_TYPE);
}

GLenum CGLESShader::n_Get_Geometry_OutType() const
{
	_ASSERTE(b_Initialized()); // needs the program object (compile / link status doesn't matter)

	return n_Get_Mode(GL_GEOMETRY_OUTPUT_TYPE);
}

bool CGLESShader::Set_TransformFeedback_Varyings(size_t n_varying_num,
	const char **p_varying_list, GLenum n_buffer_mode) // call before linking, or have to relink
{
	//_ASSERTE(GLEH_EXT_transform_feedback);
	if(!b_Initialized() && !(m_n_program_object = glCreateProgram()))
		return false;

	_ASSERTE(GLsizei(n_varying_num) == n_varying_num);
	glTransformFeedbackVaryings(m_n_program_object, GLsizei(n_varying_num), p_varying_list, n_buffer_mode);

	return true;
}

GLenum CGLESShader::n_Get_TransformFeedback_BufferMode() const
{
	return n_Get_Mode(GL_TRANSFORM_FEEDBACK_BUFFER_MODE);
}

size_t CGLESShader::n_Get_TransformFeedback_Varying_Num() const
{
	return n_Get_Quantity(GL_TRANSFORM_FEEDBACK_VARYINGS);
}

bool CGLESShader::Get_TransformFeedback_Varying(GLuint n_index, std::string &r_s_name,
	GLenum &r_n_data_type, int &r_n_size) const
{
	//_ASSERTE(GLEH_EXT_transform_feedback);
	_ASSERTE(b_Initialized());

	return Get_VariableName(n_index, r_s_name, r_n_data_type, r_n_size,
		GLFuncPtrName(glGetTransformFeedbackVarying),
		GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH);
}

char *CGLESShader::p_s_Get_TransformFeedback_Varying(GLuint n_index, GLenum &r_n_data_type, int &r_n_size) const
{
	_ASSERTE(b_Initialized());

	std::string s_temp;
	if(!Get_TransformFeedback_Varying(n_index, s_temp, r_n_data_type, r_n_size))
		return 0;
	return _strdup(s_temp.c_str());
}

int CGLESShader::n_Get_TessControl_OutVertex_Num() const
{
	return int(n_Get_Quantity(GL_TESS_CONTROL_OUTPUT_VERTICES));
}

GLenum CGLESShader::n_Get_Tesselation_Mode() const
{
	return n_Get_Mode(GL_TESS_GEN_MODE);
}

GLenum CGLESShader::n_Get_Tesselation_Spacing() const
{
	return GLenum(n_Get_Quantity(GL_TESS_GEN_SPACING));
}

GLenum CGLESShader::n_Get_Tesselation_VertexOrder() const
{
	return GLenum(GLenum(n_Get_Quantity(GL_TESS_GEN_VERTEX_ORDER)));
}

bool CGLESShader::b_Get_Tesselation_PointMode() const
{
	return n_Get_Mode(GL_TESS_GEN_POINT_MODE) == GL_TRUE;
}

GLenum CGLESShader::n_Program_Stages() const
{
	_ASSERTE(b_Initialized());

	GLenum n_result = 0;

#ifdef __GLES20SHADER_HOLD_SHADER_OBJECTS
	if(m_n_vs_object)
		n_result |= GL_VERTEX_SHADER_BIT;
	if(m_n_tcs_object)
		n_result |= GL_TESS_CONTROL_SHADER_BIT;
	if(m_n_tes_object)
		n_result |= GL_TESS_EVALUATION_SHADER_BIT;
	if(m_n_gs_object)
		n_result |= GL_GEOMETRY_SHADER_BIT;
	if(m_n_fs_object)
		n_result |= GL_FRAGMENT_SHADER_BIT;
#else // __GLES20SHADER_HOLD_SHADER_OBJECTS
	std::vector<GLuint> shader_list;
	if(!Get_ShaderList(shader_list))
		return 0;

	for(size_t i = 0, n = shader_list.size(); i < n; ++ i) {
		int n_type;
		glGetShaderiv(shader_list[i], GL_SHADER_TYPE, &n_type);
		// determine it's type

		switch(n_type) {
		case GL_VERTEX_SHADER:
			n_result |= GL_VERTEX_SHADER_BIT;
			break;
		case GL_TESS_CONTROL_SHADER:
			n_result |= GL_TESS_CONTROL_SHADER_BIT;
			break;
		case GL_TESS_EVALUATION_SHADER:
			n_result |= GL_TESS_EVALUATION_SHADER_BIT;
			break;
		case GL_GEOMETRY_SHADER:
			n_result |= GL_GEOMETRY_SHADER_BIT;
			break;
		case GL_FRAGMENT_SHADER:
			n_result |= GL_FRAGMENT_SHADER_BIT;
			break;
		}
	}
#endif // __GLES20SHADER_HOLD_SHADER_OBJECTS

	return n_result;
}
#endif // 0

void CGLESShader::Delete()
{
#ifdef __GLES20SHADER_HOLD_STATUS_INFO
	m_b_compiled = false;
	m_b_linked = false;
#endif // __GLES20SHADER_HOLD_STATUS_INFO

#ifdef __GLES20SHADER_HOLD_SHADER_OBJECTS
	{
#ifdef _DEBUG
		int n_temp;
		if(m_n_vs_object) {
			glGetShaderiv(m_n_vs_object, GL_DELETE_STATUS, &n_temp);
			_ASSERTE(n_temp == GL_TRUE);
		}
		if(m_n_tcs_object) {
			glGetShaderiv(m_n_tcs_object, GL_DELETE_STATUS, &n_temp);
			_ASSERTE(n_temp == GL_TRUE);
		}
		if(m_n_tes_object) {
			glGetShaderiv(m_n_tes_object, GL_DELETE_STATUS, &n_temp);
			_ASSERTE(n_temp == GL_TRUE);
		}
		if(m_n_gs_object) {
			glGetShaderiv(m_n_gs_object, GL_DELETE_STATUS, &n_temp);
			_ASSERTE(n_temp == GL_TRUE);
		}
		if(m_n_fs_object) {
			glGetShaderiv(m_n_fs_object, GL_DELETE_STATUS, &n_temp);
			_ASSERTE(n_temp == GL_TRUE);
		}
#endif // _DEBUG
		// check

		m_n_vs_object = 0;
		m_n_tcs_object = 0;
		m_n_tes_object = 0;
		m_n_gs_object = 0;
		m_n_fs_object = 0;
		// clear the members
	}
	// makes sure the shaders are marked for deletion, clears the variables
#else // __GLES20SHADER_HOLD_SHADER_OBJECTS
#ifdef _DEBUG
	if(!b_Initialized())
		return;

	do {
		std::vector<GLuint> shader_list;
		if(!Get_ShaderList(shader_list))
			break; // not a critical error, just error-checking
		// get shader list

		for(size_t i = 0, n = shader_list.size(); i < n; ++ i) {
			int n_deletion_marker;
			glGetShaderiv(shader_list[i], GL_DELETE_STATUS, &n_deletion_marker);
			_ASSERTE(n_deletion_marker == GL_TRUE);
			// makes sure the shader will be deleted eventually
		}
		// get shaders for a program
	} while(0);
#endif // _DEBUG
#endif // __GLES20SHADER_HOLD_SHADER_OBJECTS

	if(b_Initialized()) {
		glDeleteProgram(m_n_program_object);
		m_n_program_object = 0;
	}
	// just delete the program, the shaders are marked for deletion already
}

template <class CGetFunction>
bool CGLESShader::Get_VariableName(GLuint n_index, std::string &r_s_name,
	GLenum &r_n_data_type, int &r_n_size, CGetFunction func, GLenum n_longest_item_name) const
{
	_ASSERTE(b_Initialized());

	r_s_name.erase();
	// clear the name

	GLsizei n_size;
	GLenum n_type;
	GLsizei n_name_length = GLsizei(n_Get_Quantity(n_longest_item_name));
	// use the longest name for starting point

	if(!stl_ut::Resize_To_N(r_s_name, n_name_length))
		return false;
	// alloc the buffer

	_ASSERTE(r_s_name.length() <= CMaxIntValue<GLsizei>::result());
	func(m_n_program_object, n_index, GLsizei(r_s_name.length()),
		&n_name_length, &n_size, &n_type, &r_s_name[0]);
	if(glGetError() != GL_NO_ERROR)
		return false;
	// get name, data type, size and name length

	if(strlen(r_s_name.c_str()) < r_s_name.length() / 2) {
		std::string s_short;
		if(!stl_ut::AssignCStr(s_short, r_s_name.c_str()))
			r_s_name.resize(strlen(r_s_name.c_str())); // just cut-off the unused part (even though it's quite long)
		else
			r_s_name.swap(s_short); // swap for the short string
	} else
		r_s_name.resize(strlen(r_s_name.c_str())); // cut-off the unused part
	// handle unused space

	r_n_data_type = n_type;
	r_n_size = n_size;
	// copy size and data type

	return true;
}

inline GLenum CGLESShader::n_Get_Mode(GLenum n_name) const
{
	_ASSERTE(b_Initialized());

	int n_mode;
	glGetProgramiv(m_n_program_object, n_name, &n_mode);

	return n_mode;
}

inline size_t CGLESShader::n_Get_Quantity(GLenum n_name) const
{
	_ASSERTE(b_Initialized());

	int n_quantity;
	glGetProgramiv(m_n_program_object, n_name, &n_quantity);
	_ASSERTE(n_quantity >= 0 && n_quantity <= SIZE_MAX);

	return n_quantity;
}

inline bool CGLESShader::b_Get_State(GLenum n_name) const
{
	_ASSERTE(b_Initialized());

	int n_state;
	glGetProgramiv(m_n_program_object, n_name, &n_state);

	return n_state == GL_TRUE;
}

bool CGLESShader::b_IsInteger(const std::string &r_s_string)
{
	if(r_s_string.empty())
		return false;
	for(size_t i = 0, n = r_s_string.length(); i < n; ++ i) {
		if(!isdigit(r_s_string[i]))
			return false;
	}
	return true;
}

bool CGLESShader::b_IsGLEnum(const std::string &r_s_string)
{
	if(r_s_string.length() < 3 || r_s_string[0] != 'G' ||
	   r_s_string[1] != 'L' || r_s_string[2] != '_')
		return false;
	for(size_t i = 3, n = r_s_string.length(); i < n; ++ i) {
		if(r_s_string[i] != '_' && !isalnum(r_s_string[i]))
			return false;
	}
	return true;
}

inline int CGLESShader::b_IsIdentChar(int n_char, bool b_first_char)
{
	return n_char == '_' || (n_char >= 'a' && n_char <= 'z') ||
		(n_char >= 'A' && n_char <= 'Z') ||
		(!b_first_char && (n_char >= '0' && n_char <= '9'));
}

inline int CGLESShader::b_IsValueChar(int n_char, bool b_first_char)
{
	return b_IsIdentChar(n_char, b_first_char) || // ident (GLenum)
		(n_char >= '0' && n_char <= '9') || n_char == '+' || n_char == '-' || // int
		n_char == '.' || n_char == 'e' || n_char == 'E'; // float
}

bool CGLESShader::Get_ShaderList(std::vector<GLuint> &r_shader_list) const
{
	_ASSERTE(b_Initialized());

	GLsizei n_shader_num = GLsizei(n_Get_Quantity(GL_ATTACHED_SHADERS));
	if(!stl_ut::Resize_To_N(r_shader_list, n_shader_num))
		return false;
	_ASSERTE(r_shader_list.size() <= CMaxIntValue<GLsizei>::result());
	glGetAttachedShaders(m_n_program_object, GLsizei(r_shader_list.size()),
		&n_shader_num, &r_shader_list[0]);
	_ASSERTE(r_shader_list.size() == n_shader_num);

	return true;
}

int CGLESShader::n_Vertex_MaxVertexAttrib_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &n_value);
	return n_value;
}

#if 0 // not (yet) in OpenGL ES 2.0
int CGLESShader::n_Vertex_MaxUniformComponent_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &n_value);
	return n_value;
}

int CGLESShader::n_Vertex_MaxOutputComponent_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_VERTEX_OUTPUT_COMPONENTS, &n_value);
	return n_value;
}
#endif // 0

int CGLESShader::n_Vertex_MaxTextureImageUnit_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &n_value);
	return n_value;
}

#if 0 // not (yet) in OpenGL ES 2.0
int CGLESShader::n_MaxVertexStream_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_VERTEX_STREAMS, &n_value);
	return n_value;
}

int CGLESShader::n_TessControl_MaxUniformComponent_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS, &n_value);
	return n_value;
}

int CGLESShader::n_TessControl_MaxOutputComponent_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS, &n_value);
	return n_value;
}

int CGLESShader::n_TessControl_MaxPatchComponent_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_TESS_PATCH_COMPONENTS, &n_value);
	return n_value;
}

int CGLESShader::n_TessControl_MaxTotalOutputComponent_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS, &n_value);
	return n_value;
}

int CGLESShader::n_TessControl_MaxInputComponent_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_TESS_CONTROL_INPUT_COMPONENTS, &n_value);
	return n_value;
}

int CGLESShader::n_TessControl_MaxTextureImageUnit_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS, &n_value);
	return n_value;
}

int CGLESShader::n_TessEvaluation_MaxUniformComponent_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS, &n_value);
	return n_value;
}

int CGLESShader::n_TessEvaluation_MaxInputComponent_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS, &n_value);
	return n_value;
}

int CGLESShader::n_TessEvaluation_MaxOutputComponent_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS, &n_value);
	return n_value;
}

int CGLESShader::n_TessEvaluation_MaxTextureImageUnit_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS, &n_value);
	return n_value;
}

int CGLESShader::n_Geometry_MaxUniformComponent_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_GEOMETRY_UNIFORM_COMPONENTS, &n_value);
	return n_value;
}

int CGLESShader::n_Geometry_MaxOutputComponent_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_COMPONENTS, &n_value);
	return n_value;
}

int CGLESShader::n_Geometry_MaxOutputVertex_Num() // static quantity
{
	int n_max_vertex_num;
	glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES, &n_max_vertex_num);
	return n_max_vertex_num;
}

int CGLESShader::n_Geometry_MaxTotalOutputComponent_Num() // static quantity
{
	int n_max_component_num;
	glGetIntegerv(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, &n_max_component_num);
	return n_max_component_num;
}

int CGLESShader::n_Geometry_MaxInvocation_Num() // static quantity
{
	int n_max_invocation_num;
	glGetIntegerv(GL_MAX_GEOMETRY_SHADER_INVOCATIONS, &n_max_invocation_num);
	return n_max_invocation_num;
}

int CGLESShader::n_Geometry_MaxTextureImageUnit_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, &n_value);
	return n_value;
}

int CGLESShader::n_Fragment_MaxUniformComponent_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &n_value);
	return n_value;
}
#endif // 0

int CGLESShader::n_Fragment_MaxTextureImageUnit_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &n_value);
	return n_value;
}

#if 0 // not (yet) in OpenGL ES 2.0
float CGLESShader::f_Fragment_MinInterpolationOffset()
{
	float f_value;
	glGetFloatv(GL_MIN_FRAGMENT_INTERPOLATION_OFFSET, &f_value);
	return f_value;
}

float CGLESShader::f_Fragment_MaxInterpolationOffset()
{
	float f_value;
	glGetFloatv(GL_MAX_FRAGMENT_INTERPOLATION_OFFSET, &f_value);
	return f_value;
}

int CGLESShader::n_Fragment_InterpolationOffsetBit_Num()
{
	int n_value;
	glGetIntegerv(GL_FRAGMENT_INTERPOLATION_OFFSET_BITS, &n_value);
	return n_value;
}

int CGLESShader::n_MaxDualSourceDrawBuffer_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_DUAL_SOURCE_DRAW_BUFFERS, &n_value);
	return n_value;
}
#endif // 0

int CGLESShader::n_MaxTextureImageUnit_Num()
{
	int n_value;
	glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &n_value);
	return n_value;
}

const char *CGLESShader::p_s_ShadingLanguge_Version()
{
	return (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
}

#if 0 // not (yet) in OpenGL ES 2.0
size_t CGLESShader::n_Program_BinaryFormat_Num()
{
	int n_binary_format_num;
	glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &n_binary_format_num);
	_ASSERTE(n_binary_format_num >= 0 && n_binary_format_num <= SIZE_MAX);
	return n_binary_format_num;
}

bool CGLESShader::Get_Program_BinaryFormats(std::vector<GLenum> &r_format_list)
{
	int n_binary_format_num;
	glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &n_binary_format_num);
	if(!stl_ut::Resize_To_N(r_format_list, n_binary_format_num))
		return false;
#if 1
	_ASSERTE(sizeof(int) == sizeof(GLenum)); // make sure the storage is right
	glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, (int*)&r_format_list[0]); // gets all the formats?
#else
	for(int i = 0; i < n_binary_format_num; ++ i) {
		int n_format;
		glGetIntegeri_v(GL_PROGRAM_BINARY_FORMATS, i, &n_format); // this fails with GL_INVALID_OPERATION
		r_format_list[i] = GLenum(n_format);
	}
#endif
	return true;
}

size_t CGLESShader::n_Get_UniformBlock_Num() const
{
	return n_Get_Quantity(GL_ACTIVE_UNIFORM_BLOCKS);
}

bool CGLESShader::Get_UniformBlock_Name(GLuint n_block_index, std::string &r_s_name) const
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);
	_ASSERTE(b_Initialized());

	r_s_name.erase();
	// clear the name

	int n_name_length;// = n_Get_Quantity(GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH);
	glGetActiveUniformBlockiv(m_n_program_object, n_block_index,
		GL_UNIFORM_BLOCK_NAME_LENGTH, &n_name_length); // have the exact length
	// use the longest name for starting point

	if(!stl_ut::Resize_To_N(r_s_name, n_name_length))
		return false;
	// alloc the buffer

	_ASSERTE(r_s_name.length() <= CMaxIntValue<GLsizei>::result());
	glGetActiveUniformBlockName(m_n_program_object, n_block_index,
		GLsizei(r_s_name.length()), &n_name_length, &r_s_name[0]);
	// get name, data type, size and name length

	/*if(strlen(r_s_name.c_str()) < r_s_name.length() / 2) {
		std::string s_short;
		if(!stl_ut::AssignCStr(s_short, r_s_name.c_str()))
			r_s_name.resize(strlen(r_s_name.c_str())); // just cut-off the unused part (even though it's quite long)
		else
			r_s_name.swap(s_short); // swap for the short string
	} else*/ { // don't have to, we have the exact length
		_ASSERTE(strlen(r_s_name.c_str()) == r_s_name.length() - 1); // there should only be the terminating null
		r_s_name.resize(strlen(r_s_name.c_str())); // cut-off the unused part
	}
	// handle unused space

	return true;
}

const char *CGLESShader::p_s_Get_UniformBlock_Name(GLuint n_block_index) const
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);

	std::string s_name;
	if(!Get_UniformBlock_Name(n_block_index, s_name))
		return 0;
	return _strdup(s_name.c_str());
}

GLuint CGLESShader::n_Get_UniformBlock_Index(const char *p_s_uniform_block_name)
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);

	return glGetUniformBlockIndex(m_n_program_object, p_s_uniform_block_name);
}

size_t CGLESShader::n_Get_UniformBlock_DataSize(GLuint n_block_index)
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);

	int n_size;
	glGetActiveUniformBlockiv(m_n_program_object, n_block_index,
		GL_UNIFORM_BLOCK_DATA_SIZE, &n_size);
	_ASSERTE(n_size >= 0 && n_size <= SIZE_MAX);
	return n_size;
}

bool CGLESShader::b_Get_UniformBlock_ReferencedByVertexShader(GLuint n_block_index)
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);

	int n_result;
	glGetActiveUniformBlockiv(m_n_program_object, n_block_index,
		GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER, &n_result);
	return n_result == GL_TRUE;
}

bool CGLESShader::b_Get_UniformBlock_ReferencedByTessControlShader(GLuint n_block_index)
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);

	int n_result;
	glGetActiveUniformBlockiv(m_n_program_object, n_block_index,
		GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER, &n_result);
	return n_result == GL_TRUE;
}

bool CGLESShader::b_Get_UniformBlock_ReferencedByTessEvaluationShader(GLuint n_block_index)
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);

	int n_result;
	glGetActiveUniformBlockiv(m_n_program_object, n_block_index,
		GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER, &n_result);
	return n_result == GL_TRUE;
}

bool CGLESShader::b_Get_UniformBlock_ReferencedByGeometryShader(GLuint n_block_index)
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);

	int n_result;
	glGetActiveUniformBlockiv(m_n_program_object, n_block_index,
		GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER, &n_result);
	return n_result == GL_TRUE;
}

bool CGLESShader::b_Get_UniformBlock_ReferencedByFragmentShader(GLuint n_block_index)
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);

	int n_result;
	glGetActiveUniformBlockiv(m_n_program_object, n_block_index,
		GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER, &n_result);
	return n_result == GL_TRUE;
}

size_t CGLESShader::n_Get_UniformBlock_Uniform_Num(GLuint n_block_index)
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);

	int n_size;
	glGetActiveUniformBlockiv(m_n_program_object, n_block_index,
		GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &n_size);
	_ASSERTE(n_size >= 0 && n_size <= SIZE_MAX);
	return n_size;
}

bool CGLESShader::Get_UniformBlock_Uniform_Indices(GLuint n_block_index,
	size_t n_buffer_size, size_t &r_n_index_num, int *p_indices)
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);

	int n_size;
	glGetActiveUniformBlockiv(m_n_program_object, n_block_index,
		GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &n_size);
	_ASSERTE(n_size >= 0 && n_size <= SIZE_MAX);
	r_n_index_num = n_size;
	if(r_n_index_num < n_buffer_size)
		return false;
	glGetActiveUniformBlockiv(m_n_program_object, n_block_index,
		GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, p_indices);
	return true;
}

bool CGLESShader::Get_UniformBlock_Uniform_Indices(GLuint n_block_index, std::vector<int> &r_index_list)
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);

	int n_size;
	glGetActiveUniformBlockiv(m_n_program_object, n_block_index,
		GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &n_size);
	_ASSERTE(n_size >= 0 && n_size <= SIZE_MAX);
	if(!stl_ut::Resize_To_N(r_index_list, n_size))
		return false;
	glGetActiveUniformBlockiv(m_n_program_object, n_block_index,
		GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, &r_index_list[0]);
	return true;
}

bool CGLESShader::Get_Uniform(GLuint n_index, std::string &r_s_name, GLenum &r_n_data_type,
	int &r_n_size, int &r_n_block_index, int &r_n_block_offset) const
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);
	_ASSERTE(b_Initialized());

	if(!Get_Uniform(n_index, r_s_name, r_n_data_type, r_n_size))
		return false;
	// gets uniform name

	glGetActiveUniformsiv(m_n_program_object, 1, &n_index, GL_UNIFORM_BLOCK_INDEX, &r_n_block_index);
	if(r_n_block_index != -1)
		glGetActiveUniformsiv(m_n_program_object, 1, &n_index, GL_UNIFORM_OFFSET, &r_n_block_offset);
	// get other uniform parameters, relevant for uniforms in named uniform blocks

	return true;
}

bool CGLESShader::Get_Uniform(GLuint n_index, std::string &r_s_name, GLenum &r_n_data_type,
	int &r_n_size, int &r_n_block_index, int &r_n_block_offset, int &r_n_block_array_stride) const
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);
	_ASSERTE(b_Initialized());

	if(!Get_Uniform(n_index, r_s_name, r_n_data_type, r_n_size))
		return false;
	// gets uniform name

	glGetActiveUniformsiv(m_n_program_object, 1, &n_index, GL_UNIFORM_BLOCK_INDEX, &r_n_block_index);
	if(r_n_block_index != -1) {
		glGetActiveUniformsiv(m_n_program_object, 1, &n_index, GL_UNIFORM_OFFSET, &r_n_block_offset);
		glGetActiveUniformsiv(m_n_program_object, 1, &n_index, GL_UNIFORM_ARRAY_STRIDE, &r_n_block_array_stride);
	}
	// get other uniform parameters, relevant for uniforms in named uniform blocks

	return true;
}

bool CGLESShader::Get_Uniform(GLuint n_index, std::string &r_s_name, GLenum &r_n_data_type,
	int &r_n_size, int &r_n_block_index, int &r_n_block_offset,
	int &r_n_block_matrix_stride, bool &r_b_block_row_major) const
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);
	_ASSERTE(b_Initialized());

	if(!Get_Uniform(n_index, r_s_name, r_n_data_type, r_n_size))
		return false;
	// gets uniform name

	glGetActiveUniformsiv(m_n_program_object, 1, &n_index, GL_UNIFORM_BLOCK_INDEX, &r_n_block_index);
	if(r_n_block_index != -1) {
		glGetActiveUniformsiv(m_n_program_object, 1, &n_index, GL_UNIFORM_OFFSET, &r_n_block_offset);
		glGetActiveUniformsiv(m_n_program_object, 1, &n_index, GL_UNIFORM_MATRIX_STRIDE, &r_n_block_matrix_stride);
		int n_row_major;
		glGetActiveUniformsiv(m_n_program_object, 1, &n_index, GL_UNIFORM_IS_ROW_MAJOR, &n_row_major);
		r_b_block_row_major = (n_row_major == GL_TRUE);
	}
	// get other uniform parameters, relevant for uniforms in named uniform blocks

	return true;
}

void CGLESShader::UniformBuffer(GLuint n_block_index, GLuint n_block_binding,
	GLint n_buffer_object, GLintptr n_offset, GLsizeiptr n_size)
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);
	_ASSERTE(b_Initialized());

	glBindBufferRange(GL_UNIFORM_BUFFER, n_block_binding, n_buffer_object, n_offset, n_size);
	UniformBlock(n_block_index, n_block_binding);
}

void CGLESShader::UniformBuffer(GLuint n_block_index, GLuint n_block_binding, GLint n_buffer_object)
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);
	_ASSERTE(b_Initialized());

	glBindBufferBase(GL_UNIFORM_BUFFER, n_block_binding, n_buffer_object);
	UniformBlock(n_block_index, n_block_binding);
}

void CGLESShader::UniformBlock(GLuint n_block_index, GLuint n_block_binding)
{
	_ASSERTE(GLEH_ARB_uniform_buffer_object);
	_ASSERTE(b_Initialized());

	glUniformBlockBinding(m_n_program_object, n_block_index, n_block_binding);
}

int CGLESShader::n_Vertex_MaxUniformBlock_Num()
{
	int n_max_block_num;
	glGetIntegerv(GL_MAX_VERTEX_UNIFORM_BLOCKS, &n_max_block_num);
	return n_max_block_num;
}

int CGLESShader::n_Vertex_MaxCombinedUniformComponent_Num()
{
	int n_max_component_num;
	glGetIntegerv(GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS, &n_max_component_num);
	return n_max_component_num;
}

int CGLESShader::n_TessControl_MaxUniformBlock_Num()
{
	int n_max_block_num;
	glGetIntegerv(GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS, &n_max_block_num);
	return n_max_block_num;
}

int CGLESShader::n_TessControl_MaxCombinedUniformComponent_Num()
{
	int n_max_component_num;
	glGetIntegerv(GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS, &n_max_component_num);
	return n_max_component_num;
}

int CGLESShader::n_TessEvaluation_MaxUniformBlock_Num()
{
	int n_max_block_num;
	glGetIntegerv(GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS, &n_max_block_num);
	return n_max_block_num;
}

int CGLESShader::n_TessEvaluation_MaxCombinedUniformComponent_Num()
{
	int n_max_component_num;
	glGetIntegerv(GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS, &n_max_component_num);
	return n_max_component_num;
}

int CGLESShader::n_Geometry_MaxUniformBlock_Num()
{
	int n_max_block_num;
	glGetIntegerv(GL_MAX_GEOMETRY_UNIFORM_BLOCKS, &n_max_block_num);
	return n_max_block_num;
}

int CGLESShader::n_Geometry_MaxCombinedUniformComponent_Num()
{
	int n_max_component_num;
	glGetIntegerv(GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS, &n_max_component_num);
	return n_max_component_num;
}

int CGLESShader::n_Fragment_MaxUniformBlock_Num()
{
	int n_max_block_num;
	glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, &n_max_block_num);
	return n_max_block_num;
}

int CGLESShader::n_Fragment_MaxCombinedUniformComponent_Num()
{
	int n_max_component_num;
	glGetIntegerv(GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS, &n_max_component_num);
	return n_max_component_num;
}

int CGLESShader::n_MaxUniformBlock_Num()
{
	int n_max_block_num;
	glGetIntegerv(GL_MAX_COMBINED_UNIFORM_BLOCKS, &n_max_block_num);
	return n_max_block_num;
}

size_t CGLESShader::n_MaxUniformBlock_Size()
{
	int n_max_block_size;
	glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &n_max_block_size);
	_ASSERTE(n_max_block_size >= 0 && n_max_block_size <= SIZE_MAX);
	return n_max_block_size;
}

int CGLESShader::n_UniformBuffer_MaxBinding_Num()
{
	int n_max_binding_num;
	glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &n_max_binding_num);
	return n_max_binding_num;
}

int CGLESShader::n_UniformBuffer_OffsetAlignment()
{
	int n_align;
	glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &n_align);
	return n_align;
}

int CGLESShader::n_TransformFeedback_MaxInterleavedComponent_Num()
{
	int n_max_components;
	glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, &n_max_components);
	return n_max_components;
}

int CGLESShader::n_TransformFeedback_MaxSeparateAttrib_Num()
{
	int n_max_components;
	glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &n_max_components);
	return n_max_components;
}

int CGLESShader::n_TransformFeedback_MaxSeparateComponent_Num()
{
	int n_max_components;
	glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS, &n_max_components);
	return n_max_components;
}
#endif // 0

/*
 *								=== ~CGLESShader ===
 */
