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