all repos — mgba @ 453791d28987558d3d2eb3d4facc829b84ab8257

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