all repos — mgba @ 4350dbd9a966301ab60f31d8966311e4a1944ebb

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 _gl32VHeader =
  25	"#version 150 core\n"
  26	"#define attribute in\n"
  27	"#define varying out\n";
  28
  29static const GLchar* const _gl32FHeader =
  30	"#version 150 core\n"
  31	"#define varying in\n"
  32	"#define texture2D texture\n"
  33	"out vec4 compat_FragColor;\n"
  34	"#define gl_FragColor compat_FragColor\n";
  35
  36static const char* const _vertexShader =
  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 _nullVertexShader =
  46	"attribute vec4 position;\n"
  47	"varying vec2 texCoord;\n"
  48
  49	"void main() {\n"
  50	"	gl_Position = position;\n"
  51	"	texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);\n"
  52	"}";
  53
  54static const char* const _fragmentShader =
  55	"varying vec2 texCoord;\n"
  56	"uniform sampler2D tex;\n"
  57	"uniform float gamma;\n"
  58	"uniform vec3 desaturation;\n"
  59	"uniform vec3 scale;\n"
  60	"uniform vec3 bias;\n"
  61
  62	"void main() {\n"
  63	"	vec4 color = texture2D(tex, texCoord);\n"
  64	"	color.a = 1.;\n"
  65	"	float average = dot(color.rgb, vec3(1.)) / 3.;\n"
  66	"	color.rgb = mix(color.rgb, vec3(average), desaturation);\n"
  67	"	color.rgb = scale * pow(color.rgb, vec3(gamma, gamma, gamma)) + bias;\n"
  68	"	gl_FragColor = color;\n"
  69	"}";
  70
  71static const char* const _nullFragmentShader =
  72	"varying vec2 texCoord;\n"
  73	"uniform sampler2D tex;\n"
  74
  75	"void main() {\n"
  76	"	vec4 color = texture2D(tex, texCoord);\n"
  77	"	color.a = 1.;\n"
  78	"	gl_FragColor = color;\n"
  79	"}";
  80
  81static const char* const _interframeFragmentShader =
  82	"varying vec2 texCoord;\n"
  83	"uniform sampler2D tex;\n"
  84
  85	"void main() {\n"
  86	"	vec4 color = texture2D(tex, texCoord);\n"
  87	"	color.a = 0.5;\n"
  88	"	gl_FragColor = color;\n"
  89	"}";
  90
  91static const GLfloat _vertices[] = {
  92	-1.f, -1.f,
  93	-1.f, 1.f,
  94	1.f, 1.f,
  95	1.f, -1.f,
  96};
  97
  98static void mGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
  99	UNUSED(handle);
 100	struct mGLES2Context* context = (struct mGLES2Context*) v;
 101	glGenTextures(1, &context->tex);
 102	glBindTexture(GL_TEXTURE_2D, context->tex);
 103	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 104	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 105
 106	glGenBuffers(1, &context->vbo);
 107	glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
 108	glBufferData(GL_ARRAY_BUFFER, sizeof(_vertices), _vertices, GL_STATIC_DRAW);
 109
 110	struct mGLES2Uniform* uniforms = malloc(sizeof(struct mGLES2Uniform) * 4);
 111	uniforms[0].name = "gamma";
 112	uniforms[0].readableName = "Gamma";
 113	uniforms[0].type = GL_FLOAT;
 114	uniforms[0].value.f = 1.0f;
 115	uniforms[0].min.f = 0.1f;
 116	uniforms[0].max.f = 3.0f;
 117	uniforms[1].name = "scale";
 118	uniforms[1].readableName = "Scale";
 119	uniforms[1].type = GL_FLOAT_VEC3;
 120	uniforms[1].value.fvec3[0] = 1.0f;
 121	uniforms[1].value.fvec3[1] = 1.0f;
 122	uniforms[1].value.fvec3[2] = 1.0f;
 123	uniforms[1].min.fvec3[0] = -1.0f;
 124	uniforms[1].min.fvec3[1] = -1.0f;
 125	uniforms[1].min.fvec3[2] = -1.0f;
 126	uniforms[1].max.fvec3[0] = 2.0f;
 127	uniforms[1].max.fvec3[1] = 2.0f;
 128	uniforms[1].max.fvec3[2] = 2.0f;
 129	uniforms[2].name = "bias";
 130	uniforms[2].readableName = "Bias";
 131	uniforms[2].type = GL_FLOAT_VEC3;
 132	uniforms[2].value.fvec3[0] = 0.0f;
 133	uniforms[2].value.fvec3[1] = 0.0f;
 134	uniforms[2].value.fvec3[2] = 0.0f;
 135	uniforms[2].min.fvec3[0] = -1.0f;
 136	uniforms[2].min.fvec3[1] = -1.0f;
 137	uniforms[2].min.fvec3[2] = -1.0f;
 138	uniforms[2].max.fvec3[0] = 1.0f;
 139	uniforms[2].max.fvec3[1] = 1.0f;
 140	uniforms[2].max.fvec3[2] = 1.0f;
 141	uniforms[3].name = "desaturation";
 142	uniforms[3].readableName = "Desaturation";
 143	uniforms[3].type = GL_FLOAT_VEC3;
 144	uniforms[3].value.fvec3[0] = 0.0f;
 145	uniforms[3].value.fvec3[1] = 0.0f;
 146	uniforms[3].value.fvec3[2] = 0.0f;
 147	uniforms[3].min.fvec3[0] = 0.0f;
 148	uniforms[3].min.fvec3[1] = 0.0f;
 149	uniforms[3].min.fvec3[2] = 0.0f;
 150	uniforms[3].max.fvec3[0] = 1.0f;
 151	uniforms[3].max.fvec3[1] = 1.0f;
 152	uniforms[3].max.fvec3[2] = 1.0f;
 153	mGLES2ShaderInit(&context->initialShader, _vertexShader, _fragmentShader, -1, -1, false, uniforms, 4);
 154	mGLES2ShaderInit(&context->finalShader, 0, 0, 0, 0, false, 0, 0);
 155	mGLES2ShaderInit(&context->interframeShader, 0, _interframeFragmentShader, -1, -1, false, 0, 0);
 156
 157	if (context->initialShader.vao != (GLuint) -1) {
 158		glBindVertexArray(context->initialShader.vao);
 159		glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
 160		glBindVertexArray(context->finalShader.vao);
 161		glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
 162		glBindVertexArray(context->interframeShader.vao);
 163		glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
 164		glBindVertexArray(0);
 165	}
 166
 167	glDeleteFramebuffers(1, &context->finalShader.fbo);
 168	glDeleteTextures(1, &context->finalShader.tex);
 169	context->finalShader.fbo = 0;
 170	context->finalShader.tex = 0;
 171}
 172
 173static void mGLES2ContextSetDimensions(struct VideoBackend* v, unsigned width, unsigned height) {
 174	struct mGLES2Context* context = (struct mGLES2Context*) v;
 175	v->width = width;
 176	v->height = height;
 177
 178	glBindTexture(GL_TEXTURE_2D, context->tex);
 179#ifdef COLOR_16_BIT
 180#ifdef COLOR_5_6_5
 181	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0);
 182#else
 183	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0);
 184#endif
 185#elif defined(__BIG_ENDIAN__)
 186	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 0);
 187#else
 188	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
 189#endif
 190
 191	size_t n;
 192	for (n = 0; n < context->nShaders; ++n) {
 193		if (context->shaders[n].width < 0 || context->shaders[n].height < 0) {
 194			context->shaders[n].dirty = true;
 195		}
 196	}
 197	context->initialShader.dirty = true;
 198	context->interframeShader.dirty = true;
 199}
 200
 201static void mGLES2ContextDeinit(struct VideoBackend* v) {
 202	struct mGLES2Context* context = (struct mGLES2Context*) v;
 203	glDeleteTextures(1, &context->tex);
 204	glDeleteBuffers(1, &context->vbo);
 205	mGLES2ShaderDeinit(&context->initialShader);
 206	mGLES2ShaderDeinit(&context->finalShader);
 207	mGLES2ShaderDeinit(&context->interframeShader);
 208	free(context->initialShader.uniforms);
 209}
 210
 211static void mGLES2ContextResized(struct VideoBackend* v, unsigned w, unsigned h) {
 212	struct mGLES2Context* context = (struct mGLES2Context*) v;
 213	unsigned drawW = w;
 214	unsigned drawH = h;
 215	if (v->lockAspectRatio) {
 216		if (w * v->height > h * v->width) {
 217			drawW = h * v->width / v->height;
 218		} else if (w * v->height < h * v->width) {
 219			drawH = w * v->height / v->width;
 220		}
 221	}
 222	if (v->lockIntegerScaling) {
 223		if (drawW >= v->width) {
 224			drawW -= drawW % v->width;
 225		}
 226		if (drawH >= v->height) {
 227			drawH -= drawH % v->height;
 228		}
 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 "))) {
 461		shaderBuffer[0] = _gl32VHeader;
 462	} else {
 463		shaderBuffer[0] = _gles2Header;
 464	}
 465	if (vs) {
 466		shaderBuffer[1] = vs;
 467	} else {
 468		shaderBuffer[1] = _nullVertexShader;
 469	}
 470	glShaderSource(shader->vertexShader, 2, shaderBuffer, 0);
 471
 472	if (strncmp((const char*) version, "OpenGL ES ", strlen("OpenGL ES "))) {
 473		shaderBuffer[0] = _gl32FHeader;
 474	}
 475	if (fs) {
 476		shaderBuffer[1] = fs;
 477	} else {
 478		shaderBuffer[1] = _nullFragmentShader;
 479	}
 480	glShaderSource(shader->fragmentShader, 2, shaderBuffer, 0);
 481
 482	glAttachShader(shader->program, shader->vertexShader);
 483	glAttachShader(shader->program, shader->fragmentShader);
 484	char log[1024];
 485	glCompileShader(shader->fragmentShader);
 486	glGetShaderInfoLog(shader->fragmentShader, 1024, 0, log);
 487	if (log[0]) {
 488		mLOG(OPENGL, ERROR, "%s\n", log);
 489	}
 490	glCompileShader(shader->vertexShader);
 491	glGetShaderInfoLog(shader->vertexShader, 1024, 0, log);
 492	if (log[0]) {
 493		mLOG(OPENGL, ERROR, "%s\n", log);
 494	}
 495	glLinkProgram(shader->program);
 496	glGetProgramInfoLog(shader->program, 1024, 0, log);
 497	if (log[0]) {
 498		mLOG(OPENGL, ERROR, "%s\n", log);
 499	}
 500
 501	shader->texLocation = glGetUniformLocation(shader->program, "tex");
 502	shader->texSizeLocation = glGetUniformLocation(shader->program, "texSize");
 503	shader->positionLocation = glGetAttribLocation(shader->program, "position");
 504	size_t i;
 505	for (i = 0; i < shader->nUniforms; ++i) {
 506		shader->uniforms[i].location = glGetUniformLocation(shader->program, shader->uniforms[i].name);
 507	}
 508
 509	const GLubyte* extensions = glGetString(GL_EXTENSIONS);
 510	if (shaderBuffer[0] == _gles2Header || version[0] >= '3' || (extensions && strstr((const char*) extensions, "_vertex_array_object") != NULL)) {
 511		glGenVertexArrays(1, &shader->vao);
 512		glBindVertexArray(shader->vao);
 513		glEnableVertexAttribArray(shader->positionLocation);
 514		glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
 515		glBindVertexArray(0);
 516	} else {
 517		shader->vao = -1;
 518	}
 519
 520	glBindFramebuffer(GL_FRAMEBUFFER, 0);
 521}
 522
 523void mGLES2ShaderDeinit(struct mGLES2Shader* shader) {
 524	glDeleteTextures(1, &shader->tex);
 525	glDeleteShader(shader->fragmentShader);
 526	glDeleteProgram(shader->program);
 527	glDeleteFramebuffers(1, &shader->fbo);
 528	if (shader->vao != (GLuint) -1) {
 529		glDeleteVertexArrays(1, &shader->vao);
 530	}
 531}
 532
 533void mGLES2ShaderAttach(struct mGLES2Context* context, struct mGLES2Shader* shaders, size_t nShaders) {
 534	if (context->shaders) {
 535		if (context->shaders == shaders && context->nShaders == nShaders) {
 536			return;
 537		}
 538		mGLES2ShaderDetach(context);
 539	}
 540	context->shaders = shaders;
 541	context->nShaders = nShaders;
 542	size_t i;
 543	for (i = 0; i < nShaders; ++i) {
 544		glBindFramebuffer(GL_FRAMEBUFFER, context->shaders[i].fbo);
 545		glClearColor(0.f, 0.f, 0.f, 1.f);
 546		glClear(GL_COLOR_BUFFER_BIT);
 547
 548		if (context->shaders[i].vao != (GLuint) -1) {
 549			glBindVertexArray(context->shaders[i].vao);
 550			glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
 551			glEnableVertexAttribArray(context->shaders[i].positionLocation);
 552			glVertexAttribPointer(context->shaders[i].positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
 553		}
 554	}
 555	if (context->initialShader.vao != (GLuint) -1) {
 556		glBindVertexArray(0);
 557	}
 558	glBindFramebuffer(GL_FRAMEBUFFER, 0);
 559}
 560
 561void mGLES2ShaderDetach(struct mGLES2Context* context) {
 562	if (!context->shaders) {
 563		return;
 564	}
 565	context->shaders = 0;
 566	context->nShaders = 0;
 567}
 568
 569static bool _lookupIntValue(const struct Configuration* config, const char* section, const char* key, int* out) {
 570	const char* charValue = ConfigurationGetValue(config, section, key);
 571	if (!charValue) {
 572		return false;
 573	}
 574	char* end;
 575	unsigned long value = strtol(charValue, &end, 10);
 576	if (*end) {
 577		return false;
 578	}
 579	*out = value;
 580	return true;
 581}
 582
 583static bool _lookupFloatValue(const struct Configuration* config, const char* section, const char* key, float* out) {
 584	const char* charValue = ConfigurationGetValue(config, section, key);
 585	if (!charValue) {
 586		return false;
 587	}
 588	char* end;
 589	float value = strtof_u(charValue, &end);
 590	if (*end) {
 591		return false;
 592	}
 593	*out = value;
 594	return true;
 595}
 596
 597static bool _lookupBoolValue(const struct Configuration* config, const char* section, const char* key, GLboolean* out) {
 598	const char* charValue = ConfigurationGetValue(config, section, key);
 599	if (!charValue) {
 600		return false;
 601	}
 602	if (!strcmp(charValue, "true")) {
 603		*out = GL_TRUE;
 604		return true;
 605	}
 606	if (!strcmp(charValue, "false")) {
 607		*out = GL_FALSE;
 608		return true;
 609	}
 610	char* end;
 611	unsigned long value = strtol(charValue, &end, 10);
 612	if (*end) {
 613		return false;
 614	}
 615	*out = value;
 616	return true;
 617}
 618
 619DECLARE_VECTOR(mGLES2UniformList, struct mGLES2Uniform);
 620DEFINE_VECTOR(mGLES2UniformList, struct mGLES2Uniform);
 621
 622static void _uniformHandler(const char* sectionName, void* user) {
 623	struct mGLES2UniformList* uniforms = user;
 624	unsigned passId;
 625	int sentinel;
 626	if (sscanf(sectionName, "pass.%u.uniform.%n", &passId, &sentinel) < 1) {
 627		return;
 628	}
 629	struct mGLES2Uniform* u = mGLES2UniformListAppend(uniforms);
 630	u->name = sectionName;
 631}
 632
 633
 634static void _loadValue(struct Configuration* description, const char* name, GLenum type, const char* field, union mGLES2UniformValue* value) {
 635	char fieldName[16];
 636	switch (type) {
 637	case GL_FLOAT:
 638		value->f = 0;
 639		_lookupFloatValue(description, name, field, &value->f);
 640		break;
 641	case GL_FLOAT_VEC2:
 642		value->fvec2[0] = 0;
 643		value->fvec2[1] = 0;
 644		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 645		_lookupFloatValue(description, name, fieldName, &value->fvec2[0]);
 646		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 647		_lookupFloatValue(description, name, fieldName, &value->fvec2[1]);
 648		break;
 649	case GL_FLOAT_VEC3:
 650		value->fvec3[0] = 0;
 651		value->fvec3[1] = 0;
 652		value->fvec3[2] = 0;
 653		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 654		_lookupFloatValue(description, name, fieldName, &value->fvec3[0]);
 655		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 656		_lookupFloatValue(description, name, fieldName, &value->fvec3[1]);
 657		snprintf(fieldName, sizeof(fieldName), "%s[2]", field);
 658		_lookupFloatValue(description, name, fieldName, &value->fvec3[2]);
 659		break;
 660	case GL_FLOAT_VEC4:
 661		value->fvec4[0] = 0;
 662		value->fvec4[1] = 0;
 663		value->fvec4[2] = 0;
 664		value->fvec4[3] = 0;
 665		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 666		_lookupFloatValue(description, name, fieldName, &value->fvec4[0]);
 667		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 668		_lookupFloatValue(description, name, fieldName, &value->fvec4[1]);
 669		snprintf(fieldName, sizeof(fieldName), "%s[2]", field);
 670		_lookupFloatValue(description, name, fieldName, &value->fvec4[2]);
 671		snprintf(fieldName, sizeof(fieldName), "%s[3]", field);
 672		_lookupFloatValue(description, name, fieldName, &value->fvec4[3]);
 673		break;
 674	case GL_FLOAT_MAT2:
 675		value->fmat2x2[0] = 0;
 676		value->fmat2x2[1] = 0;
 677		value->fmat2x2[2] = 0;
 678		value->fmat2x2[3] = 0;
 679		snprintf(fieldName, sizeof(fieldName), "%s[0,0]", field);
 680		_lookupFloatValue(description, name, fieldName, &value->fmat2x2[0]);
 681		snprintf(fieldName, sizeof(fieldName), "%s[0,1]", field);
 682		_lookupFloatValue(description, name, fieldName, &value->fmat2x2[1]);
 683		snprintf(fieldName, sizeof(fieldName), "%s[1,0]", field);
 684		_lookupFloatValue(description, name, fieldName, &value->fmat2x2[2]);
 685		snprintf(fieldName, sizeof(fieldName), "%s[1,1]", field);
 686		_lookupFloatValue(description, name, fieldName, &value->fmat2x2[3]);
 687		break;
 688	case GL_FLOAT_MAT3:
 689		value->fmat3x3[0] = 0;
 690		value->fmat3x3[1] = 0;
 691		value->fmat3x3[2] = 0;
 692		value->fmat3x3[3] = 0;
 693		value->fmat3x3[4] = 0;
 694		value->fmat3x3[5] = 0;
 695		value->fmat3x3[6] = 0;
 696		value->fmat3x3[7] = 0;
 697		value->fmat3x3[8] = 0;
 698		snprintf(fieldName, sizeof(fieldName), "%s[0,0]", field);
 699		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[0]);
 700		snprintf(fieldName, sizeof(fieldName), "%s[0,1]", field);
 701		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[1]);
 702		snprintf(fieldName, sizeof(fieldName), "%s[0,2]", field);
 703		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[2]);
 704		snprintf(fieldName, sizeof(fieldName), "%s[1,0]", field);
 705		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[3]);
 706		snprintf(fieldName, sizeof(fieldName), "%s[1,1]", field);
 707		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[4]);
 708		snprintf(fieldName, sizeof(fieldName), "%s[1,2]", field);
 709		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[5]);
 710		snprintf(fieldName, sizeof(fieldName), "%s[2,0]", field);
 711		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[6]);
 712		snprintf(fieldName, sizeof(fieldName), "%s[2,1]", field);
 713		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[7]);
 714		snprintf(fieldName, sizeof(fieldName), "%s[2,2]", field);
 715		_lookupFloatValue(description, name, fieldName, &value->fmat3x3[8]);
 716		break;
 717	case GL_FLOAT_MAT4:
 718		value->fmat4x4[0] = 0;
 719		value->fmat4x4[1] = 0;
 720		value->fmat4x4[2] = 0;
 721		value->fmat4x4[3] = 0;
 722		value->fmat4x4[4] = 0;
 723		value->fmat4x4[5] = 0;
 724		value->fmat4x4[6] = 0;
 725		value->fmat4x4[7] = 0;
 726		value->fmat4x4[8] = 0;
 727		value->fmat4x4[9] = 0;
 728		value->fmat4x4[10] = 0;
 729		value->fmat4x4[11] = 0;
 730		value->fmat4x4[12] = 0;
 731		value->fmat4x4[13] = 0;
 732		value->fmat4x4[14] = 0;
 733		value->fmat4x4[15] = 0;
 734		snprintf(fieldName, sizeof(fieldName), "%s[0,0]", field);
 735		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[0]);
 736		snprintf(fieldName, sizeof(fieldName), "%s[0,1]", field);
 737		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[1]);
 738		snprintf(fieldName, sizeof(fieldName), "%s[0,2]", field);
 739		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[2]);
 740		snprintf(fieldName, sizeof(fieldName), "%s[0,3]", field);
 741		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[3]);
 742		snprintf(fieldName, sizeof(fieldName), "%s[1,0]", field);
 743		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[4]);
 744		snprintf(fieldName, sizeof(fieldName), "%s[1,1]", field);
 745		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[5]);
 746		snprintf(fieldName, sizeof(fieldName), "%s[1,2]", field);
 747		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[6]);
 748		snprintf(fieldName, sizeof(fieldName), "%s[1,3]", field);
 749		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[7]);
 750		snprintf(fieldName, sizeof(fieldName), "%s[2,0]", field);
 751		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[8]);
 752		snprintf(fieldName, sizeof(fieldName), "%s[2,1]", field);
 753		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[9]);
 754		snprintf(fieldName, sizeof(fieldName), "%s[2,2]", field);
 755		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[10]);
 756		snprintf(fieldName, sizeof(fieldName), "%s[2,3]", field);
 757		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[11]);
 758		snprintf(fieldName, sizeof(fieldName), "%s[3,0]", field);
 759		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[12]);
 760		snprintf(fieldName, sizeof(fieldName), "%s[3,1]", field);
 761		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[13]);
 762		snprintf(fieldName, sizeof(fieldName), "%s[3,2]", field);
 763		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[14]);
 764		snprintf(fieldName, sizeof(fieldName), "%s[3,3]", field);
 765		_lookupFloatValue(description, name, fieldName, &value->fmat4x4[15]);
 766		break;
 767	case GL_INT:
 768		value->i = 0;
 769		_lookupIntValue(description, name, field, &value->i);
 770		break;
 771	case GL_INT_VEC2:
 772		value->ivec2[0] = 0;
 773		value->ivec2[1] = 0;
 774		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 775		_lookupIntValue(description, name, fieldName, &value->ivec2[0]);
 776		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 777		_lookupIntValue(description, name, fieldName, &value->ivec2[1]);
 778		break;
 779	case GL_INT_VEC3:
 780		value->ivec3[0] = 0;
 781		value->ivec3[1] = 0;
 782		value->ivec3[2] = 0;
 783		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 784		_lookupIntValue(description, name, fieldName, &value->ivec3[0]);
 785		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 786		_lookupIntValue(description, name, fieldName, &value->ivec3[1]);
 787		snprintf(fieldName, sizeof(fieldName), "%s[2]", field);
 788		_lookupIntValue(description, name, fieldName, &value->ivec3[2]);
 789		break;
 790	case GL_INT_VEC4:
 791		value->ivec4[0] = 0;
 792		value->ivec4[1] = 0;
 793		value->ivec4[2] = 0;
 794		value->ivec4[3] = 0;
 795		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 796		_lookupIntValue(description, name, fieldName, &value->ivec4[0]);
 797		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 798		_lookupIntValue(description, name, fieldName, &value->ivec4[1]);
 799		snprintf(fieldName, sizeof(fieldName), "%s[2]", field);
 800		_lookupIntValue(description, name, fieldName, &value->ivec4[2]);
 801		snprintf(fieldName, sizeof(fieldName), "%s[3]", field);
 802		_lookupIntValue(description, name, fieldName, &value->ivec4[3]);
 803		break;
 804	case GL_BOOL:
 805		value->b = 0;
 806		_lookupBoolValue(description, name, field, &value->b);
 807		break;
 808	case GL_BOOL_VEC2:
 809		value->bvec2[0] = 0;
 810		value->bvec2[1] = 0;
 811		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 812		_lookupBoolValue(description, name, fieldName, &value->bvec2[0]);
 813		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 814		_lookupBoolValue(description, name, fieldName, &value->bvec2[1]);
 815		break;
 816	case GL_BOOL_VEC3:
 817		value->bvec3[0] = 0;
 818		value->bvec3[1] = 0;
 819		value->bvec3[2] = 0;
 820		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 821		_lookupBoolValue(description, name, fieldName, &value->bvec3[0]);
 822		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 823		_lookupBoolValue(description, name, fieldName, &value->bvec3[1]);
 824		snprintf(fieldName, sizeof(fieldName), "%s[2]", field);
 825		_lookupBoolValue(description, name, fieldName, &value->bvec3[2]);
 826		break;
 827	case GL_BOOL_VEC4:
 828		value->bvec4[0] = 0;
 829		value->bvec4[1] = 0;
 830		value->bvec4[2] = 0;
 831		value->bvec4[3] = 0;
 832		snprintf(fieldName, sizeof(fieldName), "%s[0]", field);
 833		_lookupBoolValue(description, name, fieldName, &value->bvec4[0]);
 834		snprintf(fieldName, sizeof(fieldName), "%s[1]", field);
 835		_lookupBoolValue(description, name, fieldName, &value->bvec4[1]);
 836		snprintf(fieldName, sizeof(fieldName), "%s[2]", field);
 837		_lookupBoolValue(description, name, fieldName, &value->bvec4[2]);
 838		snprintf(fieldName, sizeof(fieldName), "%s[3]", field);
 839		_lookupBoolValue(description, name, fieldName, &value->bvec4[3]);
 840		break;
 841	}
 842}
 843
 844static bool _loadUniform(struct Configuration* description, size_t pass, struct mGLES2Uniform* uniform) {
 845	unsigned passId;
 846	if (sscanf(uniform->name, "pass.%u.uniform.", &passId) < 1 || passId != pass) {
 847		return false;
 848	}
 849	const char* type = ConfigurationGetValue(description, uniform->name, "type");
 850	if (!type) {
 851		return false;
 852	}
 853	if (!strcmp(type, "float")) {
 854		uniform->type = GL_FLOAT;
 855	} else if (!strcmp(type, "float2")) {
 856		uniform->type = GL_FLOAT_VEC2;
 857	} else if (!strcmp(type, "float3")) {
 858		uniform->type = GL_FLOAT_VEC3;
 859	} else if (!strcmp(type, "float4")) {
 860		uniform->type = GL_FLOAT_VEC4;
 861	} else if (!strcmp(type, "float2x2")) {
 862		uniform->type = GL_FLOAT_MAT2;
 863	} else if (!strcmp(type, "float3x3")) {
 864		uniform->type = GL_FLOAT_MAT3;
 865	} else if (!strcmp(type, "float4x4")) {
 866		uniform->type = GL_FLOAT_MAT4;
 867	} else if (!strcmp(type, "int")) {
 868		uniform->type = GL_INT;
 869	} else if (!strcmp(type, "int2")) {
 870		uniform->type = GL_INT_VEC2;
 871	} else if (!strcmp(type, "int3")) {
 872		uniform->type = GL_INT_VEC3;
 873	} else if (!strcmp(type, "int4")) {
 874		uniform->type = GL_INT_VEC4;
 875	} else if (!strcmp(type, "bool")) {
 876		uniform->type = GL_BOOL;
 877	} else if (!strcmp(type, "bool2")) {
 878		uniform->type = GL_BOOL_VEC2;
 879	} else if (!strcmp(type, "bool3")) {
 880		uniform->type = GL_BOOL_VEC3;
 881	} else if (!strcmp(type, "bool4")) {
 882		uniform->type = GL_BOOL_VEC4;
 883	} else {
 884		return false;
 885	}
 886	_loadValue(description, uniform->name, uniform->type, "default", &uniform->value);
 887	_loadValue(description, uniform->name, uniform->type, "min", &uniform->min);
 888	_loadValue(description, uniform->name, uniform->type, "max", &uniform->max);
 889	const char* readable = ConfigurationGetValue(description, uniform->name, "readableName");
 890	if (readable) {
 891		uniform->readableName = strdup(readable);
 892	} else {
 893		uniform->readableName = 0;
 894	}
 895	uniform->name = strdup(strstr(uniform->name, "uniform.") + strlen("uniform."));
 896	return true;
 897}
 898
 899bool mGLES2ShaderLoad(struct VideoShader* shader, struct VDir* dir) {
 900	struct VFile* manifest = dir->openFile(dir, "manifest.ini", O_RDONLY);
 901	if (!manifest) {
 902		return false;
 903	}
 904	bool success = false;
 905	struct Configuration description;
 906	ConfigurationInit(&description);
 907	if (ConfigurationReadVFile(&description, manifest)) {
 908		int inShaders;
 909		success = _lookupIntValue(&description, "shader", "passes", &inShaders);
 910		if (inShaders > MAX_PASSES || inShaders < 1) {
 911			success = false;
 912		}
 913		if (success) {
 914			struct mGLES2Shader* shaderBlock = malloc(sizeof(struct mGLES2Shader) * inShaders);
 915			int n;
 916			for (n = 0; n < inShaders; ++n) {
 917				char passName[12];
 918				snprintf(passName, sizeof(passName), "pass.%u", n);
 919				const char* fs = ConfigurationGetValue(&description, passName, "fragmentShader");
 920				const char* vs = ConfigurationGetValue(&description, passName, "vertexShader");
 921				if (fs && (fs[0] == '.' || strstr(fs, PATH_SEP))) {
 922					success = false;
 923					break;
 924				}
 925				if (vs && (vs[0] == '.' || strstr(vs, PATH_SEP))) {
 926					success = false;
 927					break;
 928				}
 929				char* fssrc = 0;
 930				char* vssrc = 0;
 931				if (fs) {
 932					struct VFile* fsf = dir->openFile(dir, fs, O_RDONLY);
 933					if (!fsf) {
 934						success = false;
 935						break;
 936					}
 937					fssrc = malloc(fsf->size(fsf) + 1);
 938					fssrc[fsf->size(fsf)] = '\0';
 939					fsf->read(fsf, fssrc, fsf->size(fsf));
 940					fsf->close(fsf);
 941				}
 942				if (vs) {
 943					struct VFile* vsf = dir->openFile(dir, vs, O_RDONLY);
 944					if (!vsf) {
 945						success = false;
 946						free(fssrc);
 947						break;
 948					}
 949					vssrc = malloc(vsf->size(vsf) + 1);
 950					vssrc[vsf->size(vsf)] = '\0';
 951					vsf->read(vsf, vssrc, vsf->size(vsf));
 952					vsf->close(vsf);
 953				}
 954				int width = 0;
 955				int height = 0;
 956				int scaling = 0;
 957				_lookupIntValue(&description, passName, "width", &width);
 958				_lookupIntValue(&description, passName, "height", &height);
 959				_lookupIntValue(&description, passName, "integerScaling", &scaling);
 960
 961				struct mGLES2UniformList uniformVector;
 962				mGLES2UniformListInit(&uniformVector, 0);
 963				ConfigurationEnumerateSections(&description, _uniformHandler, &uniformVector);
 964				size_t u;
 965				for (u = 0; u < mGLES2UniformListSize(&uniformVector); ++u) {
 966					struct mGLES2Uniform* uniform = mGLES2UniformListGetPointer(&uniformVector, u);
 967					if (!_loadUniform(&description, n, uniform)) {
 968						mGLES2UniformListShift(&uniformVector, u, 1);
 969						--u;
 970					}
 971				}
 972				u = mGLES2UniformListSize(&uniformVector);
 973				struct mGLES2Uniform* uniformBlock = malloc(sizeof(*uniformBlock) * u);
 974				memcpy(uniformBlock, mGLES2UniformListGetPointer(&uniformVector, 0), sizeof(*uniformBlock) * u);
 975				mGLES2UniformListDeinit(&uniformVector);
 976
 977				mGLES2ShaderInit(&shaderBlock[n], vssrc, fssrc, width, height, scaling, uniformBlock, u);
 978				int b = 0;
 979				_lookupIntValue(&description, passName, "blend", &b);
 980				if (b) {
 981					shaderBlock[n].blend = b;
 982				}
 983				b = 0;
 984				_lookupIntValue(&description, passName, "filter", &b);
 985				if (b) {
 986					shaderBlock[n].filter = b;
 987				}
 988				free(fssrc);
 989				free(vssrc);
 990			}
 991			if (success) {
 992				shader->nPasses = inShaders;
 993				shader->passes = shaderBlock;
 994				shader->name = ConfigurationGetValue(&description, "shader", "name");
 995				if (shader->name) {
 996					shader->name = strdup(shader->name);
 997				}
 998				shader->author = ConfigurationGetValue(&description, "shader", "author");
 999				if (shader->author) {
1000					shader->author = strdup(shader->author);
1001				}
1002				shader->description = ConfigurationGetValue(&description, "shader", "description");
1003				if (shader->description) {
1004					shader->description = strdup(shader->description);
1005				}
1006			} else {
1007				inShaders = n;
1008				for (n = 0; n < inShaders; ++n) {
1009					mGLES2ShaderDeinit(&shaderBlock[n]);
1010				}
1011			}
1012		}
1013	}
1014	manifest->close(manifest);
1015	ConfigurationDeinit(&description);
1016	return success;
1017}
1018
1019void mGLES2ShaderFree(struct VideoShader* shader) {
1020	free((void*) shader->name);
1021	free((void*) shader->author);
1022	free((void*) shader->description);
1023	shader->name = 0;
1024	shader->author = 0;
1025	shader->description = 0;
1026	struct mGLES2Shader* shaders = shader->passes;
1027	size_t n;
1028	for (n = 0; n < shader->nPasses; ++n) {
1029		mGLES2ShaderDeinit(&shaders[n]);
1030		size_t u;
1031		for (u = 0; u < shaders[n].nUniforms; ++u) {
1032			free((void*) shaders[n].uniforms[u].name);
1033			free((void*) shaders[n].uniforms[u].readableName);
1034		}
1035	}
1036	free(shaders);
1037	shader->passes = 0;
1038	shader->nPasses = 0;
1039}