1 #include <execinfo.h>
2 #include <fcntl.h>
3 #include <linux/fb.h>
4 #include <signal.h>
5 #include <sys/ioctl.h>
6 #include <sys/time.h>
7 #include <unistd.h>
8
9 #include "glx.h"
10 #include <GLES/gl.h>
11
12 bool eglInitialized = false;
13 EGLDisplay eglDisplay;
14 EGLSurface eglSurface;
15 EGLConfig eglConfigs[1];
16
CheckEGLErrors()17 int8_t CheckEGLErrors() {
18 EGLenum error;
19 char *errortext;
20
21 error = eglGetError();
22
23 if (error != EGL_SUCCESS && error != 0) {
24 switch (error) {
25 case EGL_NOT_INITIALIZED: errortext = "EGL_NOT_INITIALIZED"; break;
26 case EGL_BAD_ACCESS: errortext = "EGL_BAD_ACCESS"; break;
27 case EGL_BAD_ALLOC: errortext = "EGL_BAD_ALLOC"; break;
28 case EGL_BAD_ATTRIBUTE: errortext = "EGL_BAD_ATTRIBUTE"; break;
29 case EGL_BAD_CONTEXT: errortext = "EGL_BAD_CONTEXT"; break;
30 case EGL_BAD_CONFIG: errortext = "EGL_BAD_CONFIG"; break;
31 case EGL_BAD_CURRENT_SURFACE: errortext = "EGL_BAD_CURRENT_SURFACE"; break;
32 case EGL_BAD_DISPLAY: errortext = "EGL_BAD_DISPLAY"; break;
33 case EGL_BAD_SURFACE: errortext = "EGL_BAD_SURFACE"; break;
34 case EGL_BAD_MATCH: errortext = "EGL_BAD_MATCH"; break;
35 case EGL_BAD_PARAMETER: errortext = "EGL_BAD_PARAMETER"; break;
36 case EGL_BAD_NATIVE_PIXMAP: errortext = "EGL_BAD_NATIVE_PIXMAP"; break;
37 case EGL_BAD_NATIVE_WINDOW: errortext = "EGL_BAD_NATIVE_WINDOW"; break;
38 default: errortext = "unknown"; break;
39 }
40
41 printf("ERROR: EGL Error detected: %s (0x%X)\n", errortext, error);
42 return 1;
43 }
44
45 return 0;
46 }
47
get_config_default(int attribute,int * value)48 static int get_config_default(int attribute, int *value) {
49 switch (attribute) {
50 case GLX_USE_GL:
51 case GLX_RGBA:
52 case GLX_DOUBLEBUFFER:
53 *value = 1;
54 break;
55 case GLX_LEVEL:
56 case GLX_STEREO:
57 *value = 0;
58 break;
59 case GLX_AUX_BUFFERS:
60 *value = 0;
61 break;
62 case GLX_RED_SIZE:
63 *value = 5;
64 break;
65 case GLX_GREEN_SIZE:
66 *value = 6;
67 break;
68 case GLX_BLUE_SIZE:
69 *value = 5;
70 break;
71 case GLX_ALPHA_SIZE:
72 *value = 8;
73 break;
74 case GLX_DEPTH_SIZE:
75 *value = 16;
76 break;
77 case GLX_STENCIL_SIZE:
78 case GLX_ACCUM_RED_SIZE:
79 case GLX_ACCUM_GREEN_SIZE:
80 case GLX_ACCUM_BLUE_SIZE:
81 case GLX_ACCUM_ALPHA_SIZE:
82 *value = 0;
83 break;
84 case GLX_TRANSPARENT_TYPE:
85 *value = GLX_NONE;
86 break;
87 case GLX_RENDER_TYPE:
88 *value = GLX_RGBA_BIT | GLX_COLOR_INDEX_BIT;
89 break;
90 case GLX_VISUAL_ID:
91 *value = 1;
92 break;
93 case GLX_FBCONFIG_ID:
94 *value = 1;
95 break;
96 case GLX_DRAWABLE_TYPE:
97 *value = GLX_WINDOW_BIT;
98 break;
99 case GLX_BUFFER_SIZE:
100 *value = 16;
101 break;
102 case GLX_X_VISUAL_TYPE:
103 case GLX_CONFIG_CAVEAT:
104 case GLX_SAMPLE_BUFFERS:
105 case GLX_SAMPLES:
106 *value = 0;
107 break;
108 default:
109 printf("libGL: unknown attrib %i\n", attribute);
110 *value = 0;
111 return 1;
112 }
113 return 0;
114 }
115
116 // hmm...
117 static EGLContext eglContext;
118 static GLXContext glxContext;
119 static Display *g_display;
120
121 #ifndef FBIO_WAITFORVSYNC
122 #define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
123 #endif
124 static bool g_showfps = false;
125 static bool g_usefb = false;
126 static bool g_vsync = false;
127 static bool g_xrefresh = false;
128 static bool g_stacktrace = false;
129 static bool g_bcm_active = false;
130 #ifndef BCMHOST
131 static bool g_bcmhost = false;
132 #else
133 static bool g_bcmhost = true;
134 #endif
135
136 static int fbdev = -1;
137 static int swap_interval = 1;
138
init_display(Display * display)139 static void init_display(Display *display) {
140 if (! g_display) {
141 g_display = XOpenDisplay(NULL);
142 }
143 if (g_usefb) {
144 eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
145 } else {
146 eglDisplay = eglGetDisplay(g_display);
147 }
148 }
149
init_vsync()150 static void init_vsync() {
151 fbdev = open("/dev/fb0", O_RDONLY);
152 if (fbdev < 0) {
153 fprintf(stderr, "Could not open /dev/fb0 for vsync.\n");
154 }
155 }
156
xrefresh()157 static void xrefresh() {
158 system("xrefresh");
159 }
160
signal_handler(int sig)161 static void signal_handler(int sig) {
162 if (g_xrefresh)
163 xrefresh();
164
165 #ifdef BCMHOST
166 if (g_bcm_active) {
167 g_bcm_active = false;
168 bcm_host_deinit();
169 }
170 #endif
171
172 if (g_stacktrace) {
173 switch (sig) {
174 case SIGBUS:
175 case SIGFPE:
176 case SIGILL:
177 case SIGSEGV: {
178 void *array[10];
179 size_t size = backtrace(array, 10);
180 if (! size) {
181 printf("No stacktrace. Compile with -funwind-tables.\n");
182 } else {
183 printf("Stacktrace: %i\n", size);
184 backtrace_symbols_fd(array, size, 2);
185 }
186 break;
187 }
188 }
189 }
190 signal(sig, SIG_DFL);
191 raise(sig);
192 }
193
scan_env()194 static void scan_env() {
195 static bool first = true;
196 if (! first)
197 return;
198
199 first = false;
200 printf("libGL: built on %s %s\n", __DATE__, __TIME__);
201 #define env(name, global, message) \
202 char *env_##name = getenv(#name); \
203 if (env_##name && strcmp(env_##name, "1") == 0) { \
204 printf("libGL: " message "\n"); \
205 global = true; \
206 }
207
208 env(LIBGL_XREFRESH, g_xrefresh, "xrefresh will be called on cleanup");
209 env(LIBGL_STACKTRACE, g_stacktrace, "stacktrace will be printed on crash");
210 if (g_xrefresh || g_stacktrace || g_bcmhost) {
211 // TODO: a bit gross. Maybe look at this: http://stackoverflow.com/a/13290134/293352
212 signal(SIGBUS, signal_handler);
213 signal(SIGFPE, signal_handler);
214 signal(SIGILL, signal_handler);
215 signal(SIGSEGV, signal_handler);
216 if (g_xrefresh || g_bcmhost) {
217 signal(SIGINT, signal_handler);
218 signal(SIGQUIT, signal_handler);
219 signal(SIGTERM, signal_handler);
220 }
221 if (g_xrefresh)
222 atexit(xrefresh);
223 #ifdef BCMHOST
224 atexit(bcm_host_deinit);
225 #endif
226 }
227 env(LIBGL_FB, g_usefb, "framebuffer output enabled");
228 env(LIBGL_FPS, g_showfps, "fps counter enabled");
229 env(LIBGL_VSYNC, g_vsync, "vsync enabled");
230 if (g_vsync) {
231 init_vsync();
232 }
233 }
234
glXCreateContext(Display * display,XVisualInfo * visual,GLXContext shareList,Bool isDirect)235 GLXContext glXCreateContext(Display *display,
236 XVisualInfo *visual,
237 GLXContext shareList,
238 Bool isDirect) {
239 EGLint configAttribs[] = {
240 #ifdef PANDORA
241 EGL_RED_SIZE, 5,
242 EGL_GREEN_SIZE, 6,
243 EGL_BLUE_SIZE, 5,
244 #endif
245 EGL_DEPTH_SIZE, 16,
246 #ifdef USE_ES2
247 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
248 #else
249 EGL_BUFFER_SIZE, 16,
250 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
251 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
252 #endif
253 EGL_NONE
254 };
255
256 #ifdef USE_ES2
257 EGLint attrib_list[] = {
258 EGL_CONTEXT_CLIENT_VERSION, 2,
259 EGL_NONE
260 };
261 #else
262 EGLint *attrib_list = NULL;
263 #endif
264
265 scan_env();
266
267 #ifdef BCMHOST
268 if (! g_bcm_active) {
269 g_bcm_active = true;
270 bcm_host_init();
271 }
272 #endif
273
274 GLXContext fake = malloc(sizeof(struct __GLXContextRec));
275 if (eglDisplay != NULL) {
276 eglMakeCurrent(eglDisplay, NULL, NULL, EGL_NO_CONTEXT);
277 if (eglContext != NULL) {
278 eglDestroyContext(eglDisplay, eglContext);
279 eglContext = NULL;
280 }
281 if (eglSurface != NULL) {
282 eglDestroySurface(eglDisplay, eglSurface);
283 eglSurface = NULL;
284 }
285 }
286
287 // make an egl context here...
288 EGLBoolean result;
289 if (eglDisplay == NULL || eglDisplay == EGL_NO_DISPLAY) {
290 init_display(display);
291 if (eglDisplay == EGL_NO_DISPLAY) {
292 printf("Unable to create EGL display.\n");
293 return fake;
294 }
295 }
296
297 // first time?
298 if (eglInitialized == false) {
299 eglBindAPI(EGL_OPENGL_ES_API);
300 result = eglInitialize(eglDisplay, NULL, NULL);
301 if (result != EGL_TRUE) {
302 printf("Unable to initialize EGL display.\n");
303 return fake;
304 }
305 eglInitialized = true;
306 }
307
308 int configsFound;
309 result = eglChooseConfig(eglDisplay, configAttribs, eglConfigs, 1, &configsFound);
310 CheckEGLErrors();
311 if (result != EGL_TRUE || configsFound == 0) {
312 printf("No EGL configs found.\n");
313 return fake;
314 }
315 eglContext = eglCreateContext(eglDisplay, eglConfigs[0], EGL_NO_CONTEXT, attrib_list);
316 CheckEGLErrors();
317
318 // need to return a glx context pointing at it
319 fake->display = g_display;
320 fake->direct = true;
321 fake->xid = 1;
322 return fake;
323 }
324
glXCreateContextAttribsARB(Display * display,void * config,GLXContext share_context,Bool direct,const int * attrib_list)325 GLXContext glXCreateContextAttribsARB(Display *display, void *config,
326 GLXContext share_context, Bool direct,
327 const int *attrib_list) {
328 return glXCreateContext(display, NULL, NULL, direct);
329 }
330
glXDestroyContext(Display * display,GLXContext ctx)331 void glXDestroyContext(Display *display, GLXContext ctx) {
332 if (eglContext) {
333 EGLBoolean result = eglDestroyContext(eglDisplay, eglContext);
334 if (eglSurface != NULL) {
335 eglDestroySurface(eglDisplay, eglSurface);
336 }
337
338 if (result != EGL_TRUE) {
339 printf("Failed to destroy EGL context.\n");
340 }
341 if (fbdev >= 0) {
342 close(fbdev);
343 fbdev = -1;
344 }
345 }
346 return;
347 }
348
glXGetCurrentDisplay()349 Display *glXGetCurrentDisplay() {
350 if (g_display && eglContext) {
351 return g_display;
352 }
353 return NULL;
354 }
355
glXChooseVisual(Display * display,int screen,int * attributes)356 XVisualInfo *glXChooseVisual(Display *display,
357 int screen,
358 int *attributes) {
359
360 // apparently can't trust the Display I'm passed?
361 if (g_display == NULL) {
362 //g_display = XOpenDisplay(NULL);
363 g_display = display;
364 }
365
366 int default_depth = XDefaultDepth(display, screen);
367 if (default_depth != 16 && default_depth != 24)
368 printf("libGL: unusual desktop color depth %d\n", default_depth);
369
370 XVisualInfo *visual = (XVisualInfo *)malloc(sizeof(XVisualInfo));
371 if (!XMatchVisualInfo(display, screen, default_depth, TrueColor, visual)) {
372 printf("libGL: XMatchVisualInfo failed in glXChooseVisual\n");
373 return NULL;
374 }
375
376 return visual;
377 }
378
379 /*
380 EGL_BAD_MATCH is generated if draw or read are not compatible with context
381 or if context is set to EGL_NO_CONTEXT and draw or read are not set to
382 EGL_NO_SURFACE, or if draw or read are set to EGL_NO_SURFACE and context is
383 not set to EGL_NO_CONTEXT.
384 */
385
glXMakeCurrent(Display * display,int drawable,GLXContext context)386 Bool glXMakeCurrent(Display *display,
387 int drawable,
388 GLXContext context) {
389
390 if (eglDisplay != NULL) {
391 eglMakeCurrent(eglDisplay, NULL, NULL, EGL_NO_CONTEXT);
392 if (eglSurface != NULL) {
393 eglDestroySurface(eglDisplay, eglSurface);
394 }
395 }
396 // call with NULL to just destroy old stuff.
397 if (! context) {
398 return true;
399 }
400 if (eglDisplay == NULL) {
401 init_display(display);
402 }
403
404 if (g_usefb)
405 drawable = 0;
406 eglSurface = eglCreateWindowSurface(eglDisplay, eglConfigs[0], drawable, NULL);
407 CheckEGLErrors();
408
409 EGLBoolean result = eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
410 CheckEGLErrors();
411 if (result) {
412 return true;
413 }
414 return false;
415 }
416
glXMakeContextCurrent(Display * display,int drawable,int readable,GLXContext context)417 Bool glXMakeContextCurrent(Display *display, int drawable,
418 int readable, GLXContext context) {
419 return glXMakeCurrent(display, drawable, context);
420 }
421
glXSwapBuffers(Display * display,int drawable)422 void glXSwapBuffers(Display *display,
423 int drawable) {
424 static int frames = 0;
425
426 render_raster();
427 if (g_vsync && fbdev >= 0) {
428 // TODO: can I just return if I don't meet vsync over multiple frames?
429 // this will just block otherwise.
430 int arg = 0;
431 for (int i = 0; i < swap_interval; i++) {
432 ioctl(fbdev, FBIO_WAITFORVSYNC, &arg);
433 }
434 }
435 eglSwapBuffers(eglDisplay, eglSurface);
436 CheckEGLErrors();
437
438 if (g_showfps) {
439 // framerate counter
440 static float avg, fps = 0;
441 static int frame1, last_frame, frame, now, current_frames;
442 struct timeval out;
443 gettimeofday(&out, NULL);
444 now = out.tv_sec;
445 frame++;
446 current_frames++;
447
448 if (frame == 1) {
449 frame1 = now;
450 } else if (frame1 < now) {
451 if (last_frame < now) {
452 float change = current_frames / (float)(now - last_frame);
453 float weight = 0.7;
454 if (! fps) {
455 fps = change;
456 } else {
457 fps = (1 - weight) * fps + weight * change;
458 }
459 current_frames = 0;
460
461 avg = frame / (float)(now - frame1);
462 printf("libGL fps: %.2f, avg: %.2f\n", fps, avg);
463 }
464 }
465 last_frame = now;
466 }
467 }
468
glXGetConfig(Display * display,XVisualInfo * visual,int attribute,int * value)469 int glXGetConfig(Display *display,
470 XVisualInfo *visual,
471 int attribute,
472 int *value) {
473 return get_config_default(attribute, value);
474 }
475
glXQueryExtensionsString(Display * display,int screen)476 const char *glXQueryExtensionsString(Display *display, int screen) {
477 const char *extensions = {
478 "GLX_ARB_create_context "
479 "GLX_ARB_create_context_profile "
480 "GLX_EXT_create_context_es2_profile "
481 };
482 return extensions;
483 }
484
glXQueryServerString(Display * display,int screen,int name)485 const char *glXQueryServerString(Display *display, int screen, int name) {
486 return "";
487 }
488
glXQueryExtension(Display * display,int * errorBase,int * eventBase)489 Bool glXQueryExtension(Display *display, int *errorBase, int *eventBase) {
490 if (errorBase)
491 *errorBase = 0;
492
493 if (eventBase)
494 *eventBase = 0;
495
496 return true;
497 }
498
glXQueryVersion(Display * display,int * major,int * minor)499 Bool glXQueryVersion(Display *display, int *major, int *minor) {
500 // TODO: figure out which version we want to pretend to implement
501 *major = 1;
502 *minor = 4;
503 return true;
504 }
505
glXGetClientString(Display * display,int name)506 const char *glXGetClientString(Display *display, int name) {
507 // TODO: return actual data here
508 switch (name) {
509 case GLX_VENDOR: break;
510 case GLX_VERSION: break;
511 case GLX_EXTENSIONS: break;
512 }
513 return "";
514 }
515
glXQueryContext(Display * dpy,GLXContext ctx,int attribute,int * value)516 int glXQueryContext( Display *dpy, GLXContext ctx, int attribute, int *value ){
517 return 0;
518 }
519
520
glXQueryDrawable(Display * dpy,int draw,int attribute,unsigned int * value)521 void glXQueryDrawable( Display *dpy, int draw, int attribute,
522 unsigned int *value ) {
523 }
524
525 // stubs for glfw (GLX 1.3)
glXGetCurrentContext()526 GLXContext glXGetCurrentContext() {
527 // hack to make some games start
528 return glxContext ? glxContext : (void *)1;
529 }
530
glXChooseFBConfig(Display * display,int screen,const int * attrib_list,int * count)531 GLXFBConfig *glXChooseFBConfig(Display *display, int screen,
532 const int *attrib_list, int *count) {
533 *count = 1;
534 GLXFBConfig *configs = malloc(sizeof(GLXFBConfig) * *count);
535 return configs;
536 }
537
glXGetFBConfigs(Display * display,int screen,int * count)538 GLXFBConfig *glXGetFBConfigs(Display *display, int screen, int *count) {
539 *count = 1;
540 GLXFBConfig *configs = malloc(sizeof(GLXFBConfig) * *count);
541 return configs;
542 }
543
glXGetFBConfigAttrib(Display * display,GLXFBConfig config,int attribute,int * value)544 int glXGetFBConfigAttrib(Display *display, GLXFBConfig config, int attribute, int *value) {
545 return get_config_default(attribute, value);
546 }
547
glXGetVisualFromFBConfig(Display * display,GLXFBConfig config)548 XVisualInfo *glXGetVisualFromFBConfig(Display *display, GLXFBConfig config) {
549 if (g_display == NULL) {
550 g_display = XOpenDisplay(NULL);
551 }
552 XVisualInfo *visual = (XVisualInfo *)malloc(sizeof(XVisualInfo));
553 XMatchVisualInfo(g_display, 0, 16, TrueColor, visual);
554 return visual;
555 }
556
glXCreateNewContext(Display * display,GLXFBConfig config,int render_type,GLXContext share_list,Bool is_direct)557 GLXContext glXCreateNewContext(Display *display, GLXFBConfig config,
558 int render_type, GLXContext share_list,
559 Bool is_direct) {
560 return glXCreateContext(display, 0, share_list, is_direct);
561 }
562
glXSwapIntervalMESA(int interval)563 void glXSwapIntervalMESA(int interval) {
564 printf("glXSwapInterval(%i)\n", interval);
565 if (! g_vsync)
566 printf("Enable LIBGL_VSYNC=1 if you want to use vsync.\n");
567 swap_interval = interval;
568 }
569
glXSwapIntervalSGI(int interval)570 void glXSwapIntervalSGI(int interval) {
571 glXSwapIntervalMESA(interval);
572 }
573
glXSwapIntervalEXT(Display * display,int drawable,int interval)574 void glXSwapIntervalEXT(Display *display, int drawable, int interval) {
575 glXSwapIntervalMESA(interval);
576 }
577
578 // misc stubs
glXCopyContext(Display * display,GLXContext src,GLXContext dst,GLuint mask)579 void glXCopyContext(Display *display, GLXContext src, GLXContext dst, GLuint mask) {}
glXCreateGLXPixmap(Display * display,XVisualInfo * visual,Pixmap pixmap)580 void glXCreateGLXPixmap(Display *display, XVisualInfo * visual, Pixmap pixmap) {} // should return GLXPixmap
glXDestroyGLXPixmap(Display * display,void * pixmap)581 void glXDestroyGLXPixmap(Display *display, void *pixmap) {} // really wants a GLXpixmap
glXCreateWindow(Display * display,GLXFBConfig config,Window win,int * attrib_list)582 void glXCreateWindow(Display *display, GLXFBConfig config, Window win, int *attrib_list) {} // should return GLXWindow
glXDestroyWindow(Display * display,void * win)583 void glXDestroyWindow(Display *display, void *win) {} // really wants a GLXWindow
glXGetCurrentDrawable()584 void glXGetCurrentDrawable() {} // this should actually return GLXDrawable. Good luck.
glXIsDirect(Display * display,GLXContext ctx)585 Bool glXIsDirect(Display * display, GLXContext ctx) {
586 return true;
587 }
glXUseXFont(Font font,int first,int count,int listBase)588 void glXUseXFont(Font font, int first, int count, int listBase) {}
glXWaitGL()589 void glXWaitGL() {}
glXWaitX()590 void glXWaitX() {}
glXReleaseBuffersMESA()591 void glXReleaseBuffersMESA() {}
592