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->state < THREAD_EXITING) {
221 if (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 GBASyncWaitFrameEnd(&context->sync);
236
237 while (SDL_PollEvent(&event)) {
238 GBASDLHandleEvent(context, &event);
239 }
240 }
241}
242
243static void _GBAEGLDeinit(struct GBAVideoEGLRenderer* renderer) {
244 eglMakeCurrent(renderer->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
245 eglDestroySurface(renderer->display, renderer->surface);
246 eglDestroyContext(renderer->display, renderer->context);
247 eglTerminate(renderer->display);
248
249 GBASDLDeinitEvents(&renderer->events);
250 GBASDLDeinitAudio(&renderer->audio);
251 SDL_Quit();
252
253 bcm_host_deinit();
254}
255
256static void _GBASDLStart(struct GBAThread* threadContext) {
257 struct GBAVideoEGLRenderer* renderer = threadContext->userData;
258 renderer->audio.audio = &threadContext->gba->audio;
259}
260
261static void _GBASDLClean(struct GBAThread* threadContext) {
262 struct GBAVideoEGLRenderer* renderer = threadContext->userData;
263 renderer->audio.audio = 0;
264}