diff options
Diffstat (limited to 'src/graphics/backend')
-rw-r--r-- | src/graphics/backend/egl.c | 248 | ||||
-rw-r--r-- | src/graphics/backend/glx.c | 173 | ||||
-rw-r--r-- | src/graphics/backend/graphics.c | 68 |
3 files changed, 489 insertions, 0 deletions
diff --git a/src/graphics/backend/egl.c b/src/graphics/backend/egl.c new file mode 100644 index 0000000..cabbf04 --- /dev/null +++ b/src/graphics/backend/egl.c @@ -0,0 +1,248 @@ +#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_egl_choose_config(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: mgl_graphics_egl_choose_config: no configs found\n"); + return false; + } + + self->configs = (EGLConfig*)calloc(num_configs, sizeof(EGLConfig)); + if(!self->configs) { + fprintf(stderr, "mgl error: mgl_graphics_egl_choose_config: 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_RENDERABLE_TYPE) != EGL_OPENGL_ES2_BIT) + // continue; + + 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(!self->ecfg) { + fprintf(stderr, "mgl error: mgl_graphics_egl_choose_config: no appropriate glx config found\n"); + return false; + } + + if(context->window_system == MGL_WINDOW_SYSTEM_X11 && !self->visual_info) { + fprintf(stderr, "mgl error: mgl_graphics_egl_choose_config: 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_egl_choose_config(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..70836e3 --- /dev/null +++ b/src/graphics/backend/glx.c @@ -0,0 +1,173 @@ +#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_choose_config(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->fbconfig) { + fprintf(stderr, "mgl error: mgl_graphics_glx_choose_config: no appropriate glx config found\n"); + return false; + } + + if(!self->visual_info) { + fprintf(stderr, "mgl error: mgl_graphics_glx_choose_config: 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_choose_config(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..201b9e3 --- /dev/null +++ b/src/graphics/backend/graphics.c @@ -0,0 +1,68 @@ +#include "../../../include/mgl/graphics/backend/graphics.h" +#include "../../../include/mgl/graphics/backend/glx.h" +#include "../../../include/mgl/graphics/backend/egl.h" +#include "../../../include/mgl/mgl.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) { + const bool result = self->make_context_current(self, window); + if(result) { + mgl_context *context = mgl_get_context(); + context->gl.glEnable(GL_TEXTURE_2D); + context->gl.glEnable(GL_BLEND); + context->gl.glEnable(GL_SCISSOR_TEST); + context->gl.glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + context->gl.glEnableClientState(GL_VERTEX_ARRAY); + context->gl.glEnableClientState(GL_TEXTURE_COORD_ARRAY); + context->gl.glEnableClientState(GL_COLOR_ARRAY); + context->gl.glPixelStorei(GL_PACK_ALIGNMENT, 1); + context->gl.glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + } + return result; +} + +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; +} |