rk_engine/cpp/display/display_glx.cpp

242 lines
8.2 KiB
C++
Raw Normal View History

// 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 <http://www.gnu.org/licenses/>.
// Adapted from https://www.khronos.org/opengl/wiki/Tutorial:_OpenGL_3.0_Context_Creation_(GLX)
#include "../display.hpp"
#include "display_glx.hpp"
2022-12-29 16:20:42 +01:00
#include "../events/events_x11.hpp"
#include <cstdio>
#include <cstring>
static bool rk_error_occured = false;
#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
typedef GLXContext (*rk_CreateContextAttribsFunc)(Display *, GLXFBConfig, GLXContext, Bool, int const *);
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
};
2022-12-29 16:20:42 +01:00
static void rk_x11_printf(
char const * messsage) {
printf("[X11] %s\n", messsage);
}
static void rk_glx_printf(
char const * messsage) {
printf("[GLX] %s\n", messsage);
}
static bool rk_extension_supported(
2022-12-29 16:20:42 +01:00
char const * extensions,
char const * extension) {
char const * where = strchr(extension, ' ');
if (where || *extension == '\0') {
return false;
}
2022-12-29 16:20:42 +01:00
for (char const * start = extensions;;) {
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;
}
2022-12-29 16:20:42 +01:00
rk_func_ptr rk_resolve_extension(
char const * extensions,
char const * extension,
char const * fn) {
if (!extensions || !extension || !fn) {
return nullptr;
}
if (!rk_extension_supported(extensions, extension)) {
printf("[GLX] Extension %s not supported\n", extension);
return nullptr;
}
rk_func_ptr const ptr = glXGetProcAddressARB(reinterpret_cast<GLubyte const *>(fn));
if (!ptr) {
printf("[GLX] Function %s::%s not found\n", extension, fn);
}
return ptr;
}
static int rk_error_handler(
Display * display,
XErrorEvent * event) {
rk_error_occured = true;
return 0;
}
rk_display_t rk_create_display(
rk_char const * name,
rk_uint width,
rk_uint height) {
if (!name || !width || !height) {
return nullptr;
};
rk_display_glx * const display = new rk_display_glx;
display->display = XOpenDisplay(nullptr);
display->window = 0;
display->colormap = 0;
display->context = nullptr;
if (!display->display) {
2022-12-29 16:20:42 +01:00
rk_x11_printf("Failed to open X display.");
rk_destroy_display(reinterpret_cast<rk_display_t>(display));
return nullptr;
}
int glx_major, glx_minor;
if (!glXQueryVersion(display->display, &glx_major, &glx_minor) ||
(glx_major == 1 && glx_minor < 3) || glx_major < 1) {
rk_glx_printf("Invalid GLX version.");
rk_destroy_display(reinterpret_cast<rk_display_t>(display));
return nullptr;
}
2022-12-29 16:20:42 +01:00
char const * const glx_exts = glXQueryExtensionsString(display->display, DefaultScreen(display->display));
2022-12-26 14:04:39 +01:00
// rk_glx_printf(glx_exts);
2022-12-29 16:20:42 +01:00
rk_CreateContextAttribsFunc const glXCreateContextAttribs = reinterpret_cast<rk_CreateContextAttribsFunc>(
rk_resolve_extension(glx_exts, "GLX_ARB_create_context", "glXCreateContextAttribsARB"));
if (!glXCreateContextAttribs) {
rk_destroy_display(reinterpret_cast<rk_display_t>(display));
return nullptr;
}
int fbcount;
GLXFBConfig * const fbc = glXChooseFBConfig(
display->display, DefaultScreen(display->display), rk_visual_attribs, &fbcount);
if (!fbc) {
rk_glx_printf("Failed to retrieve framebuffer configs.");
rk_destroy_display(reinterpret_cast<rk_display_t>(display));
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 * const vi = glXGetVisualFromFBConfig(display->display, fbc[i]);
if (vi) {
int samp_buf, samples;
glXGetFBConfigAttrib(display->display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
glXGetFBConfigAttrib(display->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_glx_printf("Failed to find a suitable framebuffer config.");
rk_destroy_display(reinterpret_cast<rk_display_t>(display));
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(display->display, bestFbc);
display->colormap = XCreateColormap(
display->display, RootWindow(display->display, vi->screen), vi->visual, AllocNone);
XSetWindowAttributes swa;
swa.colormap = display->colormap;
swa.event_mask = RK_EVENTS_MASK;
display->window = XCreateWindow(display->display, RootWindow(display->display, vi->screen),
2022-12-26 14:04:39 +01:00
0, 0, width, height, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &swa);
XFree(vi);
if (!display->window) {
2022-12-29 16:20:42 +01:00
rk_x11_printf("Failed to create window.");
rk_destroy_display(reinterpret_cast<rk_display_t>(display));
return nullptr;
}
XStoreName(display->display, display->window, name);
XMapWindow(display->display, display->window);
2022-12-29 16:20:42 +01:00
rk_error_occured = false;
int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&rk_error_handler);
display->context = glXCreateContextAttribs(display->display, bestFbc, 0, True, rk_context_attribs);
XSync(display->display, False);
XSetErrorHandler(oldHandler);
if (rk_error_occured || !display->context) {
rk_glx_printf("Failed to create context.");
rk_destroy_display(reinterpret_cast<rk_display_t>(display));
return nullptr;
}
if (!glXIsDirect(display->display, display->context)) {
rk_glx_printf("Warning: Rendering context is indirect.");
}
glXMakeCurrent(display->display, display->window, display->context);
return reinterpret_cast<rk_display_t>(display);
}
void rk_destroy_display(
rk_display_t _display) {
rk_display_glx * const display = reinterpret_cast<rk_display_glx *>(_display);
if (display) {
if (display->display) {
glXMakeCurrent(display->display, 0, nullptr);
if (display->context) {
glXDestroyContext(display->display, display->context);
}
if (display->window) {
XDestroyWindow(display->display, display->window);
}
if (display->colormap) {
XFreeColormap(display->display, display->colormap);
}
XCloseDisplay(display->display);
}
delete display;
}
}
void rk_swap_buffers(
rk_display_t _display) {
rk_display_glx const * const display = reinterpret_cast<rk_display_glx const *>(_display);
if (display && display->display && display->window) {
glXSwapBuffers(display->display, display->window);
}
}