all repos — mgba @ 16608a408cd3f9df010aeef11fb7240e86f1faec

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	"	gl_FragColor = texture2D(tex, texCoord);\n"
 53	"}";
 54
 55static const GLfloat _vertices[] = {
 56	-1.f, -1.f,
 57	-1.f, 1.f,
 58	1.f, 1.f,
 59	1.f, -1.f,
 60};
 61
 62static int _GBAEGLInit(struct GBAVideoEGLRenderer* renderer);
 63static void _GBAEGLDeinit(struct GBAVideoEGLRenderer* renderer);
 64static void _GBAEGLRunloop(struct GBAThread* context, struct GBAVideoEGLRenderer* renderer);
 65static void _GBASDLStart(struct GBAThread* context);
 66static void _GBASDLClean(struct GBAThread* context);
 67
 68int main(int argc, char** argv) {
 69	const char* fname = "test.rom";
 70	if (argc > 1) {
 71		fname = argv[1];
 72	}
 73	int fd = open(fname, O_RDONLY);
 74	if (fd < 0) {
 75		return 1;
 76	}
 77
 78	struct GBAVideoEGLRenderer renderer;
 79
 80	if (!_GBAEGLInit(&renderer)) {
 81		return 1;
 82	}
 83	GBAVideoSoftwareRendererCreate(&renderer.d);
 84
 85	struct GBAThread context = {
 86		.fd = fd,
 87		.fname = fname,
 88		.biosFd = -1,
 89		.useDebugger = 0,
 90		.renderer = &renderer.d.d,
 91		.frameskip = 0,
 92		.sync.videoFrameWait = 0,
 93		.sync.audioWait = 0,
 94		.startCallback = _GBASDLStart,
 95		.cleanCallback = _GBASDLClean,
 96		.userData = &renderer
 97	};
 98	GBAThreadStart(&context);
 99
100	_GBAEGLRunloop(&context, &renderer);
101
102	GBAThreadJoin(&context);
103	close(fd);
104
105	_GBAEGLDeinit(&renderer);
106
107	return 0;
108}
109
110static int _GBAEGLInit(struct GBAVideoEGLRenderer* renderer) {
111	if (SDL_Init(SDL_INIT_JOYSTICK) < 0) {
112		return 0;
113	}
114
115	GBASDLInitEvents(&renderer->events);
116	GBASDLInitAudio(&renderer->audio);
117
118	bcm_host_init();
119	renderer->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
120	int major, minor;
121	if (EGL_FALSE == eglInitialize(renderer->display, &major, &minor)) {
122		printf("Failed to initialize EGL");
123		return 0;
124	}
125
126	if (EGL_FALSE == eglBindAPI(EGL_OPENGL_ES_API)) {
127		printf("Failed to get GLES API");
128		return 0;
129	}
130
131	const EGLint requestConfig[] = {
132		EGL_RED_SIZE, 5,
133		EGL_GREEN_SIZE, 5,
134		EGL_BLUE_SIZE, 5,
135		EGL_ALPHA_SIZE, 1,
136		EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
137		EGL_NONE
138	};
139
140	EGLConfig config;
141	EGLint numConfigs;
142
143	if (EGL_FALSE == eglChooseConfig(renderer->display, requestConfig, &config, 1, &numConfigs)) {
144		printf("Failed to choose EGL config\n");
145		return 0;
146	}
147
148	const EGLint contextAttributes[] = {
149		EGL_CONTEXT_CLIENT_VERSION, 2,
150		EGL_NONE
151	};
152
153	int dispWidth = 240, dispHeight = 160, adjWidth;
154	renderer->context = eglCreateContext(renderer->display, config, EGL_NO_CONTEXT, contextAttributes);
155	graphics_get_display_size(0, &dispWidth, &dispHeight);
156	adjWidth = dispHeight / 2 * 3;
157
158	DISPMANX_DISPLAY_HANDLE_T display = vc_dispmanx_display_open(0);
159	DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0);
160
161	VC_RECT_T destRect = {
162		.x = (dispWidth - adjWidth) / 2,
163		.y = 0,
164		.width = adjWidth,
165		.height = dispHeight
166	};
167
168	VC_RECT_T srcRect = {
169		.x = 0,
170		.y = 0,
171		.width = 240 << 16,
172		.height = 160 << 16
173	};
174
175	DISPMANX_ELEMENT_HANDLE_T element = vc_dispmanx_element_add(update, display, 0, &destRect, 0, &srcRect, DISPMANX_PROTECTION_NONE, 0, 0, 0);
176	vc_dispmanx_update_submit_sync(update);
177
178	renderer->window.element = element;
179	renderer->window.width = dispWidth;
180	renderer->window.height = dispHeight;
181
182	renderer->surface = eglCreateWindowSurface(renderer->display, config, &renderer->window, 0);
183	if (EGL_FALSE == eglMakeCurrent(renderer->display, renderer->surface, renderer->surface, renderer->context)) {
184		return 0;
185	}
186
187	renderer->d.outputBuffer = memalign(16, 256 * 256 * 4);
188	renderer->d.outputBufferStride = 256;
189	glGenTextures(1, &renderer->tex);
190	glBindTexture(GL_TEXTURE_2D, renderer->tex);
191	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
192	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
193	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
194	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
195	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
196	renderer->fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
197	renderer->vertexShader = glCreateShader(GL_VERTEX_SHADER);
198	renderer->program = glCreateProgram();
199
200	glShaderSource(renderer->fragmentShader, 1, (const GLchar**) &_fragmentShader, 0);
201	glShaderSource(renderer->vertexShader, 1, (const GLchar**) &_vertexShader, 0);
202	glAttachShader(renderer->program, renderer->vertexShader);
203	glAttachShader(renderer->program, renderer->fragmentShader);
204	char log[1024];
205	glCompileShader(renderer->fragmentShader);
206	glCompileShader(renderer->vertexShader);
207	glGetShaderInfoLog(renderer->fragmentShader, 1024, 0, log);
208	glGetShaderInfoLog(renderer->vertexShader, 1024, 0, log);
209	glLinkProgram(renderer->program);
210	glGetProgramInfoLog(renderer->program, 1024, 0, log);
211	printf("%s\n", log);
212	renderer->texLocation = glGetUniformLocation(renderer->program, "tex");
213	renderer->positionLocation = glGetAttribLocation(renderer->program, "position");
214	glClearColor(1.f, 0.f, 0.f, 1.f);
215	return 1;
216}
217
218static void _GBAEGLRunloop(struct GBAThread* context, struct GBAVideoEGLRenderer* renderer) {
219	SDL_Event event;
220
221	while (context->state < THREAD_EXITING) {
222		if (GBASyncWaitFrameStart(&context->sync, context->frameskip)) {
223			glViewport(0, 0, 240, 160);
224			glClear(GL_COLOR_BUFFER_BIT);
225			glUseProgram(renderer->program);
226			glUniform1i(renderer->texLocation, 0);
227			glActiveTexture(GL_TEXTURE0);
228			glBindTexture(GL_TEXTURE_2D, renderer->tex);
229			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, renderer->d.outputBuffer);
230			glVertexAttribPointer(renderer->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, _vertices);
231			glEnableVertexAttribArray(renderer->positionLocation);
232			glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
233			glUseProgram(0);
234			eglSwapBuffers(renderer->display, renderer->surface);
235		}
236		GBASyncWaitFrameEnd(&context->sync);
237
238		while (SDL_PollEvent(&event)) {
239			GBASDLHandleEvent(context, &event);
240		}
241	}
242}
243
244static void _GBAEGLDeinit(struct GBAVideoEGLRenderer* renderer) {
245	eglMakeCurrent(renderer->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
246	eglDestroySurface(renderer->display, renderer->surface);
247	eglDestroyContext(renderer->display, renderer->context);
248	eglTerminate(renderer->display);
249
250	GBASDLDeinitEvents(&renderer->events);
251	GBASDLDeinitAudio(&renderer->audio);
252	SDL_Quit();
253
254	bcm_host_deinit();
255}
256
257static void _GBASDLStart(struct GBAThread* threadContext) {
258	struct GBAVideoEGLRenderer* renderer = threadContext->userData;
259	renderer->audio.audio = &threadContext->gba->audio;
260}
261
262static void _GBASDLClean(struct GBAThread* threadContext) {
263	struct GBAVideoEGLRenderer* renderer = threadContext->userData;
264	renderer->audio.audio = 0;
265}