all repos — mgba @ 6c9ee4c2127cc43585e3861ac2e97028e6d5f45e

mGBA Game Boy Advance Emulator

src/platform/sdl/egl-main.c (view raw)

  1#include "debugger/debugger.h"
  2#include "gba-thread.h"
  3#include "gba.h"
  4#include "renderers/video-software.h"
  5#include "sdl-audio.h"
  6#include "sdl-events.h"
  7
  8#include <SDL/SDL.h>
  9#include <GLES2/gl2.h>
 10#include <EGL/egl.h>
 11
 12#include <bcm_host.h>
 13
 14#include <errno.h>
 15#include <fcntl.h>
 16#include <malloc.h>
 17#include <signal.h>
 18#include <sys/time.h>
 19
 20struct GBAVideoEGLRenderer {
 21	struct GBAVideoSoftwareRenderer d;
 22	struct GBASDLAudio audio;
 23	struct GBASDLEvents events;
 24
 25	EGLDisplay display;
 26	EGLSurface surface;
 27	EGLContext context;
 28	EGL_DISPMANX_WINDOW_T window;
 29	GLuint tex;
 30	GLuint fragmentShader;
 31	GLuint vertexShader;
 32	GLuint program;
 33	GLuint bufferObject;
 34	GLuint texLocation;
 35	GLuint positionLocation;
 36};
 37
 38static const char* _vertexShader =
 39	"attribute vec4 position;\n"
 40	"varying vec2 texCoord;\n"
 41
 42	"void main() {\n"
 43	"	gl_Position = position;\n"
 44	"	texCoord = (position.st + vec2(1.0, -1.0)) * vec2(0.46875, -0.3125);\n"
 45	"}";
 46
 47static const char* _fragmentShader =
 48	"varying vec2 texCoord;\n"
 49	"uniform sampler2D tex;\n"
 50
 51	"void main() {\n"
 52	"	vec4 color = texture2D(tex, texCoord);\n"
 53	"	color.a = 1.;\n"
 54	"	gl_FragColor = color;"
 55	"}";
 56
 57static const GLfloat _vertices[] = {
 58	-1.f, -1.f,
 59	-1.f, 1.f,
 60	1.f, 1.f,
 61	1.f, -1.f,
 62};
 63
 64static int _GBAEGLInit(struct GBAVideoEGLRenderer* renderer);
 65static void _GBAEGLDeinit(struct GBAVideoEGLRenderer* renderer);
 66static void _GBAEGLRunloop(struct GBAThread* context, struct GBAVideoEGLRenderer* renderer);
 67static void _GBASDLStart(struct GBAThread* context);
 68static void _GBASDLClean(struct GBAThread* context);
 69
 70int main(int argc, char** argv) {
 71	struct GBAVideoEGLRenderer renderer;
 72
 73	struct StartupOptions opts;
 74	if (!parseCommandArgs(&opts, argc, argv, 0)) {
 75		usage(argv[0], 0);
 76		freeOptions(&opts);
 77		return 1;
 78	}
 79
 80	if (!_GBAEGLInit(&renderer)) {
 81		return 1;
 82	}
 83	GBAVideoSoftwareRendererCreate(&renderer.d);
 84
 85	struct GBAThread context = {
 86		.renderer = &renderer.d.d,
 87		.sync.videoFrameWait = 0,
 88		.sync.audioWait = 1,
 89		.startCallback = _GBASDLStart,
 90		.cleanCallback = _GBASDLClean,
 91		.userData = &renderer
 92	};
 93
 94	context.debugger = createDebugger(&opts);
 95
 96	GBAMapOptionsToContext(&opts, &context);
 97
 98	renderer.audio.samples = context.audioBuffers;
 99	GBASDLInitAudio(&renderer.audio);
100
101	renderer.events.bindings = &context.inputMap;
102	GBASDLInitEvents(&renderer.events);
103
104	GBAThreadStart(&context);
105
106	_GBAEGLRunloop(&context, &renderer);
107
108	GBAThreadJoin(&context);
109	freeOptions(&opts);
110	free(context.debugger);
111
112	_GBAEGLDeinit(&renderer);
113
114	return 0;
115}
116
117static int _GBAEGLInit(struct GBAVideoEGLRenderer* renderer) {
118	if (SDL_Init(SDL_INIT_JOYSTICK) < 0) {
119		return 0;
120	}
121
122	bcm_host_init();
123	renderer->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
124	int major, minor;
125	if (EGL_FALSE == eglInitialize(renderer->display, &major, &minor)) {
126		printf("Failed to initialize EGL");
127		return 0;
128	}
129
130	if (EGL_FALSE == eglBindAPI(EGL_OPENGL_ES_API)) {
131		printf("Failed to get GLES API");
132		return 0;
133	}
134
135	const EGLint requestConfig[] = {
136		EGL_RED_SIZE, 5,
137		EGL_GREEN_SIZE, 5,
138		EGL_BLUE_SIZE, 5,
139		EGL_ALPHA_SIZE, 1,
140		EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
141		EGL_NONE
142	};
143
144	EGLConfig config;
145	EGLint numConfigs;
146
147	if (EGL_FALSE == eglChooseConfig(renderer->display, requestConfig, &config, 1, &numConfigs)) {
148		printf("Failed to choose EGL config\n");
149		return 0;
150	}
151
152	const EGLint contextAttributes[] = {
153		EGL_CONTEXT_CLIENT_VERSION, 2,
154		EGL_NONE
155	};
156
157	int dispWidth = 240, dispHeight = 160, adjWidth;
158	renderer->context = eglCreateContext(renderer->display, config, EGL_NO_CONTEXT, contextAttributes);
159	graphics_get_display_size(0, &dispWidth, &dispHeight);
160	adjWidth = dispHeight / 2 * 3;
161
162	DISPMANX_DISPLAY_HANDLE_T display = vc_dispmanx_display_open(0);
163	DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0);
164
165	VC_RECT_T destRect = {
166		.x = (dispWidth - adjWidth) / 2,
167		.y = 0,
168		.width = adjWidth,
169		.height = dispHeight
170	};
171
172	VC_RECT_T srcRect = {
173		.x = 0,
174		.y = 0,
175		.width = 240 << 16,
176		.height = 160 << 16
177	};
178
179	DISPMANX_ELEMENT_HANDLE_T element = vc_dispmanx_element_add(update, display, 0, &destRect, 0, &srcRect, DISPMANX_PROTECTION_NONE, 0, 0, 0);
180	vc_dispmanx_update_submit_sync(update);
181
182	renderer->window.element = element;
183	renderer->window.width = dispWidth;
184	renderer->window.height = dispHeight;
185
186	renderer->surface = eglCreateWindowSurface(renderer->display, config, &renderer->window, 0);
187	if (EGL_FALSE == eglMakeCurrent(renderer->display, renderer->surface, renderer->surface, renderer->context)) {
188		return 0;
189	}
190
191	renderer->d.outputBuffer = memalign(16, 256 * 256 * 4);
192	renderer->d.outputBufferStride = 256;
193	glGenTextures(1, &renderer->tex);
194	glBindTexture(GL_TEXTURE_2D, renderer->tex);
195	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
196	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
197	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
198	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
199	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
200	renderer->fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
201	renderer->vertexShader = glCreateShader(GL_VERTEX_SHADER);
202	renderer->program = glCreateProgram();
203
204	glShaderSource(renderer->fragmentShader, 1, (const GLchar**) &_fragmentShader, 0);
205	glShaderSource(renderer->vertexShader, 1, (const GLchar**) &_vertexShader, 0);
206	glAttachShader(renderer->program, renderer->vertexShader);
207	glAttachShader(renderer->program, renderer->fragmentShader);
208	char log[1024];
209	glCompileShader(renderer->fragmentShader);
210	glCompileShader(renderer->vertexShader);
211	glGetShaderInfoLog(renderer->fragmentShader, 1024, 0, log);
212	glGetShaderInfoLog(renderer->vertexShader, 1024, 0, log);
213	glLinkProgram(renderer->program);
214	glGetProgramInfoLog(renderer->program, 1024, 0, log);
215	printf("%s\n", log);
216	renderer->texLocation = glGetUniformLocation(renderer->program, "tex");
217	renderer->positionLocation = glGetAttribLocation(renderer->program, "position");
218	glClearColor(1.f, 0.f, 0.f, 1.f);
219	return 1;
220}
221
222static void _GBAEGLRunloop(struct GBAThread* context, struct GBAVideoEGLRenderer* renderer) {
223	SDL_Event event;
224
225	while (context->state < THREAD_EXITING) {
226		if (GBASyncWaitFrameStart(&context->sync, context->frameskip)) {
227			glViewport(0, 0, 240, 160);
228			glClear(GL_COLOR_BUFFER_BIT);
229			glUseProgram(renderer->program);
230			glUniform1i(renderer->texLocation, 0);
231			glActiveTexture(GL_TEXTURE0);
232			glBindTexture(GL_TEXTURE_2D, renderer->tex);
233			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, renderer->d.outputBuffer);
234			glVertexAttribPointer(renderer->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, _vertices);
235			glEnableVertexAttribArray(renderer->positionLocation);
236			glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
237			glUseProgram(0);
238			eglSwapBuffers(renderer->display, renderer->surface);
239		}
240		GBASyncWaitFrameEnd(&context->sync);
241
242		while (SDL_PollEvent(&event)) {
243			GBASDLHandleEvent(context, &renderer->events, &event);
244		}
245	}
246}
247
248static void _GBAEGLDeinit(struct GBAVideoEGLRenderer* renderer) {
249	eglMakeCurrent(renderer->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
250	eglDestroySurface(renderer->display, renderer->surface);
251	eglDestroyContext(renderer->display, renderer->context);
252	eglTerminate(renderer->display);
253
254	GBASDLDeinitEvents(&renderer->events);
255	GBASDLDeinitAudio(&renderer->audio);
256	SDL_Quit();
257
258	bcm_host_deinit();
259}
260
261static void _GBASDLStart(struct GBAThread* threadContext) {
262	struct GBAVideoEGLRenderer* renderer = threadContext->userData;
263	renderer->audio.audio = &threadContext->gba->audio;
264	renderer->audio.thread = threadContext;
265}
266
267static void _GBASDLClean(struct GBAThread* threadContext) {
268	struct GBAVideoEGLRenderer* renderer = threadContext->userData;
269	renderer->audio.audio = 0;
270}