all repos — mgba @ f62ccde49d484c65b212ae86299f4720c9dc836d

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