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 "gba/video.h"
9#include "util/configuration.h"
10#include "util/vfs.h"
11
12#define MAX_PASSES 8
13
14static const char* const _vertexShader =
15 "attribute vec4 position;\n"
16 "varying vec2 texCoord;\n"
17
18 "void main() {\n"
19 " gl_Position = position;\n"
20 " texCoord = (position.st + vec2(1.0, -1.0)) * vec2(0.5, -0.5);\n"
21 "}";
22
23static const char* const _nullVertexShader =
24 "attribute vec4 position;\n"
25 "varying vec2 texCoord;\n"
26
27 "void main() {\n"
28 " gl_Position = position;\n"
29 " texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);\n"
30 "}";
31
32static const char* const _fragmentShader =
33 "varying vec2 texCoord;\n"
34 "uniform sampler2D tex;\n"
35 "uniform float gamma;\n"
36 "uniform vec3 scale;\n"
37 "uniform vec3 bias;\n"
38
39 "void main() {\n"
40 " vec4 color = texture2D(tex, texCoord);\n"
41 " color.a = 1.;\n"
42 " color.rgb = scale * pow(color.rgb, vec3(gamma, gamma, gamma)) + bias;\n"
43 " gl_FragColor = color;\n"
44 "}";
45
46static const char* const _nullFragmentShader =
47 "varying vec2 texCoord;\n"
48 "uniform sampler2D tex;\n"
49
50 "void main() {\n"
51 " vec4 color = texture2D(tex, texCoord);\n"
52 " color.a = 1.;\n"
53 " gl_FragColor = color;\n"
54 "}";
55
56static const GLfloat _vertices[] = {
57 -1.f, -1.f,
58 -1.f, 1.f,
59 1.f, 1.f,
60 1.f, -1.f,
61};
62
63
64static void GBAGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
65 UNUSED(handle);
66 struct GBAGLES2Context* context = (struct GBAGLES2Context*) v;
67 glGenTextures(1, &context->tex);
68 glBindTexture(GL_TEXTURE_2D, context->tex);
69 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
70 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
71
72#ifdef COLOR_16_BIT
73#ifdef COLOR_5_6_5
74 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0);
75#else
76 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0);
77#endif
78#else
79 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
80#endif
81
82 glClearColor(0.f, 0.f, 0.f, 1.f);
83
84 struct GBAGLES2Uniform* uniforms = malloc(sizeof(struct GBAGLES2Uniform) * 3);
85 uniforms[0].name = "gamma";
86 uniforms[0].type = GL_FLOAT;
87 uniforms[0].value.f = 1.0f;
88 uniforms[1].name = "scale";
89 uniforms[1].type = GL_FLOAT_VEC3;
90 uniforms[1].value.fvec3[0] = 1.0f;
91 uniforms[1].value.fvec3[1] = 1.0f;
92 uniforms[1].value.fvec3[2] = 1.0f;
93 uniforms[2].name = "bias";
94 uniforms[2].type = GL_FLOAT_VEC3;
95 uniforms[2].value.fvec3[0] = 0.0f;
96 uniforms[2].value.fvec3[1] = 0.0f;
97 uniforms[2].value.fvec3[2] = 0.0f;
98 GBAGLES2ShaderInit(&context->initialShader, _vertexShader, _fragmentShader, -1, -1, uniforms, 3);
99 GBAGLES2ShaderInit(&context->finalShader, 0, 0, 0, 0, 0, 0);
100 glDeleteFramebuffers(1, &context->finalShader.fbo);
101 context->finalShader.fbo = 0;
102}
103
104static void GBAGLES2ContextDeinit(struct VideoBackend* v) {
105 struct GBAGLES2Context* context = (struct GBAGLES2Context*) v;
106 glDeleteTextures(1, &context->tex);
107 GBAGLES2ShaderDeinit(&context->initialShader);
108 GBAGLES2ShaderDeinit(&context->finalShader);
109 free(context->initialShader.uniforms);
110}
111
112static void GBAGLES2ContextResized(struct VideoBackend* v, int w, int h) {
113 int drawW = w;
114 int drawH = h;
115 if (v->lockAspectRatio) {
116 if (w * 2 > h * 3) {
117 drawW = h * 3 / 2;
118 } else if (w * 2 < h * 3) {
119 drawH = w * 2 / 3;
120 }
121 }
122 glViewport(0, 0, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
123 glClearColor(0.f, 0.f, 0.f, 1.f);
124 glClear(GL_COLOR_BUFFER_BIT);
125 glViewport((w - drawW) / 2, (h - drawH) / 2, drawW, drawH);
126}
127
128static void GBAGLES2ContextClear(struct VideoBackend* v) {
129 UNUSED(v);
130 glClearColor(0.f, 0.f, 0.f, 1.f);
131 glClear(GL_COLOR_BUFFER_BIT);
132}
133
134void _drawShader(struct GBAGLES2Shader* shader) {
135 GLint viewport[4];
136 glBindFramebuffer(GL_FRAMEBUFFER, shader->fbo);
137 if (shader->blend) {
138 glEnable(GL_BLEND);
139 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
140 } else {
141 glDisable(GL_BLEND);
142 }
143
144 glGetIntegerv(GL_VIEWPORT, viewport);
145 int drawW = shader->width;
146 int drawH = shader->height;
147 int padW = 0;
148 int padH = 0;
149 if (!shader->width) {
150 drawW = viewport[2];
151 padW = viewport[0];
152 }
153 if (!shader->height) {
154 drawH = viewport[3];
155 padH = viewport[1];
156 }
157 glViewport(padW, padH, drawW, drawH);
158 if (!shader->width || !shader->height) {
159 GLint oldTex;
160 glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTex);
161 glBindTexture(GL_TEXTURE_2D, shader->tex);
162 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, drawW, drawH, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
163 glBindTexture(GL_TEXTURE_2D, oldTex);
164 }
165
166 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST);
167 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST);
168 glUseProgram(shader->program);
169 glUniform1i(shader->texLocation, 0);
170 glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, _vertices);
171 glEnableVertexAttribArray(shader->positionLocation);
172 size_t u;
173 for (u = 0; u < shader->nUniforms; ++u) {
174 struct GBAGLES2Uniform* uniform = &shader->uniforms[u];
175 switch (uniform->type) {
176 case GL_FLOAT:
177 glUniform1f(uniform->location, uniform->value.f);
178 break;
179 case GL_INT:
180 glUniform1f(uniform->location, uniform->value.i);
181 break;
182 case GL_UNSIGNED_INT:
183 glUniform1f(uniform->location, uniform->value.ui);
184 break;
185 case GL_FLOAT_VEC2:
186 glUniform2fv(uniform->location, 1, uniform->value.fvec2);
187 break;
188 case GL_FLOAT_VEC3:
189 glUniform3fv(uniform->location, 1, uniform->value.fvec3);
190 break;
191 case GL_FLOAT_VEC4:
192 glUniform4fv(uniform->location, 1, uniform->value.fvec4);
193 break;
194 case GL_INT_VEC2:
195 glUniform2iv(uniform->location, 1, uniform->value.ivec2);
196 break;
197 case GL_INT_VEC3:
198 glUniform3iv(uniform->location, 1, uniform->value.ivec3);
199 break;
200 case GL_INT_VEC4:
201 glUniform4iv(uniform->location, 1, uniform->value.ivec4);
202 break;
203 case GL_UNSIGNED_INT_VEC2:
204 glUniform2uiv(uniform->location, 1, uniform->value.uivec2);
205 break;
206 case GL_UNSIGNED_INT_VEC3:
207 glUniform3uiv(uniform->location, 1, uniform->value.uivec3);
208 break;
209 case GL_UNSIGNED_INT_VEC4:
210 glUniform4uiv(uniform->location, 1, uniform->value.uivec4);
211 break;
212 case GL_FLOAT_MAT2:
213 glUniformMatrix2fv(uniform->location, 1, GL_FALSE, uniform->value.fmat2x2);
214 break;
215 case GL_FLOAT_MAT2x3:
216 glUniformMatrix2x3fv(uniform->location, 1, GL_FALSE, uniform->value.fmat2x3);
217 break;
218 case GL_FLOAT_MAT2x4:
219 glUniformMatrix2x4fv(uniform->location, 1, GL_FALSE, uniform->value.fmat2x4);
220 break;
221 case GL_FLOAT_MAT3x2:
222 glUniformMatrix3x2fv(uniform->location, 1, GL_FALSE, uniform->value.fmat3x2);
223 break;
224 case GL_FLOAT_MAT3:
225 glUniformMatrix3fv(uniform->location, 1, GL_FALSE, uniform->value.fmat3x3);
226 break;
227 case GL_FLOAT_MAT3x4:
228 glUniformMatrix3x4fv(uniform->location, 1, GL_FALSE, uniform->value.fmat3x4);
229 break;
230 case GL_FLOAT_MAT4x2:
231 glUniformMatrix2fv(uniform->location, 1, GL_FALSE, uniform->value.fmat4x2);
232 break;
233 case GL_FLOAT_MAT4x3:
234 glUniformMatrix2x3fv(uniform->location, 1, GL_FALSE, uniform->value.fmat4x3);
235 break;
236 case GL_FLOAT_MAT4:
237 glUniformMatrix2x4fv(uniform->location, 1, GL_FALSE, uniform->value.fmat4x4);
238 break;
239 }
240 }
241 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
242 glBindFramebuffer(GL_FRAMEBUFFER, 0);
243 glBindTexture(GL_TEXTURE_2D, shader->tex);
244 glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
245}
246
247void GBAGLES2ContextDrawFrame(struct VideoBackend* v) {
248 struct GBAGLES2Context* context = (struct GBAGLES2Context*) v;
249 glActiveTexture(GL_TEXTURE0);
250 glBindTexture(GL_TEXTURE_2D, context->tex);
251
252 context->finalShader.filter = v->filter;
253 _drawShader(&context->initialShader);
254 size_t n;
255 for (n = 0; n < context->nShaders; ++n) {
256 _drawShader(&context->shaders[n]);
257 }
258 _drawShader(&context->finalShader);
259 glUseProgram(0);
260}
261
262void GBAGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) {
263 struct GBAGLES2Context* context = (struct GBAGLES2Context*) v;
264 glBindTexture(GL_TEXTURE_2D, context->tex);
265 glPixelStorei(GL_UNPACK_ROW_LENGTH, 256);
266#ifdef COLOR_16_BIT
267#ifdef COLOR_5_6_5
268 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
269#else
270 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
271#endif
272#else
273 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame);
274#endif
275 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
276}
277
278void GBAGLES2ContextCreate(struct GBAGLES2Context* context) {
279 context->d.init = GBAGLES2ContextInit;
280 context->d.deinit = GBAGLES2ContextDeinit;
281 context->d.resized = GBAGLES2ContextResized;
282 context->d.swap = 0;
283 context->d.clear = GBAGLES2ContextClear;
284 context->d.postFrame = GBAGLES2ContextPostFrame;
285 context->d.drawFrame = GBAGLES2ContextDrawFrame;
286 context->d.setMessage = 0;
287 context->d.clearMessage = 0;
288 context->shaders = 0;
289 context->nShaders = 0;
290}
291
292void GBAGLES2ShaderInit(struct GBAGLES2Shader* shader, const char* vs, const char* fs, int width, int height, struct GBAGLES2Uniform* uniforms, size_t nUniforms) {
293 shader->width = width >= 0 ? width : VIDEO_HORIZONTAL_PIXELS;
294 shader->height = height >= 0 ? height : VIDEO_VERTICAL_PIXELS;
295 shader->filter = false;
296 shader->blend = false;
297 shader->uniforms = uniforms;
298 shader->nUniforms = nUniforms;
299 glGenFramebuffers(1, &shader->fbo);
300 glBindFramebuffer(GL_FRAMEBUFFER, shader->fbo);
301
302 glGenTextures(1, &shader->tex);
303 glBindTexture(GL_TEXTURE_2D, shader->tex);
304 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
305 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
306 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
307 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
308 if (shader->width && shader->height) {
309 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, shader->width, shader->height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
310 }
311
312 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shader->tex, 0);
313 shader->program = glCreateProgram();
314 shader->vertexShader = glCreateShader(GL_VERTEX_SHADER);
315 shader->fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
316 if (vs) {
317 glShaderSource(shader->vertexShader, 1, (const GLchar**) &vs, 0);
318 } else {
319 glShaderSource(shader->vertexShader, 1, (const GLchar**) &_nullVertexShader, 0);
320 }
321 if (fs) {
322 glShaderSource(shader->fragmentShader, 1, (const GLchar**) &fs, 0);
323 } else {
324 glShaderSource(shader->fragmentShader, 1, (const GLchar**) &_nullFragmentShader, 0);
325 }
326 glAttachShader(shader->program, shader->vertexShader);
327 glAttachShader(shader->program, shader->fragmentShader);
328 char log[1024];
329 glCompileShader(shader->fragmentShader);
330 glGetShaderInfoLog(shader->fragmentShader, 1024, 0, log);
331 printf("%s\n", log);
332 glCompileShader(shader->vertexShader);
333 glGetShaderInfoLog(shader->vertexShader, 1024, 0, log);
334 printf("%s\n", log);
335 glLinkProgram(shader->program);
336 glGetProgramInfoLog(shader->program, 1024, 0, log);
337 printf("%s\n", log);
338
339 shader->texLocation = glGetUniformLocation(shader->program, "tex");
340 shader->positionLocation = glGetAttribLocation(shader->program, "position");
341 size_t i;
342 for (i = 0; i < shader->nUniforms; ++i) {
343 shader->uniforms[i].location = glGetUniformLocation(shader->program, shader->uniforms[i].name);
344 }
345 glBindFramebuffer(GL_FRAMEBUFFER, 0);
346}
347
348void GBAGLES2ShaderDeinit(struct GBAGLES2Shader* shader) {
349 glDeleteTextures(1, &shader->tex);
350 glDeleteShader(shader->fragmentShader);
351 glDeleteProgram(shader->program);
352 glDeleteFramebuffers(1, &shader->fbo);
353}
354
355void GBAGLES2ShaderAttach(struct GBAGLES2Context* context, struct GBAGLES2Shader* shaders, size_t nShaders) {
356 if (context->shaders) {
357 if (context->shaders == shaders && context->nShaders == nShaders) {
358 return;
359 }
360 GBAGLES2ShaderDetach(context);
361 }
362 context->shaders = shaders;
363 context->nShaders = nShaders;
364}
365
366void GBAGLES2ShaderDetach(struct GBAGLES2Context* context) {
367 if (!context->shaders) {
368 return;
369 }
370 context->shaders = 0;
371}
372
373static bool _lookupIntValue(const struct Configuration* config, const char* section, const char* key, int* out) {
374 const char* charValue = ConfigurationGetValue(config, section, key);
375 if (!charValue) {
376 return false;
377 }
378 char* end;
379 unsigned long value = strtol(charValue, &end, 10);
380 if (*end) {
381 return false;
382 }
383 *out = value;
384 return true;
385}
386
387bool GBAGLES2ShaderLoad(struct GBAGLES2Shader** shaders, size_t* nShaders, struct GBAGLES2ShaderMetadata* metadata, struct VDir* dir) {
388 struct VFile* manifest = dir->openFile(dir, "manifest.ini", O_RDONLY);
389 if (!manifest) {
390 return false;
391 }
392 bool success = false;
393 struct Configuration description;
394 ConfigurationInit(&description);
395 if (ConfigurationReadVFile(&description, manifest)) {
396 int inShaders;
397 success = _lookupIntValue(&description, "shader", "passes", &inShaders);
398 if (inShaders > MAX_PASSES || inShaders < 1) {
399 success = false;
400 }
401 if (success) {
402 if (metadata) {
403 metadata->name = ConfigurationGetValue(&description, "shader", "name");
404 if (metadata->name) {
405 metadata->name = strdup(metadata->name);
406 }
407 metadata->author = ConfigurationGetValue(&description, "shader", "author");
408 if (metadata->author) {
409 metadata->author = strdup(metadata->author);
410 }
411 metadata->description = ConfigurationGetValue(&description, "shader", "description");
412 if (metadata->description) {
413 metadata->description = strdup(metadata->description);
414 }
415 }
416 struct GBAGLES2Shader* shaderBlock = malloc(sizeof(struct GBAGLES2Shader) * inShaders);
417 int n;
418 for (n = 0; n < inShaders; ++n) {
419 char passName[12];
420 snprintf(passName, sizeof(passName), "pass.%u", n);
421 const char* fs = ConfigurationGetValue(&description, passName, "fragmentShader");
422 const char* vs = ConfigurationGetValue(&description, passName, "vertexShader");
423 if (fs && (fs[0] == '.' || strstr(fs, PATH_SEP))) {
424 success = false;
425 break;
426 }
427 if (vs && (vs[0] == '.' || strstr(vs, PATH_SEP))) {
428 success = false;
429 break;
430 }
431 char* fssrc = 0;
432 char* vssrc = 0;
433 if (fs) {
434 struct VFile* fsf = dir->openFile(dir, fs, O_RDONLY);
435 if (!fsf) {
436 success = false;
437 break;
438 }
439 fssrc = malloc(fsf->size(fsf));
440 fsf->read(fsf, fssrc, fsf->size(fsf));
441 fsf->close(fsf);
442 }
443 if (vs) {
444 struct VFile* vsf = dir->openFile(dir, vs, O_RDONLY);
445 if (!vsf) {
446 success = false;
447 free(fssrc);
448 break;
449 }
450 vssrc = malloc(vsf->size(vsf));
451 vsf->read(vsf, vssrc, vsf->size(vsf));
452 vsf->close(vsf);
453 }
454 int width = 0;
455 int height = 0;
456 _lookupIntValue(&description, passName, "width", &width);
457 _lookupIntValue(&description, passName, "height", &height);
458 GBAGLES2ShaderInit(&shaderBlock[n], vssrc, fssrc, width, height, 0, 0);
459 int b = 0;
460 _lookupIntValue(&description, passName, "blend", &b);
461 if (b) {
462 shaderBlock[n].blend = b;
463 }
464 b = 0;
465 _lookupIntValue(&description, passName, "filter", &b);
466 if (b) {
467 shaderBlock[n].filter = b;
468 }
469 free(fssrc);
470 free(vssrc);
471 }
472 if (success) {
473 *nShaders = inShaders;
474 *shaders = shaderBlock;
475 } else {
476 inShaders = n;
477 for (n = 0; n < inShaders; ++n) {
478 GBAGLES2ShaderDeinit(&shaderBlock[n]);
479 }
480 }
481 }
482 }
483 ConfigurationDeinit(&description);
484 return success;
485}
486
487void GBAGLES2ShaderFree(struct GBAGLES2Shader* shaders, size_t nShaders) {
488 size_t n;
489 for (n = 0; n < nShaders; ++n) {
490 GBAGLES2ShaderDeinit(&shaders[n]);
491 size_t u;
492 for (u = 0; u < shaders[n].nUniforms; ++u) {
493 free((void*) shaders[n].uniforms[u].name);
494 }
495 }
496 free(shaders);
497}