all repos — mgba @ 8eb4f3ca4d1210e654248605a5fa48546b1d4ee5

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
 10static const char* const _vertexShader =
 11	"attribute vec4 position;\n"
 12	"varying vec2 texCoord;\n"
 13
 14	"void main() {\n"
 15	"	gl_Position = position;\n"
 16	"	texCoord = (position.st + vec2(1.0, -1.0)) * vec2(0.5, -0.5);\n"
 17	"}";
 18
 19static const char* const _nullVertexShader =
 20	"attribute vec4 position;\n"
 21	"varying vec2 texCoord;\n"
 22
 23	"void main() {\n"
 24	"	gl_Position = position;\n"
 25	"	texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);\n"
 26	"}";
 27
 28static const char* const _fragmentShader =
 29	"varying vec2 texCoord;\n"
 30	"uniform sampler2D tex;\n"
 31	"uniform float gamma;\n"
 32	"uniform vec3 scale;\n"
 33	"uniform vec3 bias;\n"
 34
 35	"void main() {\n"
 36	"	vec4 color = texture2D(tex, texCoord);\n"
 37	"	color.a = 1.;\n"
 38	"	color.rgb = scale * pow(color.rgb, vec3(gamma, gamma, gamma)) + bias;\n"
 39	"	gl_FragColor = color;\n"
 40	"}";
 41
 42static const char* const _nullFragmentShader =
 43	"varying vec2 texCoord;\n"
 44	"uniform sampler2D tex;\n"
 45
 46	"void main() {\n"
 47	"	vec4 color = texture2D(tex, texCoord);\n"
 48	"	color.a = 1.;\n"
 49	"	gl_FragColor = color;\n"
 50	"}";
 51
 52static const GLfloat _vertices[] = {
 53	-1.f, -1.f,
 54	-1.f, 1.f,
 55	1.f, 1.f,
 56	1.f, -1.f,
 57};
 58
 59
 60static void GBAGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
 61	UNUSED(handle);
 62	struct GBAGLES2Context* context = (struct GBAGLES2Context*) v;
 63	glGenTextures(1, &context->tex);
 64	glBindTexture(GL_TEXTURE_2D, context->tex);
 65	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 66	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 67
 68#ifdef COLOR_16_BIT
 69#ifdef COLOR_5_6_5
 70	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0);
 71#else
 72	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);
 73#endif
 74#else
 75	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
 76#endif
 77
 78	glClearColor(0.f, 0.f, 0.f, 1.f);
 79
 80	struct GBAGLES2Uniform* uniforms = malloc(sizeof(struct GBAGLES2Uniform) * 3);
 81	uniforms[0].name = "gamma";
 82	uniforms[0].type = GL_FLOAT;
 83	uniforms[0].value.f = 1.0f;
 84	uniforms[1].name = "scale";
 85	uniforms[1].type = GL_FLOAT_VEC3;
 86	uniforms[1].value.fvec3[0] = 1.0f;
 87	uniforms[1].value.fvec3[1] = 1.0f;
 88	uniforms[1].value.fvec3[2] = 1.0f;
 89	uniforms[2].name = "bias";
 90	uniforms[2].type = GL_FLOAT_VEC3;
 91	uniforms[2].value.fvec3[0] = 0.0f;
 92	uniforms[2].value.fvec3[1] = 0.0f;
 93	uniforms[2].value.fvec3[2] = 0.0f;
 94	GBAGLES2ShaderInit(&context->initialShader, _vertexShader, _fragmentShader, -1, -1, uniforms, 3);
 95	GBAGLES2ShaderInit(&context->finalShader, 0, 0, 0, 0, 0, 0);
 96	glDeleteFramebuffers(1, &context->finalShader.fbo);
 97	context->finalShader.fbo = 0;
 98}
 99
