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