all repos — mgba @ f1816279a57555d2b8ca4ad1b4c28c0a21576c37

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 "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}