// SPDX-License-Identifier: BSD-3-Clause // gz_pbuf.c - LD_PRELOAD shim that gives an SDL2 GL client a GLX pbuffer // context instead of a window context. The pbuffer path is proven on the // emulated vGPU (gl_frames); the window drawable path wedges the X server. // Build: gcc -shared -fPIC -O2 -o gz_pbuf.so gz_pbuf.c -lGL -lX11 -ldl #define _GNU_SOURCE #include #include #include #include #include #include #include typedef struct SDL_Window SDL_Window; static GLXContext g_ctx; static GLXPbuffer g_pb; static Display *g_dpy; static int g_w, g_h; static void *g_win; static void *(*real_GetWindowSize)(SDL_Window *, int *, int *); static int (*real_MakeCurrent)(SDL_Window *, void *); static void (*real_SwapWindow)(SDL_Window *); static void resolve(void) { if (!real_GetWindowSize) real_GetWindowSize = dlsym(RTLD_NEXT, "SDL_GetWindowSize"); if (!real_MakeCurrent) real_MakeCurrent = dlsym(RTLD_NEXT, "SDL_GL_MakeCurrent"); if (!real_SwapWindow) real_SwapWindow = dlsym(RTLD_NEXT, "SDL_GL_SwapWindow"); } static int make_pb(int w, int h) { static int attribs[] = { GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT, GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_ALPHA_SIZE, 8, GLX_DOUBLEBUFFER, False, None }; GLXFBConfig *fb; int n = 0, scr; if (!g_dpy) g_dpy = XOpenDisplay(NULL); if (!g_dpy) return 0; scr = DefaultScreen(g_dpy); fb = glXChooseFBConfig(g_dpy, scr, attribs, &n); if (!fb || n < 1) return 0; g_pb = glXCreatePbuffer(g_dpy, fb[0], (int[]){ GLX_PBUFFER_WIDTH, w, GLX_PBUFFER_HEIGHT, h, None }); if (!g_pb) return 0; g_ctx = glXCreateNewContext(g_dpy, fb[0], GLX_RGBA_TYPE, NULL, True); if (!g_ctx) { glXDestroyPbuffer(g_dpy, g_pb); g_pb = 0; return 0; } g_w = w; g_h = h; return 1; } void *SDL_GL_CreateContext(SDL_Window *win) { int w = 640, h = 480; const char *ew, *eh; resolve(); ew = getenv("GZPBUF_W"); eh = getenv("GZPBUF_H"); if (ew && eh) { w = atoi(ew); h = atoi(eh); } else if (real_GetWindowSize) { real_GetWindowSize(win, &w, &h); if (w <= 0 || h <= 0) { w = 640; h = 480; } } if (!make_pb(w, h)) { fprintf(stderr, "[gzpbuf] pbuffer create failed\n"); return NULL; } g_win = win; glXMakeCurrent(g_dpy, g_pb, g_ctx); fprintf(stderr, "[gzpbuf] pbuffer %dx%d ctx=%p\n", g_w, g_h, (void *)g_ctx); return (void *)g_ctx; } int SDL_GL_MakeCurrent(SDL_Window *win, void *ctx) { resolve(); if (ctx && g_pb) { glXMakeCurrent(g_dpy, g_pb, g_ctx); return 0; } if (real_MakeCurrent) return real_MakeCurrent(win, ctx); return 0; } void SDL_GL_SwapWindow(SDL_Window *win) { (void)win; glFinish(); } void SDL_GL_GetDrawableSize(SDL_Window *win, int *w, int *h) { resolve(); if (g_w > 0 && g_h > 0) { if (w) *w = g_w; if (h) *h = g_h; return; } if (real_GetWindowSize) { real_GetWindowSize(win, w, h); return; } if (w) *w = 640; if (h) *h = 480; } void SDL_GL_GetWindowSize(SDL_Window *win, int *w, int *h) { SDL_GL_GetDrawableSize(win, w, h); } void *SDL_GL_GetCurrentContext(void) { return (void *)g_ctx; } SDL_Window *SDL_GL_GetCurrentWindow(void) { return (SDL_Window *)g_win; } void SDL_GL_DeleteContext(void *ctx) { if (g_ctx && (!ctx || ctx == (void *)g_ctx)) { glXMakeCurrent(g_dpy, None, NULL); glXDestroyContext(g_dpy, g_ctx); if (g_pb) glXDestroyPbuffer(g_dpy, g_pb); g_ctx = NULL; g_pb = 0; } }