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