all repos — mgba @ 2f39a575672e95c5e073518fa0b91e8ac60cbd11

mGBA Game Boy Advance Emulator

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

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