100static void GBAGLES2ContextDeinit(struct VideoBackend* v) {
101	struct GBAGLES2Context* context = (struct GBAGLES2Context*) v;
102	glDeleteTextures(1, &context->tex);
103	GBAGLES2ShaderDeinit(&context->initialShader);
104	GBAGLES2ShaderDeinit(&context->finalShader);
105	free(context->initialShader.uniforms);
106}
107
108static void GBAGLES2ContextResized(struct VideoBackend* v, int w, int h) {
109	int drawW = w;
110	int drawH = h;
111	if (v->lockAspectRatio) {
112		if (w * 2 > h * 3) {
113			drawW = h * 3 / 2;
114		} else if (w * 2 < h * 3) {
115			drawH = w * 2 / 3;
116		}
117	}
118	glViewport(0, 0, 240, 160);
119	glClearColor(0.f, 0.f, 0.f, 1.f);
120	glClear(GL_COLOR_BUFFER_BIT);
121	glViewport((w - drawW) / 2, (h - drawH) / 2, drawW, drawH);
122}
123
124static void GBAGLES2ContextClear(struct VideoBackend* v) {
125	UNUSED(v);
126	glClearColor(0.f, 0.f, 0.f, 1.f);
127	glClear(GL_COLOR_BUFFER_BIT);
128}
129
130void _drawShader(struct GBAGLES2Shader* shader) {
131	GLint viewport[4];
132	glBindFramebuffer(GL_FRAMEBUFFER, shader->fbo);
133	if (shader->blend) {
134		glEnable(GL_BLEND);
135		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
136	} else {
137		glDisable(GL_BLEND);
138	}
139	glGetIntegerv(GL_VIEWPORT, viewport);
140	glViewport(0, 0, shader->width ? shader->width : viewport[2], shader->height ? shader->height : viewport[3]);
141	if (!shader->width || !shader->height) {
142		GLint oldTex;
143		glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTex);
144		glBindTexture(GL_TEXTURE_2D, shader->tex);
145		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, shader->width ? shader->width : viewport[2], shader->height ? shader->height : viewport[3], 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
146		glBindTexture(GL_TEXTURE_2D, oldTex);
147	}
148
149	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST);
150	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST);
151	glUseProgram(shader->program);
152	glUniform1i(shader->texLocation, 0);
153	glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, _vertices);
154	glEnableVertexAttribArray(shader->positionLocation);
155	size_t u;
156	for (u = 0; u < shader->nUniforms; ++u) {
157		struct GBAGLES2Uniform* uniform = &shader->uniforms[u];
158		switch (uniform->type) {
159		case GL_FLOAT:
160			glUniform1f(uniform->location, uniform->value.f);
161			break;
162		case GL_INT:
163			glUniform1f(uniform->location, uniform->value.i);
164			break;
165		case GL_UNSIGNED_INT:
166			glUniform1f(uniform->location, uniform->value.ui);
167			break;
168		case GL_FLOAT_VEC2:
169			glUniform2fv(uniform->location, 1, uniform->value.fvec2);
170			break;
171		case GL_FLOAT_VEC3:
172			glUniform3fv(uniform->location, 1, uniform->value.fvec3);
173			break;
174		case GL_FLOAT_VEC4:
175			glUniform4fv(uniform->location, 1, uniform->value.fvec4);
176			break;
177		case GL_INT_VEC2:
178			glUniform2iv(uniform->location, 1, uniform->value.ivec2);
179			break;
180		case GL_INT_VEC3:
181			glUniform3iv(uniform->location, 1, uniform->value.ivec3);
182			break;
183		case GL_INT_VEC4:
184			glUniform4iv(uniform->location, 1, uniform->value.ivec4);
185			break;
186		case GL_UNSIGNED_INT_VEC2:
187			glUniform2uiv(uniform->location, 1, uniform->value.uivec2);
188			break;
189		case GL_UNSIGNED_INT_VEC3:
190			glUniform3uiv(uniform->location, 1, uniform->value.uivec3);
191			break;
192		case GL_UNSIGNED_INT_VEC4:
193			glUniform4uiv(uniform->location, 1, uniform->value.uivec4);
194			break;
195		case GL_FLOAT_MAT2:
196			glUniformMatrix2fv(uniform->location, 1, GL_FALSE, uniform->value.fmat2x2);
197			break;
198		case GL_FLOAT_MAT2x3:
199			glUniformMatrix2x3fv(uniform->location, 1, GL_FALSE, uniform->value.fmat2x3);
200			break;
201		case GL_FLOAT_MAT2x4:
202			glUniformMatrix2x4fv(uniform->location, 1, GL_FALSE, uniform->value.fmat2x4);
203			break;
204		case GL_FLOAT_MAT3x2:
205			glUniformMatrix3x2fv(uniform->location, 1, GL_FALSE, uniform->value.fmat3x2);
206			break;
207		case GL_FLOAT_MAT3:
208			glUniformMatrix3fv(uniform->location, 1, GL_FALSE, uniform->value.fmat3x3);
209			break;
210		case GL_FLOAT_MAT3x4:
211			glUniformMatrix3x4fv(uniform->location, 1, GL_FALSE, uniform->value.fmat3x4);
212			break;
213		case GL_FLOAT_MAT4x2:
214			glUniformMatrix2fv(uniform->location, 1, GL_FALSE, uniform->value.fmat4x2);
215			break;
216		case GL_FLOAT_MAT4x3:
217			glUniformMatrix2x3fv(uniform->location, 1, GL_FALSE, uniform->value.fmat4x3);
218			break;
219		case GL_FLOAT_MAT4:
220			glUniformMatrix2x4fv(uniform->location, 1, GL_FALSE, uniform->value.fmat4x4);
221			break;
222		}
223	}
224	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
225	glBindFramebuffer(GL_FRAMEBUFFER, 0);
226	glBindTexture(GL_TEXTURE_2D, shader->tex);
227	glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
228}
229
230void GBAGLES2ContextDrawFrame(struct VideoBackend* v) {
231	struct GBAGLES2Context* context = (struct GBAGLES2Context*) v;
232	glActiveTexture(GL_TEXTURE0);
233	glBindTexture(GL_TEXTURE_2D, context->tex);
234
235	context->finalShader.filter = v->filter;
236	_drawShader(&context->initialShader);
237	size_t n;
238	for (n = 0; n < context->nShaders; ++n) {
239		_drawShader(&context->shaders[n]);
240	}
241	_drawShader(&context->finalShader);
242	glUseProgram(0);
243}
244
245void GBAGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) {
246	struct GBAGLES2Context* context = (struct GBAGLES2Context*) v;
247	glBindTexture(GL_TEXTURE_2D, context->tex);
248	glPixelStorei(GL_UNPACK_ROW_LENGTH, 256);
249#ifdef COLOR_16_BIT
250#ifdef COLOR_5_6_5
251	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
252#else
253	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);
254#endif
255#else
256	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame);
257#endif
258	glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
259}
260
261void GBAGLES2ContextCreate(struct GBAGLES2Context* context) {
262	context->d.init = GBAGLES2ContextInit;
263	context->d.deinit = GBAGLES2ContextDeinit;
264	context->d.resized = GBAGLES2ContextResized;
265	context->d.swap = 0;
266	context->d.clear = GBAGLES2ContextClear;
267	context->d.postFrame = GBAGLES2ContextPostFrame;
268	context->d.drawFrame = GBAGLES2ContextDrawFrame;
269	context->d.setMessage = 0;
270	context->d.clearMessage = 0;
271	context->shaders = 0;
272	context->nShaders = 0;
273}
274
275void GBAGLES2ShaderInit(struct GBAGLES2Shader* shader, const char* vs, const char* fs, int width, int height, struct GBAGLES2Uniform* uniforms, size_t nUniforms) {
276	shader->width = width >= 0 ? width : VIDEO_HORIZONTAL_PIXELS;
277	shader->height = height >= 0 ? height : VIDEO_VERTICAL_PIXELS;
278	shader->filter = false;
279	shader->blend = false;
280	shader->uniforms = uniforms;
281	shader->nUniforms = nUniforms;
282	glGenFramebuffers(1, &shader->fbo);
283	glBindFramebuffer(GL_FRAMEBUFFER, shader->fbo);
284
285	glGenTextures(1, &shader->tex);
286	glBindTexture(GL_TEXTURE_2D, shader->tex);
287	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
288	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
289	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
290	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
291	if (shader->width && shader->height) {
292		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, shader->width, shader->height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
293	}
294
295	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shader->tex, 0);
296	shader->program = glCreateProgram();
297	shader->vertexShader = glCreateShader(GL_VERTEX_SHADER);
298	shader->fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
299	if (vs) {
300		glShaderSource(shader->vertexShader, 1, (const GLchar**) &vs, 0);
301	} else {
302		glShaderSource(shader->vertexShader, 1, (const GLchar**) &_nullVertexShader, 0);
303	}
304	if (fs) {
305		glShaderSource(shader->fragmentShader, 1, (const GLchar**) &fs, 0);
306	} else {
307		glShaderSource(shader->fragmentShader, 1, (const GLchar**) &_nullFragmentShader, 0);
308	}
309	glAttachShader(shader->program, shader->vertexShader);
310	glAttachShader(shader->program, shader->fragmentShader);
311	char log[1024];
312	glCompileShader(shader->fragmentShader);
313	glGetShaderInfoLog(shader->fragmentShader, 1024, 0, log);
314	printf("%s\n", log);
315	glCompileShader(shader->vertexShader);
316	glGetShaderInfoLog(shader->vertexShader, 1024, 0, log);
317	printf("%s\n", log);
318	glLinkProgram(shader->program);
319	glGetProgramInfoLog(shader->program, 1024, 0, log);
320	printf("%s\n", log);
321
322	shader->texLocation = glGetUniformLocation(shader->program, "tex");
323	shader->positionLocation = glGetAttribLocation(shader->program, "position");
324	size_t i;
325	for (i = 0; i < shader->nUniforms; ++i) {
326		shader->uniforms[i].location = glGetUniformLocation(shader->program, shader->uniforms[i].name);
327	}
328	glBindFramebuffer(GL_FRAMEBUFFER, 0);
329}
330
331void GBAGLES2ShaderDeinit(struct GBAGLES2Shader* shader) {
332	glDeleteTextures(1, &shader->tex);
333	glDeleteShader(shader->fragmentShader);
334	glDeleteProgram(shader->program);
335	glDeleteFramebuffers(1, &shader->fbo);
336}
337
338void GBAGLES2ShaderAttach(struct GBAGLES2Context* context, struct GBAGLES2Shader* shaders, size_t nShaders) {
339	if (context->shaders) {
340		if (context->shaders == shaders && context->nShaders == nShaders) {
341			return;
342		}
343		GBAGLES2ShaderDetach(context);
344	}
345	context->shaders = shaders;
346	context->nShaders = nShaders;
347}
348
349void GBAGLES2ShaderDetach(struct GBAGLES2Context* context) {
350	if (!context->shaders) {
351		return;
352	}
353	context->shaders = 0;
354}