Split display from render and rework init/terminate.
This commit is contained in:
243
cpp/display/display_glx.cpp
Normal file
243
cpp/display/display_glx.cpp
Normal file
@ -0,0 +1,243 @@
|
||||
// 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"
|
||||
#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 *);
|
||||
|
||||
rk_DrawElementsInstancedBaseInstanceFunc rk_DrawElementsInstancedBaseInstance = nullptr;
|
||||
rk_MultiDrawElementsIndirectFunc 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_glx_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_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) {
|
||||
rk_glx_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;
|
||||
}
|
||||
char const * const glx_exts = glXQueryExtensionsString(display->display, DefaultScreen(display->display));
|
||||
|
||||
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.background_pixmap = None;
|
||||
// swa.border_pixel = 0;
|
||||
swa.event_mask = RK_EVENTS_MASK;
|
||||
display->window = XCreateWindow(display->display, RootWindow(display->display, vi->screen),
|
||||
0, 0, width, height, 0, vi->depth, InputOutput, vi->visual,
|
||||
/*CWBorderPixel |*/ CWColormap | CWEventMask, &swa);
|
||||
XFree(vi);
|
||||
if (!display->window) {
|
||||
rk_glx_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);
|
||||
|
||||
rk_CreateContextAttribsFunc const glXCreateContextAttribs =
|
||||
reinterpret_cast<rk_CreateContextAttribsFunc>(
|
||||
glXGetProcAddressARB(reinterpret_cast<GLubyte const *>("glXCreateContextAttribsARB")));
|
||||
|
||||
if (!rk_extension_supported(glx_exts, "GLX_ARB_create_context") || !glXCreateContextAttribs) {
|
||||
rk_glx_printf("glXCreateContextAttribsARB extension not found.");
|
||||
rk_destroy_display(reinterpret_cast<rk_display_t>(display));
|
||||
return nullptr;
|
||||
} else {
|
||||
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);
|
||||
|
||||
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<rk_DrawElementsInstancedBaseInstanceFunc>(
|
||||
glXGetProcAddressARB(reinterpret_cast<GLubyte const *>("DrawElementsInstancedBaseInstance")));
|
||||
if (rk_DrawElementsInstancedBaseInstance) {
|
||||
rk_glx_printf("Using extension GL_EXT_base_instance::DrawElementsInstancedBaseInstance.");
|
||||
if (rk_extension_supported(gl_exts, "GL_EXT_multi_draw_indirect")) {
|
||||
rk_MultiDrawElementsIndirect =
|
||||
reinterpret_cast<rk_MultiDrawElementsIndirectFunc>(
|
||||
glXGetProcAddressARB(reinterpret_cast<GLubyte const *>("MultiDrawElementsIndirectEXT")));
|
||||
if (rk_MultiDrawElementsIndirect) {
|
||||
rk_glx_printf("Using extension GL_EXT_multi_draw_indirect::MultiDrawElementsIndirectEXT.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
34
cpp/display/display_glx.hpp
Normal file
34
cpp/display/display_glx.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
// 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/>.
|
||||
|
||||
#ifndef _RK_ENGINE_DISPLAY_GLX_H
|
||||
#define _RK_ENGINE_DISPLAY_GLX_H
|
||||
|
||||
#include "../types.hpp"
|
||||
#include "display_x11.hpp"
|
||||
#include "../events/events_x11.hpp"
|
||||
#include <GL/glx.h>
|
||||
|
||||
struct rk_display_glx : public rk_display_x11 {
|
||||
GLXContext context;
|
||||
};
|
||||
|
||||
typedef void (*rk_DrawElementsInstancedBaseInstanceFunc)(rk_uint, rk_uint, rk_uint, const void *, rk_uint, rk_uint);
|
||||
typedef void (*rk_MultiDrawElementsIndirectFunc)(rk_uint, rk_uint, const void *, rk_uint, rk_uint);
|
||||
|
||||
extern rk_DrawElementsInstancedBaseInstanceFunc rk_DrawElementsInstancedBaseInstance;
|
||||
extern rk_MultiDrawElementsIndirectFunc rk_MultiDrawElementsIndirect;
|
||||
|
||||
#endif // _RK_ENGINE_DISPLAY_GLX_H
|
27
cpp/display/display_x11.hpp
Normal file
27
cpp/display/display_x11.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
// 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/>.
|
||||
|
||||
#ifndef _RK_ENGINE_DISPLAY_X11_H
|
||||
#define _RK_ENGINE_DISPLAY_X11_H
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
struct rk_display_x11 {
|
||||
Display * display;
|
||||
Window window;
|
||||
Colormap colormap;
|
||||
};
|
||||
|
||||
#endif // _RK_ENGINE_DISPLAY_X11_H
|
Reference in New Issue
Block a user