all repos — mgba @ 8e43ffed9041775e7daf427eef9a047812743a1e

mGBA Game Boy Advance Emulator

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