// Copyright (C) 2022 RozK // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . // Adapted from https://www.khronos.org/opengl/wiki/Tutorial:_OpenGL_3.0_Context_Creation_(GLX) #include "render_context.hpp" #include #include #include #include static Display * rk_display = nullptr; static Colormap rk_colormap = 0; static Window rk_window = 0; static GLXContext rk_context = nullptr; static bool rk_error_occured = false; #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 typedef GLXContext (*glXCreateContextAttribsARBProc)(Display *, GLXFBConfig, GLXContext, Bool, int const *); PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC rk_DrawElementsInstancedBaseInstance = nullptr; PFNGLMULTIDRAWELEMENTSINDIRECTPROC rk_MultiDrawElementsIndirect = nullptr; static int const rk_visual_attribs[] = { GLX_X_RENDERABLE, True, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_ALPHA_SIZE, 8, GLX_DEPTH_SIZE, 24, GLX_STENCIL_SIZE, 8, GLX_DOUBLEBUFFER, True, None }; static int const rk_context_attribs[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, 2, GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_ES_PROFILE_BIT_EXT, None }; static void rk_printf( char const * messsage) { printf("[GLX] %s\n", messsage); } static bool rk_extension_supported( char const * extlist, char const * extension) { char const * where = strchr(extension, ' '); if (where || *extension == '\0') { return false; } for (char const * start = extlist;;) { where = strstr(start, extension); if (!where) { break; } char const * const terminator = where + strlen(extension); if ((where == start || *(where - 1) == ' ') && (*terminator == ' ' || *terminator == '\0')) { return true; } start = terminator; } return false; } static int rk_error_handler( Display * display, XErrorEvent * event) { rk_error_occured = true; return 0; } rk_window_t rk_create_context( char const * name, unsigned width, unsigned height) { rk_display = XOpenDisplay(nullptr); if (!rk_display) { rk_printf("Failed to open X display."); return nullptr; } int glx_major, glx_minor; if (!glXQueryVersion(rk_display, &glx_major, &glx_minor) || (glx_major == 1 && glx_minor < 3) || glx_major < 1) { rk_printf("Invalid GLX version."); rk_destroy_context(); return nullptr; } int fbcount; GLXFBConfig * const fbc = glXChooseFBConfig(rk_display, DefaultScreen(rk_display), rk_visual_attribs, &fbcount); if (!fbc) { rk_printf("Failed to retrieve framebuffer configs."); rk_destroy_context(); return nullptr; } printf("[GLX] Found %d framebuffer configs.\n", fbcount); int best_fbc = -1; int best_num_samp = -1; for (int i = 0; i < fbcount; ++i) { XVisualInfo * vi = glXGetVisualFromFBConfig(rk_display, fbc[i]); if (vi) { int samp_buf, samples; glXGetFBConfigAttrib(rk_display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); glXGetFBConfigAttrib(rk_display, fbc[i], GLX_SAMPLES, &samples); if (best_fbc < 0 || (samp_buf && samples > best_num_samp)) { best_fbc = i; best_num_samp = samples; } XFree(vi); } } if (best_fbc == -1) { XFree(fbc); rk_printf("Failed to find a suitable framebuffer config."); rk_destroy_context(); return nullptr; } GLXFBConfig const bestFbc = fbc[best_fbc]; XFree(fbc); printf("[GLX] Select framebuffer config with %d samples.\n", best_num_samp); XVisualInfo * const vi = glXGetVisualFromFBConfig(rk_display, bestFbc); rk_colormap = XCreateColormap(rk_display, RootWindow(rk_display, vi->screen), vi->visual, AllocNone); XSetWindowAttributes swa; swa.colormap = rk_colormap; swa.background_pixmap = None; swa.border_pixel = 0; swa.event_mask = StructureNotifyMask; rk_window = XCreateWindow(rk_display, RootWindow(rk_display, vi->screen), 0, 0, width, height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask, &swa); if (!rk_window) { rk_printf("Failed to create window."); rk_destroy_context(); return nullptr; } XFree(vi); XStoreName(rk_display, rk_window, name); XMapWindow(rk_display, rk_window); char const * const glx_exts = glXQueryExtensionsString(rk_display, DefaultScreen(rk_display)); glXCreateContextAttribsARBProc const glXCreateContextAttribsARB = reinterpret_cast( glXGetProcAddressARB(reinterpret_cast("glXCreateContextAttribsARB"))); rk_error_occured = false; int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&rk_error_handler); if (!rk_extension_supported(glx_exts, "GLX_ARB_create_context") || !glXCreateContextAttribsARB) { rk_printf("glXCreateContextAttribsARB() extension not found."); rk_destroy_context(); return nullptr; } else { rk_context = glXCreateContextAttribsARB(rk_display, bestFbc, 0, True, rk_context_attribs); XSync(rk_display, False); if (rk_error_occured || !rk_context) { rk_printf("Failed to create context."); rk_destroy_context(); return nullptr; } } XSetErrorHandler(oldHandler); if (!glXIsDirect(rk_display, rk_context)) { rk_printf("Warning: Rendering context is indirect."); } glXMakeCurrent(rk_display, rk_window, rk_context); char const * const gl_exts = reinterpret_cast(glGetString(GL_EXTENSIONS)); if (rk_extension_supported(gl_exts, "GL_EXT_base_instance")) { rk_DrawElementsInstancedBaseInstance = reinterpret_cast( glXGetProcAddressARB(reinterpret_cast("DrawElementsInstancedBaseInstance"))); if (rk_DrawElementsInstancedBaseInstance) { rk_printf("Using extension GL_EXT_base_instance::DrawElementsInstancedBaseInstance."); if (rk_extension_supported(gl_exts, "GL_EXT_multi_draw_indirect")) { rk_MultiDrawElementsIndirect = reinterpret_cast( glXGetProcAddressARB(reinterpret_cast("MultiDrawElementsIndirectEXT"))); if (rk_MultiDrawElementsIndirect) { rk_printf("Using extension GL_EXT_multi_draw_indirect::MultiDrawElementsIndirectEXT."); } } } } return reinterpret_cast(rk_window); } void rk_swap_buffers() { if (rk_display && rk_window) { glXSwapBuffers(rk_display, rk_window); } } void rk_destroy_context() { if (rk_display) { glXMakeCurrent(rk_display, 0, nullptr); if (rk_context) { glXDestroyContext(rk_display, rk_context); rk_context = nullptr; } if (rk_window) { XDestroyWindow(rk_display, rk_window); rk_window = 0; } if (rk_colormap) { XFreeColormap(rk_display, rk_colormap); rk_colormap = 0; } XCloseDisplay(rk_display); rk_display = nullptr; } }