diff options
Diffstat (limited to 'src/libui_sdl/libui/unix/gl.c')
-rw-r--r-- | src/libui_sdl/libui/unix/gl.c | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/src/libui_sdl/libui/unix/gl.c b/src/libui_sdl/libui/unix/gl.c new file mode 100644 index 0000000..e1665ee --- /dev/null +++ b/src/libui_sdl/libui/unix/gl.c @@ -0,0 +1,246 @@ +// 26 may 2019 +#include "uipriv_unix.h" + +#include <GL/gl.h> +#include <GL/glx.h> +#include <EGL/egl.h> + +extern GThread* gtkthread; +extern GMutex glmutex; + +struct uiGLContext +{ + GtkWidget* widget; + GdkWindow* window; + + GdkGLContext *gctx; + int vermaj, vermin; + + int width, height; + int scale; + GLuint renderbuffer[2][2]; + GLuint framebuffer[2]; + int backbuffer; +}; + +static void areaAllocRenderbuffer(uiGLContext* glctx); + +static PFNGLGENRENDERBUFFERSPROC _glGenRenderbuffers; +static PFNGLDELETERENDERBUFFERSPROC _glDeleteRenderbuffers; +static PFNGLBINDRENDERBUFFERPROC _glBindRenderbuffer; +static PFNGLRENDERBUFFERSTORAGEPROC _glRenderbufferStorage; +static PFNGLGETRENDERBUFFERPARAMETERIVPROC _glGetRenderbufferParameteriv; + +static PFNGLGENRENDERBUFFERSPROC _glGenFramebuffers; +static PFNGLDELETERENDERBUFFERSPROC _glDeleteFramebuffers; +static PFNGLBINDRENDERBUFFERPROC _glBindFramebuffer; +static PFNGLFRAMEBUFFERTEXTUREPROC _glFramebufferTexture; +static PFNGLFRAMEBUFFERRENDERBUFFERPROC _glFramebufferRenderbuffer; +static PFNGLCHECKFRAMEBUFFERSTATUSPROC _glCheckFramebufferStatus; + +static int _procsLoaded = 0; + +static void _loadGLProcs(GdkGLContext* glctx) +{ + if (_procsLoaded) return; + + _glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)uiGLGetProcAddress("glGenRenderbuffers"); + _glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)uiGLGetProcAddress("glDeleteRenderbuffers"); + _glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)uiGLGetProcAddress("glBindRenderbuffer"); + _glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)uiGLGetProcAddress("glRenderbufferStorage"); + _glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)uiGLGetProcAddress("glGetRenderbufferParameteriv"); + + _glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)uiGLGetProcAddress("glGenFramebuffers"); + _glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)uiGLGetProcAddress("glDeleteFramebuffers"); + _glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)uiGLGetProcAddress("glBindFramebuffer"); + _glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC)uiGLGetProcAddress("glFramebufferTexture"); + _glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)uiGLGetProcAddress("glFramebufferRenderbuffer"); + _glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)uiGLGetProcAddress("glCheckFramebufferStatus"); + + _procsLoaded = 1; +} + +uiGLContext *createGLContext(GtkWidget* widget, int maj, int min) +{ + GdkWindow* gdkwin = gtk_widget_get_window(widget); + + GError* err = NULL; + GdkGLContext* gctx = gdk_window_create_gl_context(gdkwin, &err); + if (err != NULL || gctx == NULL) + { + return NULL; + } + + // TODO: make the set_use_es call conditional (#ifdef or smth) for older versions of gdk? + gdk_gl_context_set_use_es(gctx, FALSE); + gdk_gl_context_set_required_version(gctx, maj, min); + + gboolean res = gdk_gl_context_realize(gctx, &err); + if (err != NULL || res == FALSE) + { + return NULL; + } + + uiGLContext* ctx = uiNew(uiGLContext); + + GtkAllocation allocation; + gtk_widget_get_allocation(widget, &allocation); + int window_scale = gdk_window_get_scale_factor(gdkwin); + ctx->width = allocation.width; + ctx->height = allocation.height; + ctx->scale = window_scale; + + gdk_gl_context_make_current(gctx); + _loadGLProcs(gctx); + areaAllocRenderbuffer(ctx); + ctx->backbuffer = 0; + + ctx->widget = widget; + ctx->window = gdkwin; + ctx->gctx = gctx; + + return ctx; +} + +void freeGLContext(uiGLContext* glctx) +{ + if (glctx == NULL) return; + + gdk_gl_context_make_current(glctx->gctx); + _glDeleteRenderbuffers(4, &glctx->renderbuffer[0][0]); + _glDeleteFramebuffers(2, &glctx->framebuffer[0]); + + gdk_gl_context_clear_current(); + g_object_unref(glctx->gctx); + uiFree(glctx); +} + +static void areaAllocRenderbuffer(uiGLContext* glctx) +{ + // TODO: create textures as a fallback if GL_RGB renderbuffer isn't supported? + // they say GL implementations aren't required to support a GL_RGB renderbuffer + // however, a GL_RGBA one would cause gdk_cairo_draw_from_gl() to fall back to glReadPixels() + + _glGenRenderbuffers(4, &glctx->renderbuffer[0][0]); + _glGenFramebuffers(2, &glctx->framebuffer[0]); + + _glBindRenderbuffer(GL_RENDERBUFFER, glctx->renderbuffer[0][0]); + _glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB, glctx->width*glctx->scale, glctx->height*glctx->scale); + //_glBindRenderbuffer(GL_RENDERBUFFER, glctx->renderbuffer[0][1]); + //_glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, glctx->width*glctx->scale, glctx->height*glctx->scale); + + _glBindFramebuffer(GL_FRAMEBUFFER, glctx->framebuffer[0]); + _glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, glctx->renderbuffer[0][0]); + _glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, glctx->renderbuffer[0][1]); + + _glBindRenderbuffer(GL_RENDERBUFFER, glctx->renderbuffer[1][0]); + _glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB, glctx->width*glctx->scale, glctx->height*glctx->scale); + //_glBindRenderbuffer(GL_RENDERBUFFER, glctx->renderbuffer[1][1]); + //_glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, glctx->width*glctx->scale, glctx->height*glctx->scale); + + _glBindFramebuffer(GL_FRAMEBUFFER, glctx->framebuffer[1]); + _glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, glctx->renderbuffer[1][0]); + _glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, glctx->renderbuffer[1][1]); + + //if (_glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + // printf("FRAMEBUFFER IS BAD!! %04X\n", _glCheckFramebufferStatus(GL_FRAMEBUFFER)); +} + +static void areaReallocRenderbuffer(uiGLContext* glctx) +{ + _glBindRenderbuffer(GL_RENDERBUFFER, glctx->renderbuffer[0][0]); + _glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB, glctx->width*glctx->scale, glctx->height*glctx->scale); + //_glBindRenderbuffer(GL_RENDERBUFFER, glctx->renderbuffer[0][1]); + //_glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, glctx->width*glctx->scale, glctx->height*glctx->scale); + + _glBindRenderbuffer(GL_RENDERBUFFER, glctx->renderbuffer[1][0]); + _glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB, glctx->width*glctx->scale, glctx->height*glctx->scale); + //_glBindRenderbuffer(GL_RENDERBUFFER, glctx->renderbuffer[1][1]); + //_glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, glctx->width*glctx->scale, glctx->height*glctx->scale); +} + +void areaDrawGL(GtkWidget* widget, uiAreaDrawParams* dp, cairo_t* cr, uiGLContext* glctx) +{ + int window_scale = gdk_window_get_scale_factor(glctx->window); + + if (glctx->width != dp->AreaWidth || glctx->height != dp->AreaHeight || glctx->scale != window_scale) + { + glctx->width = dp->AreaWidth; + glctx->height = dp->AreaHeight; + glctx->scale = window_scale; + areaReallocRenderbuffer(glctx); + } + else + { + gdk_cairo_draw_from_gl(cr, gtk_widget_get_window(widget), + glctx->renderbuffer[glctx->backbuffer][0], GL_RENDERBUFFER, + 1, 0, 0, glctx->width*glctx->scale, glctx->height*glctx->scale); + } +} + +int uiGLGetFramebuffer(uiGLContext* ctx) +{ + return ctx->framebuffer[ctx->backbuffer]; +} + +float uiGLGetFramebufferScale(uiGLContext* ctx) +{ + return (float)ctx->scale; +} + +void uiGLSwapBuffers(uiGLContext* ctx) +{ + ctx->backbuffer = ctx->backbuffer ? 0 : 1; +} + +void uiGLMakeContextCurrent(uiGLContext* ctx) +{ + if (!ctx) + { + gdk_gl_context_clear_current(); + return; + } + + if (ctx->gctx == gdk_gl_context_get_current()) return; + gdk_gl_context_make_current(ctx->gctx); +} + +void uiGLBegin(uiGLContext* ctx) +{ + if (g_thread_self() != gtkthread) + { + g_mutex_lock(&glmutex); + } +} + +void uiGLEnd(uiGLContext* ctx) +{ + if (g_thread_self() != gtkthread) + { + g_mutex_unlock(&glmutex); + } +} + +void *uiGLGetProcAddress(const char* proc) +{ + // TODO: consider using epoxy or something funny + + void* ptr; + + ptr = glXGetProcAddressARB((const GLubyte*)proc); + if (ptr) return ptr; + + ptr = eglGetProcAddress(proc); + if (ptr) return ptr; + + ptr = dlsym(NULL /* RTLD_DEFAULT */, proc); + if (ptr) return ptr; + + return NULL; +} +unsigned int uiGLGetVersion(uiGLContext* ctx) +{ + if (!ctx) return 0; + return uiGLVersion(ctx->vermaj, ctx->vermin); +} + |