all repos — mgba @ a2ed0785d95ba766025d02a1f00622bcdedbd09b

mGBA Game Boy Advance Emulator

src/platform/opengl/gles2.c (view raw)

   1/* Copyright (c) 2013-2015 Jeffrey Pfau
   2 *
   3 * This Source Code Form is subject to the terms of the Mozilla Public
   4 * License, v. 2.0. If a copy of the MPL was not distributed with this
   5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
   6#include "gles2.h"
   7
   8#include <mgba/core/log.h>
   9#include <mgba-util/configuration.h>
  10#include <mgba-util/formatting.h>
  11#include <mgba-util/math.h>
  12#include <mgba-util/vector.h>
  13#include <mgba-util/vfs.h>
  14
  15mLOG_DECLARE_CATEGORY(OPENGL);
  16mLOG_DEFINE_CATEGORY(OPENGL, "OpenGL", "video.ogl");
  17
  18#define MAX_PASSES 8
  19
  20static const GLchar* const _gles2Header =
  21	"#version 100\n"
  22	"precision mediump float;\n";
  23
  24static const GLchar* const _gl3Header =
  25	"#version 120\n";
  26
  27static const char* const _vertexShader =
  28	"attribute vec4 position;\n"
  29	"varying vec2 texCoord;\n"
  30
  31	"void main() {\n"
  32	"	gl_Position = position;\n"
  33	"	texCoord = (position.st + vec2(1.0, -1.0)) * vec2(0.5, -0.5);\n"
  34	"}";
  35
  36static const char* const _nullVertexShader =
  37	"attribute vec4 position;\n"
  38	"varying vec2 texCoord;\n"
  39
  40	"void main() {\n"
  41	"	gl_Position = position;\n"
  42	"	texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);\n"
  43	"}";
  44
  45static const char* const _fragmentShader =
  46	"varying vec2 texCoord;\n"
  47	"uniform sampler2D tex;\n"
  48	"uniform float gamma;\n"
  49	"uniform vec3 desaturation;\n"
  50	"uniform vec3 scale;\n"
  51	"uniform vec3 bias;\n"
  52
  53	"void main() {\n"
  54	"	vec4 color = texture2D(tex, texCoord);\n"
  55	"	color.a = 1.;\n"
  56	"	float average = dot(color.rgb, vec3(1.)) / 3.;\n"
  57	"	color.rgb = mix(color.rgb, vec3(average), desaturation);\n"
  58	"	color.rgb = scale * pow(color.rgb, vec3(gamma, gamma, gamma)) + bias;\n"
  59	"	gl_FragColor = color;\n"
  60	"}";
  61
  62static const char* const _nullFragmentShader =
  63	"varying vec2 texCoord;\n"
  64	"uniform sampler2D tex;\n"
  65
  66	"void main() {\n"
  67	"	vec4 color = texture2D(tex, texCoord);\n"
  68	"	color.a = 1.;\n"
  69	"	gl_FragColor = color;\n"
  70	"}";
  71
  72static const char* const _interframeFragmentShader =
  73	"varying vec2 texCoord;\n"
  74	"uniform sampler2D tex;\n"
  75
  76	"void main() {\n"
  77	"	vec4 color = texture2D(tex, texCoord);\n"
  78	"	color.a = 0.5;\n"
  79	"	gl_FragColor = color;\n"
  80	"}";
  81
  82static const GLfloat _vertices[] = {
  83	-1.f, -1.f,
  84	-1.f, 1.f,
  85	1.f, 1.f,
  86	1.f, -1.f,
  87};
  88
  89static void mGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
  90	UNUSED(handle);
  91	struct mGLES2Context* context = (struct mGLES2Context*) v;
  92	glGenTextures(1, &context->tex);
  93	glBindTexture(GL_TEXTURE_2D, context->tex);
  94	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  95	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  96
  97	glGenBuffers(1, &context->vbo);
  98	glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
  99	glBufferData(GL_ARRAY_BUFFER, sizeof(_vertices), _vertices, GL_STATIC_DRAW);
 100
 101	struct mGLES2Uniform* uniforms = malloc(sizeof(struct mGLES2Uniform) * 4);
 102	uniforms[0].name = "gamma";
 103	uniforms[0].readableName = "Gamma";
 104	uniforms[0].type = GL_FLOAT;
 105	uniforms[0].value.f = 1.0f;
 106	uniforms[0].min.f = 0.1f;
 107	uniforms[0].max.f = 3.0f;
 108	uniforms[1].name = "scale";
 109	uniforms[1].readableName = "Scale";
 110	uniforms[1].type = GL_FLOAT_VEC3;
 111	uniforms[1].value.fvec3[0] = 1.0f;
 112	uniforms[1].value.fvec3[1] = 1.0f;
 113	uniforms[1].value.fvec3[2] = 1.0f;
 114	uniforms[1].min.fvec3[0] = -1.0f;
 115	uniforms[1].min.fvec3[1] = -1.0f;
 116	uniforms[1].min.fvec3[2] = -1.0f;
 117	uniforms[1].max.fvec3[0] = 2.0f;
 118	uniforms[1].max.fvec3[1] = 2.0f;
 119	uniforms[1].max.fvec3[2] = 2.0f;
 120	uniforms[2].name = "bias";
 121	uniforms[2].readableName = "Bias";
 122	uniforms[2].type = GL_FLOAT_VEC3;
 123	uniforms[2].value.fvec3[0] = 0.0f;
 124	uniforms[2].value.fvec3[1] = 0.0f;
 125	uniforms[2].value.fvec3[2] = 0.0f;
 126	uniforms[2].min.fvec3[0] = -1.0f;
 127	uniforms[2].min.fvec3[1] = -1.0f;
 128	uniforms[2].min.fvec3[2] = -1.0f;
 129	uniforms[2].max.fvec3[0] = 1.0f;
 130	uniforms[2].max.fvec3[1] = 1.0f;
 131	uniforms[2].max.fvec3[2] = 1.0f;
 132	uniforms[3].name = "desaturation";
 133	uniforms[3].readableName = "Desaturation";
 134	uniforms[3].type = GL_FLOAT_VEC3;
 135	uniforms[3].value.fvec3[0] = 0.0f;
 136	uniforms[3].value.fvec3[1] = 0.0f;
 137	uniforms[3].value.fvec3[2] = 0.0f;
 138	uniforms[3].min.fvec3[0] = 0.0f;
 139	uniforms[3].min.fvec3[1] = 0.0f;
 140	uniforms[3].min.fvec3[2] = 0.0f;
 141	uniforms[3].max.fvec3[0] = 1.0f;
 142	uniforms[3].max.fvec3[1] = 1.0f;
 143	uniforms[3].max.fvec3[2] = 1.0f;
 144	mGLES2ShaderInit(&context->initialShader, _vertexShader, _fragmentShader, -1, -1, false, uniforms, 4);
 145	mGLES2ShaderInit(&context->finalShader, 0, 0, 0, 0, false, 0, 0);
 146	mGLES2ShaderInit(&context->interframeShader, 0, _interframeFragmentShader, -1, -1, false, 0, 0);
 147
 148	if (context->initialShader.vao != (GLuint) -1) {
 149		glBindVertexArray(context->initialShader.vao);
 150		glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
 151		glBindVertexArray(context->finalShader.vao);
 152		glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
 153		glBindVertexArray(context->interframeShader.vao);
 154		glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
 155		glBindVertexArray(0);
 156	}
 157
 158	glDeleteFramebuffers(1, &context->finalShader.fbo);
 159	glDeleteTextures(1, &context->finalShader.tex);
 160	context->finalShader.fbo = 0;
 161	context->finalShader.tex = 0;
 162}
 163
 164static void mGLES2ContextSetDimensions(struct VideoBackend* v, unsigned width, unsigned height) {
 165	struct mGLES2Context* context = (struct mGLES2Context*) v;
 166	v->width = width;
 167	v->height = height;
 168
 169	glBindTexture(GL_TEXTURE_2D, context->tex);
 170#ifdef COLOR_16_BIT
 171#ifdef COLOR_5_6_5
 172	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0);
 173#else
 174	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0);
 175#endif
 176#elif defined(__BIG_ENDIAN__)
 177	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 0);
 178#else
 179	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
 180#endif
 181
 182	size_t n;
 183	for (n = 0; n < context->nShaders; ++n) {
 184		if (context->shaders[n].width < 0 || context->shaders[n].height < 0) {
 185			context->shaders[n].dirty = true;
 186		}
 187	}
 188	context->initialShader.dirty = true;
 189	context->interframeShader.dirty = true;
 190}
 191
 192static void mGLES2ContextDeinit(struct VideoBackend* v) {
 193	struct mGLES2Context* context = (struct mGLES2Context*) v;
 194	glDeleteTextures(1, &context->tex);
 195	glDeleteBuffers(1, &context->vbo);
 196	mGLES2ShaderDeinit(&context->initialShader);
 197	mGLES2ShaderDeinit(&context->finalShader);
 198	mGLES2ShaderDeinit(&context->interframeShader);
 199	free(context->initialShader.uniforms);
 200}
 201
 202static void mGLES2ContextResized(struct VideoBackend* v, unsigned w, unsigned h) {
 203	struct mGLES2Context* context = (struct mGLES2Context*) v;
 204	unsigned drawW = w;
 205	unsigned drawH = h;
 206	if (v->lockAspectRatio) {
 207		if (w * v->height > h * v->width) {
 208			drawW = h * v->width / v->height;
 209		} else if (w * v->height < h * v->width) {
 210			drawH = w * v->height / v->width;
 211		}
 212	}
 213	if (v->lockIntegerScaling) {
 214		if (drawW >= v->width) {
 215			drawW -= drawW % v->width;
 216		}
 217		if (drawH >= v->height) {
 218			drawH -= drawH % v->height;
 219		}
 220	}
 221	size_t n;
 222	for (n = 0; n < context->nShaders; ++n) {
 223		if (context->shaders[n].width == 0 || context->shaders[n].height == 0) {
 224			context->shaders[n].dirty = true;
 225		}
 226	}
 227	glBindFramebuffer(GL_FRAMEBUFFER, 0);
 228	glViewport((w - drawW) / 2, (h - drawH) / 2, drawW, drawH);
 229}
 230
 231static void mGLES2ContextClear(struct VideoBackend* v) {
 232	UNUSED(v);
 233	glBindFramebuffer(GL_FRAMEBUFFER, 0);
 234	glClearColor(0.f, 0.f, 0.f, 1.f);
 235	glClear(GL_COLOR_BUFFER_BIT);
 236}
 237
 238void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
 239	GLint viewport[4];
 240	glGetIntegerv(GL_VIEWPORT, viewport);
 241	int drawW = shader->width;
 242	int drawH = shader->height;
 243	int padW = 0;
 244	int padH = 0;
 245	if (!drawW) {
 246		drawW = viewport[2];
 247		padW = viewport[0];
 248	} else if (shader->width < 0) {
 249		drawW = context->d.width * -shader->width;
 250	}
 251	if (!drawH) {
 252		drawH = viewport[3];
 253		padH = viewport[1];
 254	} else if (shader->height < 0) {
 255		drawH = context->d.height * -shader->height;
 256	}
 257	if (shader->integerScaling) {
 258		padW = 0;
 259		padH = 0;
 260		drawW -= drawW % context->d.width;
 261		drawH -= drawH % context->d.height;
 262	}
 263
 264	if (shader->dirty) {
 265		if (shader->tex && (shader->width <= 0 || shader->height <= 0)) {
 266			GLint oldTex;
 267			glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTex);
 268			glBindTexture(GL_TEXTURE_2D, shader->tex);
 269			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, drawW, drawH, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
 270			glBindTexture(GL_TEXTURE_2D, oldTex);
 271		}
 272		shader->dirty = false;
 273	}
 274
 275	glBindFramebuffer(GL_FRAMEBUFFER, shader->fbo);
 276	glViewport(padW, padH, drawW, drawH);
 277	if (shader->blend) {
 278		glEnable(GL_BLEND);
 279		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 280	} else {
 281		glDisable(GL_BLEND);
 282		glClearColor(0.f, 0.f, 0.f, 1.f);
 283		glClear(GL_COLOR_BUFFER_BIT);
 284	}
 285
 286	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST);
 287	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST);
 288	glUseProgram(shader->program);
 289	glUniform1i(shader->texLocation, 0);
 290	glUniform2f(shader->texSizeLocation, context->d.width - padW, context->d.height - padH);
 291	if (shader->vao != (GLuint) -1) {
 292		glBindVertexArray(shader->vao);
 293	} else {
 294		glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
 295		glEnableVertexAttribArray(shader->positionLocation);
 296		glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
 297	}
 298	size_t u;
 299	for (u = 0; u < shader->nUniforms; ++u) {
 300		struct mGLES2Uniform* uniform = &shader->uniforms[u];
 301		switch (uniform->type) {
 302		case GL_FLOAT:
 303			glUniform1f(uniform->location, uniform->value.f);
 304			break;
 305		case GL_INT:
 306			glUniform1i(uniform->location, uniform->value.i);
 307			break;
 308		case GL_BOOL:
 309			glUniform1i(uniform->location, uniform->value.b);
 310			break;
 311		case GL_FLOAT_VEC2:
 312			glUniform2fv(uniform->location, 1, uniform->value.fvec2);
 313			break;
 314		case GL_FLOAT_VEC3:
 315			glUniform3fv(uniform->location, 1, uniform->value.fvec3);
 316			break;
 317		case GL_FLOAT_VEC4:
 318			glUniform4fv(uniform->location, 1, uniform->value.fvec4);
 319			break;
 320		case GL_INT_VEC2:
 321			glUniform2iv(uniform->location, 1, uniform->value.ivec2);
 322			break;
 323		case GL_INT_VEC3:
 324			glUniform3iv(uniform->location, 1, uniform->value.ivec3);
 325			break;
 326		case GL_INT_VEC4:
 327			glUniform4iv(uniform->location, 1, uniform->value.ivec4);
 328			break;
 329		case GL_BOOL_VEC2:
 330			glUniform2i(uniform->location, uniform->value.bvec2[0], uniform->value.bvec2[1]);
 331			break;
 332		case GL_BOOL_VEC3:
 333			glUniform3i(uniform->location, uniform->value.bvec3[0], uniform->value.bvec3[1], uniform->value.bvec3[2]);
 334			break;
 335		case GL_BOOL_VEC4:
 336			glUniform4i(uniform->location, uniform->value.bvec4[0], uniform->value.bvec4[1], uniform->value.bvec4[2], uniform->value.bvec4[3]);
 337			break;
 338		case GL_FLOAT_MAT2:
 339			glUniformMatrix2fv(uniform->location, 1, GL_FALSE, uniform->value.fmat2x2);
 340			break;
 341		case GL_FLOAT_MAT3:
 342			glUniformMatrix3fv(uniform->location, 1, GL_FALSE, uniform->value.fmat3x3);
 343			break;
 344		case GL_FLOAT_MAT4:
 345			glUniformMatrix4fv(uniform->location, 1, GL_FALSE, uniform->value.fmat4x4);
 346			break;
 347		}
 348	}
 349	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 350	glBindTexture(GL_TEXTURE_2D, shader->tex);
 351}
 352
 353void mGLES2ContextDrawFrame(struct VideoBackend* v) {
 354	struct mGLES2Context* context = (struct mGLES2Context*) v;
 355	glActiveTexture(GL_TEXTURE0);
 356	glBindTexture(GL_TEXTURE_2D, context->tex);
 357
 358	GLint viewport[4];
 359	glGetIntegerv(GL_VIEWPORT, viewport);
 360
 361	context->finalShader.filter = v->filter;
 362	context->finalShader.dirty = true;
 363	_drawShader(context, &context->initialShader);
 364	if (v->interframeBlending) {
 365		context->interframeShader.blend = true;
 366		glViewport(0, 0, viewport[2], viewport[3]);
 367		_drawShader(context, &context->interframeShader);
 368	}
 369	size_t n;
 370	for (n = 0; n < context->nShaders; ++n) {
 371		glViewport(0, 0, viewport[2], viewport[3]);
 372		_drawShader(context, &context->shaders[n]);
 373	}
 374	glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
 375	_drawShader(context, &context->finalShader);
 376	if (v->interframeBlending) {
 377		context->interframeShader.blend = false;
 378		glBindTexture(GL_TEXTURE_2D, context->tex);
 379		_drawShader(context, &context->initialShader);
 380		glViewport(0, 0, viewport[2], viewport[3]);
 381		_drawShader(context, &context->interframeShader);
 382	}
 383	glBindFramebuffer(GL_FRAMEBUFFER, 0);
 384	glUseProgram(0);
 385	if (context->finalShader.vao != (GLuint) -1) {
 386		glBindVertexArray(0);
 387	}
 388}
 389
 390void mGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) {
 391	struct mGLES2Context* context = (struct mGLES2Context*) v;
 392	glBindTexture(GL_TEXTURE_2D, context->tex);
 393#ifdef COLOR_16_BIT
 394#ifdef COLOR_5_6_5
 395	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
 396#else
 397	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
 398#endif
 399#elif defined(__BIG_ENDIAN__)
 400	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, frame);
 401#else
 402	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame);
 403#endif
 404}
 405
 406void mGLES2ContextCreate(struct mGLES2Context* context) {
 407	context->d.init = mGLES2ContextInit;
 408	context->d.deinit = mGLES2ContextDeinit;
 409	context->d.setDimensions = mGLES2ContextSetDimensions;
 410	context->d.resized = mGLES2ContextResized;
 411	context->d.swap = 0;
 412	context->d.clear = mGLES2ContextClear;
 413	context->d.postFrame = mGLES2ContextPostFrame;
 414	context->d.drawFrame = mGLES2ContextDrawFrame;
 415	context->d.setMessage = 0;
 416	context->d.clearMessage = 0;
 417	context->shaders = 0;
 418	context->nShaders = 0;
 419}
 420
 421void mGLES2ShaderInit(struct mGLES2Shader* shader, const char* vs, const char* fs, int width, int height, bool integerScaling, struct mGLES2Uniform* uniforms, size_t nUniforms) {
 422	shader->width = width;
 423	shader->height = height;
 424	shader->integerScaling = integerScaling;
 425	shader->filter = false;
 426	shader->blend = false;
 427	shader->dirty = true;
 428	shader->uniforms = uniforms;
 429	shader->nUniforms = nUniforms;
 430	glGenFramebuffers(1, &shader->fbo);
 431	glBindFramebuffer(GL_FRAMEBUFFER, shader->fbo);
 432
 433	glGenTextures(1, &shader->tex);
 434	glBindTexture(GL_TEXTURE_2D, shader->tex);
 435	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 436	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 437	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 438	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 439	if (shader->width > 0 && shader->height > 0) {
 440		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, shader->width, shader->height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
 441	} else {
 442		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);		
 443	}
 444
 445	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shader->tex, 0);
 446	shader->program = glCreateProgram();
 447	shader->vertexShader = glCreateShader(GL_VERTEX_SHADER);
 448	shader->fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
 449	const GLchar* shaderBuffer[2];
 450	const GLubyte* version = glGetString(GL_VERSION);
 451	if (strncmp((const char*) version, "OpenGL ES ", strlen("OpenGL ES "))) {
 452		shaderBuffer[0] = _gl3Header;
 453	} else {
 454		shaderBuffer[0] = _gles2Header;
 455	}
 456	if (vs) {
 457		shaderBuffer[1] = vs;
 458	} else {
 459		shaderBuffer[1] = _nullVertexShader;
 460	}
 461	glShaderSource(shader->vertexShader, 2, shaderBuffer, 0);
 462
 463	if (fs) {
 464		shaderBuffer[1] = fs;
 465	} else {
 466		shaderBuffer[1] = _nullFragmentShader;
 467	}
 468	glShaderSource(shader->fragmentShader, 2, shaderBuffer, 0);
 469
 470	glAttachShader(shader->program, shader->vertexShader);
 471	glAttachShader(shader->program, shader->fragmentShader);
 472	char log[1024];
 473	glCompileShader(shader->fragmentShader);
 474	glGetShaderInfoLog(shader->fragmentShader, 1024, 0, log);
 475	if (log[0]) {
 476		mLOG(OPENGL, ERROR, "%s\n", log);
 477	}
 478	glCompileShader(shader->vertexShader);
 479	glGetShaderInfoLog(shader->vertexShader, 1024, 0, log);
 480	if (log[0]) {
 481		mLOG(OPENGL, ERROR, "%s\n", log);
 482	}
 483	glLinkProgram(shader->program);
 484	glGetProgramInfoLog(shader->program, 1024, 0, log);
 485	if (log[0]) {
 486		mLOG(OPENGL, ERROR, "%s\n", log);
 487	}
 488
 489	shader->texLocation = glGetUniformLocation(shader->program, "tex");
 490	shader->texSizeLocation = glGetUniformLocation(shader->program, "texSize");
 491	shader->positionLocation = glGetAttribLocation(shader->program, "position");
 492	size_t i;
 493	for (i = 0; i < shader->nUniforms; ++i) {
 494		shader->uniforms[i].location = glGetUniformLocation(shader->program, shader->uniforms[i].name);
 495	}
 496
 497	const GLubyte* extensions = glGetString(GL_EXTENSIONS);
 498	if (shaderBuffer[0] == _gles2Header || version[0] >= '3' || (extensions && strstr((const char*) extensions, "_vertex_array_object") != NULL)) {
 499		glGenVertexArrays(1, &shader->vao);
 500		glBindVertexArray(shader->vao);
 501		glEnableVertexAttribArray(shader->positionLocation);
 502		glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
 503		glBindVertexArray(0);
 504	} else {
 505		shader->vao = -1;
 506	}
 507
 508	glBindFramebuffer(GL_FRAMEBUFFER, 0);
 509}
 510
 511void mGLES2ShaderDeinit(struct mGLES2Shader* shader) {
 512	glDeleteTextures(1, &shader->tex);
 513	glDeleteShader(shader->fragmentShader);
 514	glDeleteProgram(shader->program);
 515	glDeleteFramebuffers(1, &shader->fbo);
 516	if (shader->vao != (GLuint) -1) {
 517		glDeleteVertexArrays(1, &shader->vao);
 518	}
 519}
 520
 521void mGLES2ShaderAttach(struct mGLES2Context* context, struct mGLES2Shader* shaders, size_t nShaders) {
 522	if (context->shaders) {
 523		if (context->shaders == shaders && context->nShaders == nShaders) {
 524			return;
 525		}
 526		mGLES2ShaderDetach(context);
 527	}
 528	context->shaders = shaders;
 529	context->nShaders = nShaders;
 530	size_t i;
 531	for (i = 0; i < nShaders; ++i) {
 532		glBindFramebuffer(GL_FRAMEBUFFER, context->shaders[i].fbo);
 533		glClearColor(0.f, 0.f, 0.f, 1.f);
 534		glClear(GL_COLOR_BUFFER_BIT);
 535
 536		if (context->shaders[i].vao != (GLuint) -1) {
 537			glBindVertexArray(context->shaders[i].vao);
 538			glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
 539			glEnableVertexAttribArray(context->shaders[i].positionLocation);
 540			glVertexAttribPointer(context->shaders[i].positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
 541		}
 542	}
 543	if (context->initialShader.vao != (GLuint) -1) {
 544		glBindVertexArray(0);
 545	}
 546	glBindFramebuffer(GL_FRAMEBUFFER, 0);
 547}
 548
 549void mGLES2ShaderDetach(struct mGLES2Context* context) {
 550	if (!context->shaders) {
 551		return;
 552	}
 553	context->shaders = 0;
 554	context->nShaders = 0;
 555}
 556
 557static bool _lookupIntValue(const struct Configuration* config, const char* section, const char* key, int* out) {
 558	const char* charValue = ConfigurationGetValue(config, section, key);
 559	if (!charValue) {
 560		return false;
 561	}
 562	char* end;
 563	unsigned long value = strtol(charValue, &end, 10);
 564	if (*end) {
 565		return false;
 566	}
 567	*out = value;
 568	return true;
 569}
 570
 571static bool _lookupFloatValue(const struct Configuration* config, const char* section, const char* key, float* out) {
 572	const char* charValue = ConfigurationGetValue(config, section, key);
 573	if (!charValue) {
 574		return false;
 575	}
 576	char* end;
 577	float value = strtof_u(charValue, &end);
 578	if (*end) {
 579		return false;
 580	}
 581	*out = value;
 582	return true;
 583}
 584
 585static bool _lookupBoolValue(const struct Configuration* config, const char* section, const char* key, GLboolean* out) {
 586	const char* charValue = ConfigurationGetValue(config, section, key);
 587	if (!charValue) {
 588		return false;
 589	}
 590	if (!strcmp(charValue, "true")) {
 591		*out = GL_TRUE;
 592		return true;
 593	}
 594	if (!strcmp(charValue, "false")) {
 595		*out = GL_FALSE;
 596		return true;
 597	}
 598	char* end;
 599	unsigned long value = strtol(charValue, &end, 10);
 600	if (*end) {
 601		return false;
 602	}
 603	*out = value;
 604	return true;
 605}
 606
 607DECLARE_VECTOR(mGLES2UniformList, struct mGLES2Uniform);
 608DEFINE_VECTOR(mGLES2UniformList, struct mGLES2Uniform);
 609
 610static void _uniformHandler(const char* sectionName, void* user) {
 611	struct mGLES2UniformList* uniforms = user;
 612	unsigned passId;
 613	int sentinel;
 614	if (sscanf(sectionName, "pass.%u.uniform.%n", &passId, &sentinel) < 1) {
 615		return;
 616	}
 617	struct mGLES2Uniform* u = mGLES2UniformListAppend(uniforms);
 618	u->name = sectionName;
 619}
 620
 621
 622static void _loadValue(struct Configuration* description, const char* name, GLenum type, const char* field, union mGLES2UniformValue* value) {
 623	char fieldName[16];
 624	switch (type) {
 625	case GL_FLOAT:
 626		value->f = 0;
 627		_lookupFloatValue(description, name, field, &value->f);
 628		break;
 629	case GL_FLOAT_VEC2:
 630		value->fvec2[0] = 0;
 631		value->fvec2[1] = 0;
 632		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 633		_lookupFloatValue(description, name, fieldName, &value->fvec2[0]);
 634		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 635		_lookupFloatValue(description, name, fieldName, &value->fvec2[1]);
 636		break;
 637	case GL_FLOAT_VEC3:
 638		value->fvec3[0] = 0;
 639		value->fvec3[1] = 0;
 640		value->fvec3[2] = 0;
 641		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 642		_lookupFloatValue(description, name, fieldName, &value->fvec3[0]);
 643		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 644		_lookupFloatValue(description, name, fieldName, &value->fvec3[1]);
 645		snprintf(fieldName, sizeof(fieldName), "%s[2]", field);
 646		_lookupFloatValue(description, name, fieldName, &value->fvec3[2]);
 647		break;
 648	case GL_FLOAT_VEC4:
 649		value->fvec4[0] = 0;
 650		value->fvec4[1] = 0;
 651		value->fvec4[2] = 0;
 652		value->fvec4[3] = 0;
 653		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 654		_lookupFloatValue(description, name, fieldName, &value->fvec4[0]);
 655		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 656		_lookupFloatValue(description, name, fieldName, &value->fvec4[1]);
 657		snprintf(fieldName, sizeof(fieldName), "%s[2]", field);
 658		_lookupFloatValue(description, name, fieldName, &value->fvec4[2]);
 659		snprintf(fieldName, sizeof(fieldName), "%s[3]", field);
 660		_lookupFloatValue(description, name, fieldName, &value->fvec4[3]);
 661		break;
 662	case GL_FLOAT_MAT2:
 663		value->fmat2x2[0] = 0;
 664		value->fmat2x2[1] = 0;
 665		value->fmat2x2[2] = 0;
 666		value->fmat2x2[3] = 0;
 667		snprintf(fieldName, sizeof(fieldName), "%s[0,0]", field);
 668		_lookupFloatValue(description, name, fieldName, &value->fmat2x2[0]);
 669		snprintf(fieldName, sizeof(fieldName), "%s[0,1]", field);
 670		_lookupFloatValue(description, name, fieldName, &value->fmat2x2[1]);
 671		snprintf(fieldName, sizeof(fieldName), "%s[1,0]", field);
 672		_lookupFloatValue(description, name, fieldName, &value->fmat2x2[2]);
 673		snprintf(fieldName, sizeof(fieldName), "%s[1,1]", field);
 674		_lookupFloatValue(description, name, fieldName, &value->fmat2x2[3]);
 675		break;
 676	case GL_FLOAT_MAT3:
 677		value->fmat3x3[0] = 0;
 678		value->fmat3x3[1] = 0;
 679		value->fmat3x3[2] = 0;
 680		value->fmat3x3[3] = 0;
 681		value->fmat3x3[4] = 0;
 682		value->fmat3x3[5] = 0;
 683		value->fmat3x3[6] = 0;
 684		value->fmat3x3[7] = 0;
 685		value->fmat3x3[8] = 0;
 686		snprintf(fieldName, sizeof(fieldName), "%s[0,0]", field);
 687		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[0]);
 688		snprintf(fieldName, sizeof(fieldName), "%s[0,1]", field);
 689		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[1]);
 690		snprintf(fieldName, sizeof(fieldName), "%s[0,2]", field);
 691		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[2]);
 692		snprintf(fieldName, sizeof(fieldName), "%s[1,0]", field);
 693		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[3]);
 694		snprintf(fieldName, sizeof(fieldName), "%s[1,1]", field);
 695		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[4]);
 696		snprintf(fieldName, sizeof(fieldName), "%s[1,2]", field);
 697		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[5]);
 698		snprintf(fieldName, sizeof(fieldName), "%s[2,0]", field);
 699		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[6]);
 700		snprintf(fieldName, sizeof(fieldName), "%s[2,1]", field);
 701		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[7]);
 702		snprintf(fieldName, sizeof(fieldName), "%s[2,2]", field);
 703		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[8]);
 704		break;
 705	case GL_FLOAT_MAT4:
 706		value->fmat4x4[0] = 0;
 707		value->fmat4x4[1] = 0;
 708		value->fmat4x4[2] = 0;
 709		value->fmat4x4[3] = 0;
 710		value->fmat4x4[4] = 0;
 711		value->fmat4x4[5] = 0;
 712		value->fmat4x4[6] = 0;
 713		value->fmat4x4[7] = 0;
 714		value->fmat4x4[8] = 0;
 715		value->fmat4x4[9] = 0;
 716		value->fmat4x4[10] = 0;
 717		value->fmat4x4[11] = 0;
 718		value->fmat4x4[12] = 0;
 719		value->fmat4x4[13] = 0;
 720		value->fmat4x4[14] = 0;
 721		value->fmat4x4[15] = 0;
 722		snprintf(fieldName, sizeof(fieldName), "%s[0,0]", field);
 723		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[0]);
 724		snprintf(fieldName, sizeof(fieldName), "%s[0,1]", field);
 725		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[1]);
 726		snprintf(fieldName, sizeof(fieldName), "%s[0,2]", field);
 727		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[2]);
 728		snprintf(fieldName, sizeof(fieldName), "%s[0,3]", field);
 729		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[3]);
 730		snprintf(fieldName, sizeof(fieldName), "%s[1,0]", field);
 731		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[4]);
 732		snprintf(fieldName, sizeof(fieldName), "%s[1,1]", field);
 733		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[5]);
 734		snprintf(fieldName, sizeof(fieldName), "%s[1,2]", field);
 735		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[6]);
 736		snprintf(fieldName, sizeof(fieldName), "%s[1,3]", field);
 737		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[7]);
 738		snprintf(fieldName, sizeof(fieldName), "%s[2,0]", field);
 739		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[8]);
 740		snprintf(fieldName, sizeof(fieldName), "%s[2,1]", field);
 741		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[9]);
 742		snprintf(fieldName, sizeof(fieldName), "%s[2,2]", field);
 743		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[10]);
 744		snprintf(fieldName, sizeof(fieldName), "%s[2,3]", field);
 745		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[11]);
 746		snprintf(fieldName, sizeof(fieldName), "%s[3,0]", field);
 747		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[12]);
 748		snprintf(fieldName, sizeof(fieldName), "%s[3,1]", field);
 749		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[13]);
 750		snprintf(fieldName, sizeof(fieldName), "%s[3,2]", field);
 751		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[14]);
 752		snprintf(fieldName, sizeof(fieldName), "%s[3,3]", field);
 753		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[15]);
 754		break;
 755	case GL_INT:
 756		value->i = 0;
 757		_lookupIntValue(description, name, field, &value->i);
 758		break;
 759	case GL_INT_VEC2:
 760		value->ivec2[0] = 0;
 761		value->ivec2[1] = 0;
 762		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 763		_lookupIntValue(description, name, fieldName, &value->ivec2[0]);
 764		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 765		_lookupIntValue(description, name, fieldName, &value->ivec2[1]);
 766		break;
 767	case GL_INT_VEC3:
 768		value->ivec3[0] = 0;
 769		value->ivec3[1] = 0;
 770		value->ivec3[2] = 0;
 771		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 772		_lookupIntValue(description, name, fieldName, &value->ivec3[0]);
 773		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 774		_lookupIntValue(description, name, fieldName, &value->ivec3[1]);
 775		snprintf(fieldName, sizeof(fieldName), "%s[2]", field);
 776		_lookupIntValue(description, name, fieldName, &value->ivec3[2]);
 777		break;
 778	case GL_INT_VEC4:
 779		value->ivec4[0] = 0;
 780		value->ivec4[1] = 0;
 781		value->ivec4[2] = 0;
 782		value->ivec4[3] = 0;
 783		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 784		_lookupIntValue(description, name, fieldName, &value->ivec4[0]);
 785		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 786		_lookupIntValue(description, name, fieldName, &value->ivec4[1]);
 787		snprintf(fieldName, sizeof(fieldName), "%s[2]", field);
 788		_lookupIntValue(description, name, fieldName, &value->ivec4[2]);
 789		snprintf(fieldName, sizeof(fieldName), "%s[3]", field);
 790		_lookupIntValue(description, name, fieldName, &value->ivec4[3]);
 791		break;
 792	case GL_BOOL:
 793		value->b = 0;
 794		_lookupBoolValue(description, name, field, &value->b);
 795		break;
 796	case GL_BOOL_VEC2:
 797		value->bvec2[0] = 0;
 798		value->bvec2[1] = 0;
 799		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 800		_lookupBoolValue(description, name, fieldName, &value->bvec2[0]);
 801		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 802		_lookupBoolValue(description, name, fieldName, &value->bvec2[1]);
 803		break;
 804	case GL_BOOL_VEC3:
 805		value->bvec3[0] = 0;
 806		value->bvec3[1] = 0;
 807		value->bvec3[2] = 0;
 808		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 809		_lookupBoolValue(description, name, fieldName, &value->bvec3[0]);
 810		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 811		_lookupBoolValue(description, name, fieldName, &value->bvec3[1]);
 812		snprintf(fieldName, sizeof(fieldName), "%s[2]", field);
 813		_lookupBoolValue(description, name, fieldName, &value->bvec3[2]);
 814		break;
 815	case GL_BOOL_VEC4:
 816		value->bvec4[0] = 0;
 817		value->bvec4[1] = 0;
 818		value->bvec4[2] = 0;
 819		value->bvec4[3] = 0;
 820		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 821		_lookupBoolValue(description, name, fieldName, &value->bvec4[0]);
 822		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 823		_lookupBoolValue(description, name, fieldName, &value->bvec4[1]);
 824		snprintf(fieldName, sizeof(fieldName), "%s[2]", field);
 825		_lookupBoolValue(description, name, fieldName, &value->bvec4[2]);
 826		snprintf(fieldName, sizeof(fieldName), "%s[3]", field);
 827		_lookupBoolValue(description, name, fieldName, &value->bvec4[3]);
 828		break;
 829	}
 830}
 831
 832static bool _loadUniform(struct Configuration* description, size_t pass, struct mGLES2Uniform* uniform) {
 833	unsigned passId;
 834	if (sscanf(uniform->name, "pass.%u.uniform.", &passId) < 1 || passId != pass) {
 835		return false;
 836	}
 837	const char* type = ConfigurationGetValue(description, uniform->name, "type");
 838	if (!type) {
 839		return false;
 840	}
 841	if (!strcmp(type, "float")) {
 842		uniform->type = GL_FLOAT;
 843	} else if (!strcmp(type, "float2")) {
 844		uniform->type = GL_FLOAT_VEC2;
 845	} else if (!strcmp(type, "float3")) {
 846		uniform->type = GL_FLOAT_VEC3;
 847	} else if (!strcmp(type, "float4")) {
 848		uniform->type = GL_FLOAT_VEC4;
 849	} else if (!strcmp(type, "float2x2")) {
 850		uniform->type = GL_FLOAT_MAT2;
 851	} else if (!strcmp(type, "float3x3")) {
 852		uniform->type = GL_FLOAT_MAT3;
 853	} else if (!strcmp(type, "float4x4")) {
 854		uniform->type = GL_FLOAT_MAT4;
 855	} else if (!strcmp(type, "int")) {
 856		uniform->type = GL_INT;
 857	} else if (!strcmp(type, "int2")) {
 858		uniform->type = GL_INT_VEC2;
 859	} else if (!strcmp(type, "int3")) {
 860		uniform->type = GL_INT_VEC3;
 861	} else if (!strcmp(type, "int4")) {
 862		uniform->type = GL_INT_VEC4;
 863	} else if (!strcmp(type, "bool")) {
 864		uniform->type = GL_BOOL;
 865	} else if (!strcmp(type, "bool2")) {
 866		uniform->type = GL_BOOL_VEC2;
 867	} else if (!strcmp(type, "bool3")) {
 868		uniform->type = GL_BOOL_VEC3;
 869	} else if (!strcmp(type, "bool4")) {
 870		uniform->type = GL_BOOL_VEC4;
 871	} else {
 872		return false;
 873	}
 874	_loadValue(description, uniform->name, uniform->type, "default", &uniform->value);
 875	_loadValue(description, uniform->name, uniform->type, "min", &uniform->min);
 876	_loadValue(description, uniform->name, uniform->type, "max", &uniform->max);
 877	const char* readable = ConfigurationGetValue(description, uniform->name, "readableName");
 878	if (readable) {
 879		uniform->readableName = strdup(readable);
 880	} else {
 881		uniform->readableName = 0;
 882	}
 883	uniform->name = strdup(strstr(uniform->name, "uniform.") + strlen("uniform."));
 884	return true;
 885}
 886
 887bool mGLES2ShaderLoad(struct VideoShader* shader, struct VDir* dir) {
 888	struct VFile* manifest = dir->openFile(dir, "manifest.ini", O_RDONLY);
 889	if (!manifest) {
 890		return false;
 891	}
 892	bool success = false;
 893	struct Configuration description;
 894	ConfigurationInit(&description);
 895	if (ConfigurationReadVFile(&description, manifest)) {
 896		int inShaders;
 897		success = _lookupIntValue(&description, "shader", "passes", &inShaders);
 898		if (inShaders > MAX_PASSES || inShaders < 1) {
 899			success = false;
 900		}
 901		if (success) {
 902			struct mGLES2Shader* shaderBlock = malloc(sizeof(struct mGLES2Shader) * inShaders);
 903			int n;
 904			for (n = 0; n < inShaders; ++n) {
 905				char passName[12];
 906				snprintf(passName, sizeof(passName), "pass.%u", n);
 907				const char* fs = ConfigurationGetValue(&description, passName, "fragmentShader");
 908				const char* vs = ConfigurationGetValue(&description, passName, "vertexShader");
 909				if (fs && (fs[0] == '.' || strstr(fs, PATH_SEP))) {
 910					success = false;
 911					break;
 912				}
 913				if (vs && (vs[0] == '.' || strstr(vs, PATH_SEP))) {
 914					success = false;
 915					break;
 916				}
 917				char* fssrc = 0;
 918				char* vssrc = 0;
 919				if (fs) {
 920					struct VFile* fsf = dir->openFile(dir, fs, O_RDONLY);
 921					if (!fsf) {
 922						success = false;
 923						break;
 924					}
 925					fssrc = malloc(fsf->size(fsf) + 1);
 926					fssrc[fsf->size(fsf)] = '\0';
 927					fsf->read(fsf, fssrc, fsf->size(fsf));
 928					fsf->close(fsf);
 929				}
 930				if (vs) {
 931					struct VFile* vsf = dir->openFile(dir, vs, O_RDONLY);
 932					if (!vsf) {
 933						success = false;
 934						free(fssrc);
 935						break;
 936					}
 937					vssrc = malloc(vsf->size(vsf) + 1);
 938					vssrc[vsf->size(vsf)] = '\0';
 939					vsf->read(vsf, vssrc, vsf->size(vsf));
 940					vsf->close(vsf);
 941				}
 942				int width = 0;
 943				int height = 0;
 944				int scaling = 0;
 945				_lookupIntValue(&description, passName, "width", &width);
 946				_lookupIntValue(&description, passName, "height", &height);
 947				_lookupIntValue(&description, passName, "integerScaling", &scaling);
 948
 949				struct mGLES2UniformList uniformVector;
 950				mGLES2UniformListInit(&uniformVector, 0);
 951				ConfigurationEnumerateSections(&description, _uniformHandler, &uniformVector);
 952				size_t u;
 953				for (u = 0; u < mGLES2UniformListSize(&uniformVector); ++u) {
 954					struct mGLES2Uniform* uniform = mGLES2UniformListGetPointer(&uniformVector, u);
 955					if (!_loadUniform(&description, n, uniform)) {
 956						mGLES2UniformListShift(&uniformVector, u, 1);
 957						--u;
 958					}
 959				}
 960				u = mGLES2UniformListSize(&uniformVector);
 961				struct mGLES2Uniform* uniformBlock = malloc(sizeof(*uniformBlock) * u);
 962				memcpy(uniformBlock, mGLES2UniformListGetPointer(&uniformVector, 0), sizeof(*uniformBlock) * u);
 963				mGLES2UniformListDeinit(&uniformVector);
 964
 965				mGLES2ShaderInit(&shaderBlock[n], vssrc, fssrc, width, height, scaling, uniformBlock, u);
 966				int b = 0;
 967				_lookupIntValue(&description, passName, "blend", &b);
 968				if (b) {
 969					shaderBlock[n].blend = b;
 970				}
 971				b = 0;
 972				_lookupIntValue(&description, passName, "filter", &b);
 973				if (b) {
 974					shaderBlock[n].filter = b;
 975				}
 976				free(fssrc);
 977				free(vssrc);
 978			}
 979			if (success) {
 980				shader->nPasses = inShaders;
 981				shader->passes = shaderBlock;
 982				shader->name = ConfigurationGetValue(&description, "shader", "name");
 983				if (shader->name) {
 984					shader->name = strdup(shader->name);
 985				}
 986				shader->author = ConfigurationGetValue(&description, "shader", "author");
 987				if (shader->author) {
 988					shader->author = strdup(shader->author);
 989				}
 990				shader->description = ConfigurationGetValue(&description, "shader", "description");
 991				if (shader->description) {
 992					shader->description = strdup(shader->description);
 993				}
 994			} else {
 995				inShaders = n;
 996				for (n = 0; n < inShaders; ++n) {
 997					mGLES2ShaderDeinit(&shaderBlock[n]);
 998				}
 999			}
1000		}
1001	}
1002	manifest->close(manifest);
1003	ConfigurationDeinit(&description);
1004	return success;
1005}
1006
1007void mGLES2ShaderFree(struct VideoShader* shader) {
1008	free((void*) shader->name);
1009	free((void*) shader->author);
1010	free((void*) shader->description);
1011	shader->name = 0;
1012	shader->author = 0;
1013	shader->description = 0;
1014	struct mGLES2Shader* shaders = shader->passes;
1015	size_t n;
1016	for (n = 0; n < shader->nPasses; ++n) {
1017		mGLES2ShaderDeinit(&shaders[n]);
1018		size_t u;
1019		for (u = 0; u < shaders[n].nUniforms; ++u) {
1020			free((void*) shaders[n].uniforms[u].name);
1021			free((void*) shaders[n].uniforms[u].readableName);
1022		}
1023	}
1024	free(shaders);
1025	shader->passes = 0;
1026	shader->nPasses = 0;
1027}