all repos — mgba @ 5930e10cf42562fd4937ae7eb40d1de39f06a5c4

mGBA Game Boy Advance Emulator

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

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