rk_engine/cpp/opengl/render_context_glx.cpp

277 lines
9.2 KiB
C++
Raw Normal View History

2022-08-28 04:23:13 +02:00
// 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 "render_context.hpp"
#include <cstdio>
#include <cstring>
#include <GLES3/gl32.h>
2022-08-28 04:23:13 +02:00
#include <X11/Xlib.h>
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
};
2022-08-28 04:23:13 +02:00
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,
rk_uint width,
rk_uint height) {
2022-08-28 04:23:13 +02:00
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);
2022-08-28 04:23:13 +02:00
if (!fbc) {
rk_printf("Failed to retrieve a framebuffer config.");
rk_destroy_context();
return nullptr;
}
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 srgb = 0;
glXGetFBConfigAttrib(rk_display, fbc[i], GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &srgb);
if (srgb) {
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 sRGB framebuffer.");
rk_destroy_context();
return nullptr;
}
GLXFBConfig const bestFbc = fbc[best_fbc];
XFree(fbc);
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,
2022-08-28 04:23:13 +02:00
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);
2022-08-28 04:23:13 +02:00
2022-09-19 02:25:52 +02:00
char const * const glx_exts = glXQueryExtensionsString(rk_display, DefaultScreen(rk_display));
glXCreateContextAttribsARBProc const glXCreateContextAttribsARB =
reinterpret_cast<glXCreateContextAttribsARBProc>(
glXGetProcAddressARB(reinterpret_cast<const GLubyte *>("glXCreateContextAttribsARB")));
2022-08-28 04:23:13 +02:00
rk_error_occured = false;
int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&rk_error_handler);
2022-09-19 02:25:52 +02:00
if (!rk_extension_supported(glx_exts, "GLX_ARB_create_context") || !glXCreateContextAttribsARB) {
rk_printf("glXCreateContextAttribsARB() extension not found.");
2022-08-28 04:23:13 +02:00
rk_destroy_context();
return nullptr;
} else {
rk_context = glXCreateContextAttribsARB(rk_display, bestFbc, 0, True, rk_context_attribs);
2022-08-28 04:23:13 +02:00
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);
2022-09-19 02:25:52 +02:00
char const * const gl_exts = reinterpret_cast<char const *>(glGetString(GL_EXTENSIONS));
if (rk_extension_supported(gl_exts, "GL_EXT_base_instance")) {
rk_DrawElementsInstancedBaseInstance =
reinterpret_cast<PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC>(
glXGetProcAddressARB(reinterpret_cast<const GLubyte *>("DrawElementsInstancedBaseInstance")));
if (rk_DrawElementsInstancedBaseInstance) {
rk_printf("Using extension GL_EXT_base_instance::DrawElementsInstancedBaseInstance.");
2022-09-19 02:25:52 +02:00
if (rk_extension_supported(gl_exts, "GL_EXT_multi_draw_indirect")) {
rk_MultiDrawElementsIndirect =
reinterpret_cast<PFNGLMULTIDRAWELEMENTSINDIRECTPROC>(
glXGetProcAddressARB(reinterpret_cast<const GLubyte *>("MultiDrawElementsIndirectEXT")));
if (rk_MultiDrawElementsIndirect) {
rk_printf("Using extension GL_EXT_multi_draw_indirect::MultiDrawElementsIndirectEXT.");
}
}
}
}
2022-08-28 04:23:13 +02:00
return reinterpret_cast<rk_window_t>(rk_window);
}
void rk_swap_buffers() {
if (rk_display && rk_window) {
glXSwapBuffers(rk_display, rk_window);
}
}
char ** rk_load_shader_source(
char const * filename,
rk_uint * length) {
char ** shader = nullptr;
char buffer[1024];
FILE * const file = fopen(filename, "rt");
if (file) {
int nlines = 0;
while (fgets(buffer, sizeof(buffer), file)) {
++nlines;
}
if (nlines) {
rewind(file);
shader = new char*[nlines];
for (int line = 0; line < nlines; ++line) {
shader[line] = new char[sizeof(buffer)];
fgets(shader[line], sizeof(buffer), file);
}
} else {
printf("Shader %s is empty.\n", filename);
}
fclose(file);
*length = nlines;
} else {
printf("Cannot open shader %s.\n", filename);
}
return shader;
}
void rk_free_shader_source(
char ** shader,
rk_uint length) {
if (shader) {
for (rk_uint line = 0; line < length; ++line) {
delete[] shader[line];
}
}
delete[] shader;
}
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;
}
}