aboutsummaryrefslogtreecommitdiff
path: root/src/graphics
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2025-04-18 12:55:00 +0200
committerdec05eba <dec05eba@protonmail.com>2025-04-18 12:55:00 +0200
commit26c56565cc0573ce23eb8d172a6765bce1f657ce (patch)
treec4ceaacd336946acec12af5f2c5c4d2a78370744 /src/graphics
parent506c271eafec23bf469caf6c29431191fa885e58 (diff)
Separate glx and egl from window system to prepare for wayland
Diffstat (limited to 'src/graphics')
-rw-r--r--src/graphics/backend/egl.c246
-rw-r--r--src/graphics/backend/glx.c174
-rw-r--r--src/graphics/backend/graphics.c54
3 files changed, 474 insertions, 0 deletions
diff --git a/src/graphics/backend/egl.c b/src/graphics/backend/egl.c
new file mode 100644
index 0000000..f92e2fc
--- /dev/null
+++ b/src/graphics/backend/egl.c
@@ -0,0 +1,246 @@
+#include "../../../include/mgl/graphics/backend/egl.h"
+#include "../../../include/mgl/mgl.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <X11/Xutil.h>
+#include <X11/extensions/Xrender.h>
+
+static void mgl_graphics_egl_deinit(mgl_graphics *self);
+
+typedef struct {
+ EGLDisplay display;
+ EGLSurface surface;
+ EGLContext context;
+ EGLConfig *configs;
+ EGLConfig ecfg;
+ XVisualInfo *visual_info;
+} mgl_graphics_egl;
+
+static int32_t mgl_graphics_egl_get_config_attrib(mgl_graphics_egl *self, EGLConfig ecfg, int32_t attribute_name) {
+ mgl_context *context = mgl_get_context();
+ int32_t value = 0;
+ context->gl.eglGetConfigAttrib(self->display, ecfg, attribute_name, &value);
+ return value;
+}
+
+static bool xvisual_match_alpha(Display *dpy, XVisualInfo *visual_info, bool alpha) {
+ XRenderPictFormat *pict_format = XRenderFindVisualFormat(dpy, visual_info->visual);
+ if(!pict_format)
+ return false;
+ return (alpha && pict_format->direct.alphaMask > 0) || (!alpha && pict_format->direct.alphaMask == 0);
+}
+
+static bool mgl_graphics_context_choose(mgl_graphics_egl *self, mgl_context *context, bool alpha) {
+ self->configs = NULL;
+ self->ecfg = NULL;
+ self->visual_info = NULL;
+
+ int32_t num_configs = 0;
+ context->gl.eglGetConfigs(self->display, NULL, 0, &num_configs);
+ if(num_configs == 0) {
+ fprintf(stderr, "mgl error: no configs found\n");
+ return false;
+ }
+
+ self->configs = (EGLConfig*)calloc(num_configs, sizeof(EGLConfig));
+ if(!self->configs) {
+ fprintf(stderr, "mgl error: failed to allocate %d configs\n", (int)num_configs);
+ return false;
+ }
+
+ context->gl.eglGetConfigs(self->display, self->configs, num_configs, &num_configs);
+ for(int i = 0; i < num_configs; i++) {
+ self->ecfg = self->configs[i];
+
+ if(mgl_graphics_egl_get_config_attrib(self, self->ecfg, EGL_COLOR_BUFFER_TYPE) != EGL_RGB_BUFFER)
+ continue;
+
+ if(!(mgl_graphics_egl_get_config_attrib(self, self->ecfg, EGL_SURFACE_TYPE) & EGL_WINDOW_BIT))
+ continue;
+
+ if(mgl_graphics_egl_get_config_attrib(self, self->ecfg, EGL_RED_SIZE) != 8)
+ continue;
+
+ if(mgl_graphics_egl_get_config_attrib(self, self->ecfg, EGL_GREEN_SIZE) != 8)
+ continue;
+
+ if(mgl_graphics_egl_get_config_attrib(self, self->ecfg, EGL_BLUE_SIZE) != 8)
+ continue;
+
+ if(mgl_graphics_egl_get_config_attrib(self, self->ecfg, EGL_ALPHA_SIZE) != (alpha ? 8 : 0))
+ continue;
+
+ if(context->window_system == MGL_WINDOW_SYSTEM_WAYLAND)
+ break;
+
+ XVisualInfo vi = {0};
+ vi.visualid = mgl_graphics_egl_get_config_attrib(self, self->ecfg, EGL_NATIVE_VISUAL_ID);
+ if(!vi.visualid)
+ continue;
+
+ int vis_count = 0;
+ self->visual_info = XGetVisualInfo(context->connection, VisualIDMask, &vi, &vis_count);
+ if(!self->visual_info)
+ continue;
+
+ if(xvisual_match_alpha(context->connection, self->visual_info, alpha)) {
+ break;
+ } else {
+ XFree(self->visual_info);
+ self->visual_info = NULL;
+ self->ecfg = NULL;
+ }
+ }
+
+ if(context->window_system == MGL_WINDOW_SYSTEM_X11 && !self->visual_info) {
+ if(self->configs) {
+ free(self->configs);
+ self->configs = NULL;
+ }
+ self->ecfg = NULL;
+
+ fprintf(stderr, "mgl error: mgl_graphics_context_choose: no appropriate visual found\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool mgl_graphics_egl_make_context_current(mgl_graphics *self, mgl_window_handle window) {
+ (void)window;
+ mgl_graphics_egl *impl = self->impl;
+ mgl_context *context = mgl_get_context();
+
+ if(!impl->surface) {
+ impl->surface = context->gl.eglCreateWindowSurface(impl->display, impl->ecfg, (EGLNativeWindowType)window, NULL);
+ if(!impl->surface) {
+ fprintf(stderr, "mgl error: mgl_graphics_egl_make_context_current: failed to create window surface\n");
+ return false;
+ }
+ }
+
+ return context->gl.eglMakeCurrent(impl->display, impl->surface, impl->surface, impl->context) != 0;
+}
+
+static void mgl_graphics_egl_swap_buffers(mgl_graphics *self, mgl_window_handle window) {
+ (void)window;
+ mgl_graphics_egl *impl = self->impl;
+ mgl_context *context = mgl_get_context();
+ context->gl.eglSwapBuffers(impl->display, impl->surface);
+}
+
+static bool mgl_graphics_egl_set_swap_interval(mgl_graphics *self, mgl_window_handle window, bool enabled) {
+ (void)window;
+ mgl_graphics_egl *impl = self->impl;
+ mgl_context *context = mgl_get_context();
+ return context->gl.eglSwapInterval(impl->display, (int32_t)enabled) == 1;
+}
+
+static void* mgl_graphics_egl_get_xvisual_info(mgl_graphics *self) {
+ mgl_graphics_egl *impl = self->impl;
+ return impl->visual_info;
+}
+
+static void* mgl_graphics_egl_get_display(mgl_graphics *self) {
+ mgl_graphics_egl *impl = self->impl;
+ return impl->display;
+}
+
+static void* mgl_graphics_egl_get_context(mgl_graphics *self) {
+ mgl_graphics_egl *impl = self->impl;
+ return impl->context;
+}
+
+bool mgl_graphics_egl_init(mgl_graphics *self) {
+ mgl_graphics_egl *impl = calloc(1, sizeof(mgl_graphics_egl));
+ if(!impl)
+ return false;
+
+ self->deinit = mgl_graphics_egl_deinit;
+ self->make_context_current = mgl_graphics_egl_make_context_current;
+ self->swap_buffers = mgl_graphics_egl_swap_buffers;
+ self->set_swap_interval = mgl_graphics_egl_set_swap_interval;
+ self->get_xvisual_info = mgl_graphics_egl_get_xvisual_info;
+ self->get_display = mgl_graphics_egl_get_display;
+ self->get_context = mgl_graphics_egl_get_context;
+ self->impl = impl;
+
+ const int32_t ctxattr[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE, EGL_NONE
+ };
+
+ mgl_context *context = mgl_get_context();
+ context->gl.eglBindAPI(EGL_OPENGL_API);
+
+ impl->display = context->gl.eglGetDisplay((EGLNativeDisplayType)context->connection);
+ if(!impl->display) {
+ fprintf(stderr, "mgl error: mgl_graphics_egl_init: eglGetDisplay failed\n");
+ mgl_graphics_egl_deinit(self);
+ return false;
+ }
+
+ if(!context->gl.eglInitialize(impl->display, NULL, NULL)) {
+ fprintf(stderr, "mgl error: mgl_graphics_egl_init: eglInitialize failed\n");
+ mgl_graphics_egl_deinit(self);
+ return false;
+ }
+
+ if(!mgl_graphics_context_choose(impl, context, self->alpha)) {
+ mgl_graphics_egl_deinit(self);
+ return false;
+ }
+
+ impl->context = context->gl.eglCreateContext(impl->display, impl->ecfg, NULL, ctxattr);
+ if(!impl->context) {
+ fprintf(stderr, "mgl error: mgl_graphics_egl_init: failed to create egl context\n");
+ mgl_graphics_egl_deinit(self);
+ return false;
+ }
+
+ return true;
+}
+
+void mgl_graphics_egl_deinit(mgl_graphics *self) {
+ mgl_graphics_egl *impl = self->impl;
+ if(!impl)
+ return;
+
+ mgl_context *context = mgl_get_context();
+
+ if(impl->visual_info) {
+ XFree(impl->visual_info);
+ impl->visual_info = NULL;
+ }
+
+ if(impl->configs) {
+ free(impl->configs);
+ impl->configs = NULL;
+ }
+
+ if(impl->context) {
+ context->gl.eglMakeCurrent(impl->display, NULL, NULL, NULL);
+ context->gl.eglDestroyContext(impl->display, impl->context);
+ impl->context = NULL;
+ }
+
+ if(impl->surface) {
+ context->gl.eglDestroySurface(impl->display, impl->surface);
+ impl->surface = NULL;
+ }
+
+ if(impl->display) {
+ context->gl.eglTerminate(impl->display);
+ impl->display = NULL;
+ }
+
+ if(impl->visual_info) {
+ XFree(impl->visual_info);
+ impl->visual_info = NULL;
+ }
+
+ free(self->impl);
+ self->impl = NULL;
+}
diff --git a/src/graphics/backend/glx.c b/src/graphics/backend/glx.c
new file mode 100644
index 0000000..548b0a0
--- /dev/null
+++ b/src/graphics/backend/glx.c
@@ -0,0 +1,174 @@
+#include "../../../include/mgl/graphics/backend/glx.h"
+#include "../../../include/mgl/mgl.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <X11/Xutil.h>
+#include <X11/extensions/Xrender.h>
+
+static void mgl_graphics_glx_deinit(mgl_graphics *self);
+
+typedef struct {
+ GLXContext glx_context;
+ GLXFBConfig *fbconfigs;
+ GLXFBConfig fbconfig;
+ XVisualInfo *visual_info;
+} mgl_graphics_glx;
+
+static bool xvisual_match_alpha(Display *dpy, XVisualInfo *visual_info, bool alpha) {
+ XRenderPictFormat *pict_format = XRenderFindVisualFormat(dpy, visual_info->visual);
+ if(!pict_format)
+ return false;
+ return (alpha && pict_format->direct.alphaMask > 0) || (!alpha && pict_format->direct.alphaMask == 0);
+}
+
+static bool mgl_graphics_glx_context_choose(mgl_graphics_glx *self, mgl_context *context, bool alpha) {
+ const int attr[] = {
+ GLX_RENDER_TYPE, GLX_RGBA_BIT,
+ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
+ GLX_DOUBLEBUFFER, True,
+ GLX_RED_SIZE, 8,
+ GLX_GREEN_SIZE, 8,
+ GLX_BLUE_SIZE, 8,
+ GLX_ALPHA_SIZE, alpha ? 8 : 0,
+ // TODO:
+ //GLX_DEPTH_SIZE, 0,
+ None
+ };
+
+ self->fbconfigs = NULL;
+ self->visual_info = NULL;
+ self->fbconfig = NULL;
+
+ int numfbconfigs = 0;
+ self->fbconfigs = context->gl.glXChooseFBConfig(context->connection, DefaultScreen(context->connection), attr, &numfbconfigs);
+ for(int i = 0; i < numfbconfigs; i++) {
+ self->visual_info = (XVisualInfo*)context->gl.glXGetVisualFromFBConfig(context->connection, self->fbconfigs[i]);
+ if(!self->visual_info)
+ continue;
+
+ if(xvisual_match_alpha(context->connection, self->visual_info, alpha)) {
+ self->fbconfig = self->fbconfigs[i];
+ break;
+ } else {
+ XFree(self->visual_info);
+ self->visual_info = NULL;
+ self->fbconfig = NULL;
+ }
+ }
+
+ if(!self->visual_info) {
+ if(self->fbconfigs) {
+ XFree(self->fbconfigs);
+ self->fbconfigs = NULL;
+ }
+ self->fbconfig = NULL;
+
+ fprintf(stderr, "mgl error: no appropriate visual found\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool mgl_graphics_glx_make_context_current(mgl_graphics *self, mgl_window_handle window) {
+ mgl_graphics_glx *impl = self->impl;
+ mgl_context *context = mgl_get_context();
+ return context->gl.glXMakeContextCurrent(context->connection, (GLXDrawable)window, (GLXDrawable)window, impl->glx_context) != 0;
+}
+
+static void mgl_graphics_glx_swap_buffers(mgl_graphics *self, mgl_window_handle window) {
+ (void)self;
+ mgl_context *context = mgl_get_context();
+ context->gl.glXSwapBuffers(context->connection, (GLXDrawable)window);
+}
+
+/* TODO: Use gl OML present for other platforms than nvidia? nvidia doesn't support present yet */
+/* TODO: check for glx swap control extension string (GLX_EXT_swap_control, etc) */
+static bool mgl_graphics_glx_set_swap_interval(mgl_graphics *self, mgl_window_handle window, bool enabled) {
+ (void)self;
+ mgl_context *context = mgl_get_context();
+
+ int result = 0;
+ if(context->gl.glXSwapIntervalEXT) {
+ context->gl.glXSwapIntervalEXT(context->connection, (GLXDrawable)window, enabled);
+ } else if(context->gl.glXSwapIntervalMESA) {
+ result = context->gl.glXSwapIntervalMESA(enabled);
+ } else if(context->gl.glXSwapIntervalSGI) {
+ result = context->gl.glXSwapIntervalSGI(enabled);
+ } else {
+ static int warned = 0;
+ if (!warned) {
+ warned = 1;
+ fprintf(stderr, "mgl warning: setting vertical sync not supported\n");
+ }
+ }
+
+ if(result != 0)
+ fprintf(stderr, "mgl warning: setting vertical sync failed\n");
+
+ return result == 0;
+}
+
+static void* mgl_graphics_glx_get_xvisual_info(mgl_graphics *self) {
+ mgl_graphics_glx *impl = self->impl;
+ return impl->visual_info;
+}
+
+bool mgl_graphics_glx_init(mgl_graphics *self) {
+ mgl_graphics_glx *impl = calloc(1, sizeof(mgl_graphics_glx));
+ if(!impl)
+ return false;
+
+ self->deinit = mgl_graphics_glx_deinit;
+ self->make_context_current = mgl_graphics_glx_make_context_current;
+ self->swap_buffers = mgl_graphics_glx_swap_buffers;
+ self->set_swap_interval = mgl_graphics_glx_set_swap_interval;
+ self->get_xvisual_info = mgl_graphics_glx_get_xvisual_info;
+ self->impl = impl;
+
+ mgl_context *context = mgl_get_context();
+ if(!mgl_graphics_glx_context_choose(impl, context, self->alpha)) {
+ mgl_graphics_glx_deinit(self);
+ return false;
+ }
+
+ impl->glx_context = context->gl.glXCreateNewContext(context->connection, impl->fbconfig, GLX_RGBA_TYPE, 0, True);
+ if(!impl->glx_context) {
+ fprintf(stderr, "mgl error: mgl_graphics_glx_init: glXCreateContext failed\n");
+ mgl_graphics_glx_deinit(self);
+ return false;
+ }
+
+ return true;
+}
+
+void mgl_graphics_glx_deinit(mgl_graphics *self) {
+ mgl_graphics_glx *impl = self->impl;
+ if(!impl)
+ return;
+
+ mgl_context *context = mgl_get_context();
+
+ if(impl->glx_context) {
+ context->gl.glXMakeContextCurrent(context->connection, None, None, NULL);
+ context->gl.glXDestroyContext(context->connection, impl->glx_context);
+ impl->glx_context = NULL;
+ }
+
+ if(impl->visual_info) {
+ XFree(impl->visual_info);
+ impl->visual_info = NULL;
+ }
+
+ if(impl->fbconfigs) {
+ XFree(impl->fbconfigs);
+ impl->fbconfigs = NULL;
+ }
+
+ impl->fbconfig = NULL;
+
+ free(self->impl);
+ self->impl = NULL;
+}
diff --git a/src/graphics/backend/graphics.c b/src/graphics/backend/graphics.c
new file mode 100644
index 0000000..792894e
--- /dev/null
+++ b/src/graphics/backend/graphics.c
@@ -0,0 +1,54 @@
+#include "../../../include/mgl/graphics/backend/graphics.h"
+#include "../../../include/mgl/graphics/backend/glx.h"
+#include "../../../include/mgl/graphics/backend/egl.h"
+
+#include <string.h>
+
+bool mgl_graphics_init(mgl_graphics *self, const mgl_graphics_create_params *params) {
+ memset(self, 0, sizeof(*self));
+ self->graphics_api = params ? params->graphics_api : MGL_GRAPHICS_API_EGL;
+ self->alpha = params && params->alpha;
+
+ switch(self->graphics_api) {
+ case MGL_GRAPHICS_API_GLX:
+ return mgl_graphics_glx_init(self);
+ case MGL_GRAPHICS_API_EGL:
+ return mgl_graphics_egl_init(self);
+ }
+ return false;
+}
+
+void mgl_graphics_deinit(mgl_graphics *self) {
+ if(self->deinit)
+ self->deinit(self);
+}
+
+bool mgl_graphics_make_context_current(mgl_graphics *self, mgl_window_handle window) {
+ return self->make_context_current(self, window);
+}
+
+void mgl_graphics_swap_buffers(mgl_graphics *self, mgl_window_handle window) {
+ self->swap_buffers(self, window);
+}
+
+bool mgl_graphics_set_swap_interval(mgl_graphics *self, mgl_window_handle window, bool enabled) {
+ return self->set_swap_interval(self, window, enabled);
+}
+
+void* mgl_graphics_get_xvisual_info(mgl_graphics *self) {
+ return self->get_xvisual_info(self);
+}
+
+void* mgl_graphics_get_display(mgl_graphics *self) {
+ if(self->get_display)
+ return self->get_display(self);
+ else
+ return NULL;
+}
+
+void* mgl_graphics_get_context(mgl_graphics *self) {
+ if(self->get_context)
+ return self->get_context(self);
+ else
+ return NULL;
+}