all repos — mgba @ b1a06ed52bd8f9db00dcd722198deedd8a184f69

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