Split display from render and rework init/terminate.

This commit is contained in:
Roz K 2022-12-24 11:27:53 +01:00
parent 0c560890a4
commit 026ead0b33
Signed by: roz
GPG Key ID: 51FBF4E483E1C822
14 changed files with 633 additions and 482 deletions

View File

@ -14,9 +14,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
SOURCES = \
cpp/opengl/render_context_glx.cpp \
cpp/opengl/render_opengles.cpp \
cpp/display/display_glx.cpp \
cpp/events/events_x11.cpp \
cpp/render/render_opengles.cpp \
cpp/math.cpp
OUTPUTFILE = engine.so

View File

@ -19,6 +19,31 @@ from pathlib import Path
_engine = ctypes.cdll.LoadLibrary(Path(__file__).parent / "engine.so")
# types.hpp
def _flag(x):
return 1 << x
def buffer(type, size):
return (type * size)()
def _voidp(x):
return x.buffer_info()[0]
def _ubytep(x):
assert x.typecode == 'B'
return x.buffer_info()[0]
def _ushortp(x):
assert x.typecode == 'H'
return x.buffer_info()[0]
def _floatp(x):
assert x.typecode == 'f'
return x.buffer_info()[0]
# math.hpp
class vec3(ctypes.Structure):
_fields_ = ('x', ctypes.c_float), ('y', ctypes.c_float), ('z', ctypes.c_float)
@ -42,71 +67,14 @@ class mat3(ctypes.Structure):
class mat4(ctypes.Structure):
_fields_ = ('x', vec4), ('y', vec4), ('z', vec4), ('w', vec4)
vec3_right = vec3(1.0, 0.0, 0.0)
vec3_forward = vec3(0.0, 1.0, 0.0)
vec3_up = vec3(0.0, 0.0, 1.0)
def _flag(x):
return 1 << x
TEXTURE_FORMAT_SRGB8_A8 = 0
TEXTURE_FORMAT_RGBA8 = 1
TEXTURE_FORMAT_RGB10_A2 = 2
TEXTURE_FORMAT_32F = 3
TEXTURE_FORMAT_TYPECODE = ('B', 'B', 'I', 'f')
TEXTURE_FORMAT_NELEMS = (4, 4, 1, 1)
TEXTURE_FLAG_3D = _flag(0)
TEXTURE_FLAG_MIPMAPS = _flag(1)
TEXTURE_FLAG_MIN_NEAREST = 0
TEXTURE_FLAG_MIN_LINEAR = _flag(2)
TEXTURE_FLAG_MAG_NEAREST = 0
TEXTURE_FLAG_MAG_LINEAR = _flag(3)
VERTEX_FORMAT_VEC3_FLOAT = 1
VERTEX_FORMAT_VEC3_INT10 = 2
VERTEX_FORMAT_VEC3_UINT10 = 3
VERTEX_FORMAT_NORMALIZE = _flag(7)
def vertex_format(*format):
return array('B', format).tobytes()
PARAM_FORMAT_VEC3_FLOAT = 1
PARAM_FORMAT_VEC3_SHORT = 2
PARAM_FORMAT_VEC3_INT10 = 3
PARAM_FORMAT_NORMALIZE = _flag(7)
def params_format(*format):
return array('B', format).tobytes()
INSTANCE_FLAG_SPAWNED = _flag(0)
INSTANCE_FLAG_VISIBLE = _flag(1)
BATCH_MAX_SIZE = 65536
def buffer(type, size):
return (type * size)()
_vec3p = ctypes.POINTER(vec3)
_vec4p = ctypes.POINTER(vec4)
_mat3p = ctypes.POINTER(mat3)
_mat4p = ctypes.POINTER(mat4)
def _voidp(x):
return x.buffer_info()[0]
def _ubytep(x):
assert x.typecode == 'B'
return x.buffer_info()[0]
def _ushortp(x):
assert x.typecode == 'H'
return x.buffer_info()[0]
def _floatp(x):
assert x.typecode == 'f'
return x.buffer_info()[0]
vec3_right = vec3(1.0, 0.0, 0.0)
vec3_forward = vec3(0.0, 1.0, 0.0)
vec3_up = vec3(0.0, 0.0, 1.0)
vec3_rotate = _engine.rk_vec3_rotate
vec3_rotate.argtypes = (
@ -173,13 +141,64 @@ mat4_mul_mat4.argtypes = (
_mat4p, # a
_mat4p) # b
initialize = _engine.rk_initialize
initialize.restype = ctypes.c_void_p
initialize.argtypes = (
# display.hpp
create_display = _engine.rk_create_display
create_display.restype = ctypes.c_void_p
create_display.argtypes = (
ctypes.c_char_p, # name
ctypes.c_uint, # width
ctypes.c_uint) # height
destroy_display = _engine.rk_destroy_display
destroy_display.argtypes = (
ctypes.c_void_p,) # display
swap_buffers = _engine.rk_swap_buffers
swap_buffers.argtypes = (
ctypes.c_void_p,) # display
# render.hpp
TEXTURE_FORMAT_SRGB8_A8 = 0
TEXTURE_FORMAT_RGBA8 = 1
TEXTURE_FORMAT_RGB10_A2 = 2
TEXTURE_FORMAT_32F = 3
TEXTURE_FORMAT_TYPECODE = ('B', 'B', 'I', 'f')
TEXTURE_FORMAT_NELEMS = (4, 4, 1, 1)
TEXTURE_FLAG_3D = _flag(0)
TEXTURE_FLAG_MIPMAPS = _flag(1)
TEXTURE_FLAG_MIN_NEAREST = 0
TEXTURE_FLAG_MIN_LINEAR = _flag(2)
TEXTURE_FLAG_MAG_NEAREST = 0
TEXTURE_FLAG_MAG_LINEAR = _flag(3)
VERTEX_FORMAT_VEC3_FLOAT = 1
VERTEX_FORMAT_VEC3_INT10 = 2
VERTEX_FORMAT_VEC3_UINT10 = 3
VERTEX_FORMAT_NORMALIZE = _flag(7)
def vertex_format(*format):
return array('B', format).tobytes()
PARAM_FORMAT_VEC3_FLOAT = 1
PARAM_FORMAT_VEC3_SHORT = 2
PARAM_FORMAT_VEC3_INT10 = 3
PARAM_FORMAT_NORMALIZE = _flag(7)
def params_format(*format):
return array('B', format).tobytes()
INSTANCE_FLAG_SPAWNED = _flag(0)
INSTANCE_FLAG_VISIBLE = _flag(1)
BATCH_MAX_SIZE = 65536
render_initialize = _engine.rk_render_initialize
render_terminate = _engine.rk_render_terminate
_load_shader = _engine.rk_load_shader
_load_shader.restype = ctypes.c_void_p
_load_shader.argtypes = (
@ -343,7 +362,7 @@ destroy_shader = _engine.rk_destroy_shader
destroy_shader.argtypes = (
ctypes.c_void_p,) # shader
terminate = _engine.rk_terminate
# events.hpp
EVENT_FOCUS_IN = 0
EVENT_FOCUS_OUT = 1
@ -382,12 +401,24 @@ class _Events(ctypes.Union):
class Event(ctypes.Structure):
_fields_ = ('type', ctypes.c_uint), ('data', _Events)
set_autorepeat = _engine.rk_set_autorepeat
set_autorepeat.argtypes = (
ctypes.c_bool,) # autorepeat
create_events = _engine.rk_create_events
create_events.restype = ctypes.c_void_p
create_events.argtypes = (
ctypes.c_void_p,) # display
set_acceleration = _engine.rk_set_acceleration
set_acceleration.argtypes = (
destroy_events = _engine.rk_destroy_events
destroy_events.argtypes = (
ctypes.c_void_p, # display
ctypes.c_void_p) # events
set_key_autorepeat = _engine.rk_set_key_autorepeat
set_key_autorepeat.argtypes = (
ctypes.c_void_p, # events
ctypes.c_bool) # autorepeat
set_motion_acceleration = _engine.rk_set_motion_acceleration
set_motion_acceleration.argtypes = (
ctypes.c_void_p, # events
ctypes.c_uint, # numerator
ctypes.c_uint, # denominator
ctypes.c_uint) # threshold
@ -395,5 +426,6 @@ set_acceleration.argtypes = (
consume_events = _engine.rk_consume_events
consume_events.restype = ctypes.c_uint
consume_events.argtypes = (
ctypes.POINTER(Event), # events
ctypes.c_void_p, # events
ctypes.POINTER(Event), # buffer
ctypes.c_uint) # max_events

34
cpp/display.hpp Normal file
View 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_H
#define _RK_ENGINE_DISPLAY_H
#include "types.hpp"
typedef rk_handle_t rk_display_t;
RK_EXPORT rk_display_t rk_create_display(
rk_char const * name,
rk_uint width,
rk_uint height);
RK_EXPORT void rk_destroy_display(
rk_display_t display);
RK_EXPORT void rk_swap_buffers(
rk_display_t display);
#endif // _RK_ENGINE_DISPLAY_H

243
cpp/display/display_glx.cpp Normal file
View 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);
}
}

View File

@ -13,10 +13,17 @@
// 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_RENDER_CONTEXT_H
#define _RK_ENGINE_RENDER_CONTEXT_H
#ifndef _RK_ENGINE_DISPLAY_GLX_H
#define _RK_ENGINE_DISPLAY_GLX_H
#include "../render.hpp"
#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);
@ -24,12 +31,4 @@ typedef void (*rk_MultiDrawElementsIndirectFunc)(rk_uint, rk_uint, const void *,
extern rk_DrawElementsInstancedBaseInstanceFunc rk_DrawElementsInstancedBaseInstance;
extern rk_MultiDrawElementsIndirectFunc rk_MultiDrawElementsIndirect;
extern rk_window_t rk_create_context(
char const * name,
unsigned width,
unsigned height);
extern void rk_swap_buffers();
extern void rk_destroy_context();
#endif // _RK_ENGINE_RENDER_CONTEXT_H
#endif // _RK_ENGINE_DISPLAY_GLX_H

View 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

View File

@ -17,6 +17,9 @@
#define _RK_ENGINE_EVENTS_H
#include "types.hpp"
#include "display.hpp"
typedef rk_handle_t rk_events_t;
enum rk_event_type : rk_uint {
RK_EVENT_FOCUS_IN = 0,
@ -52,19 +55,26 @@ struct rk_event {
};
};
extern bool rk_initialize_events();
extern void rk_terminate_events();
RK_EXPORT rk_events_t rk_create_events(
rk_display_t display);
RK_EXPORT void rk_set_autorepeat(
RK_EXPORT void rk_destroy_events(
rk_display_t display,
rk_events_t events);
RK_EXPORT void rk_set_key_autorepeat(
rk_events_t events,
rk_bool autorepeat);
RK_EXPORT void rk_set_acceleration(
RK_EXPORT void rk_set_motion_acceleration(
rk_events_t events,
rk_uint numerator,
rk_uint denominator,
rk_uint threshold);
RK_EXPORT rk_uint rk_consume_events(
rk_event * events,
rk_events_t events,
rk_event * buffer,
rk_uint max_events);
#endif // _RK_ENGINE_EVENTS_H

View File

@ -14,144 +14,168 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "../events.hpp"
#include <X11/Xlib.h>
#include "events_x11.hpp"
#include "../display/display_x11.hpp"
#include <cstdio>
extern Display * rk_display;
extern Window rk_window;
unsigned rk_events_mask = FocusChangeMask |
KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
static XIM rk_input_manager = nullptr;
static XIC rk_input_context = nullptr;
static bool rk_focused = false;
static bool rk_keyboard_autorepeat = false;
static XKeyboardState rk_previous_keyboard_state;
static int rk_previous_accel_numerator, rk_accel_numerator = 1;
static int rk_previous_accel_denominator, rk_accel_denominator = 1;
static int rk_previous_threshold, rk_accel_threshold = 0;
static void rk_backup_setup() {
XGetKeyboardControl(rk_display, &rk_previous_keyboard_state);
XGetPointerControl(rk_display,
&rk_previous_accel_numerator, &rk_previous_accel_denominator, &rk_previous_threshold);
static void rk_x11_printf(
char const * messsage) {
printf("[X11] %s\n", messsage);
}
static void rk_restore_setup() {
if (AutoRepeatModeOn == rk_previous_keyboard_state.global_auto_repeat) {
XAutoRepeatOn(rk_display);
static void rk_backup(rk_events_x11 & events) {
XKeyboardState backup;
XGetKeyboardControl(events.display, &backup);
events.backup_autorepeat = (AutoRepeatModeOn == backup.global_auto_repeat);
XGetPointerControl(
events.display, &events.backup_numerator, &events.backup_denominator, &events.backup_threshold);
}
static void rk_restore(rk_events_x11 & events) {
if (events.backup_autorepeat) {
XAutoRepeatOn(events.display);
} else {
XAutoRepeatOff(rk_display);
XAutoRepeatOff(events.display);
}
XChangePointerControl(rk_display, True, True,
rk_previous_accel_numerator, rk_previous_accel_denominator, rk_previous_threshold);
XChangePointerControl(
events.display, True, True, events.backup_numerator, events.backup_denominator, events.backup_threshold);
}
static void rk_apply_setup() {
if (rk_keyboard_autorepeat) {
XAutoRepeatOn(rk_display);
static void rk_apply(rk_events_x11 & events) {
if (events.key_autorepeat) {
XAutoRepeatOn(events.display);
} else {
XAutoRepeatOff(rk_display);
XAutoRepeatOff(events.display);
}
XChangePointerControl(rk_display, True, True, rk_accel_numerator, rk_accel_denominator, rk_accel_threshold);
XChangePointerControl(
events.display, True, True, events.motion_numerator, events.motion_denominator, events.motion_threshold);
}
bool rk_initialize_events() {
rk_focused = false;
rk_backup_setup();
rk_keyboard_autorepeat = (AutoRepeatModeOn == rk_previous_keyboard_state.global_auto_repeat);
rk_accel_numerator = rk_previous_accel_numerator;
rk_accel_denominator = rk_previous_accel_denominator;
rk_accel_threshold = rk_previous_threshold;
rk_input_manager = XOpenIM(rk_display, nullptr, nullptr, nullptr);
if (!rk_input_manager) {
return false;
}
rk_input_context = XCreateIC(rk_input_manager,
XNInputStyle, XIMPreeditNone | XIMStatusNone,
XNClientWindow, rk_window,
NULL);
if (!rk_input_context) {
return false;
}
XSetICFocus(rk_input_context);
return true;
}
void rk_terminate_events() {
rk_restore_setup();
if (rk_input_context) {
XUnsetICFocus(rk_input_context);
XDestroyIC(rk_input_context);
rk_input_context = nullptr;
}
if (rk_input_manager) {
XCloseIM(rk_input_manager);
rk_input_manager = nullptr;
}
}
static void rk_focus_in() {
if (!rk_focused) {
rk_backup_setup();
rk_apply_setup();
XGrabPointer(rk_display, rk_window, True,
static void rk_focus_in(rk_events_x11 & events) {
if (!events.focused) {
rk_backup(events);
rk_apply(events);
XGrabPointer(events.display, events.window, True,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
GrabModeAsync, GrabModeAsync, rk_window, None, CurrentTime);
rk_focused = true;
GrabModeAsync, GrabModeAsync, events.window, None, CurrentTime);
events.focused = true;
}
}
static void rk_focus_out() {
if (rk_focused) {
XUngrabPointer(rk_display, CurrentTime);
rk_restore_setup();
rk_focused = false;
static void rk_focus_out(rk_events_x11 & events) {
if (events.focused) {
XUngrabPointer(events.display, CurrentTime);
rk_restore(events);
events.focused = false;
}
}
void rk_set_autorepeat(
rk_events_t rk_create_events(
rk_display_t _display) {
rk_display_x11 const * const display = reinterpret_cast<rk_display_x11 const *>(_display);
if (!display || !display->display || !display->window) {
return nullptr;
}
rk_events_x11 * const events = new rk_events_x11;
events->display = display->display;
events->window = display->window;
events->input_manager = XOpenIM(display->display, nullptr, nullptr, nullptr);
if (!events->input_manager) {
rk_x11_printf("Failed to open input manager.");
delete events;
return nullptr;
}
events->input_context = XCreateIC(events->input_manager,
XNInputStyle, XIMPreeditNone | XIMStatusNone,
XNClientWindow, display->window,
NULL);
if (!events->input_context) {
rk_x11_printf("Failed to create input context.");
XCloseIM(events->input_manager);
delete events;
return nullptr;
}
events->focused = false;
rk_backup(*events);
events->key_autorepeat = events->backup_autorepeat;
events->motion_numerator = events->backup_numerator;
events->motion_denominator = events->backup_denominator;
events->motion_threshold = events->backup_threshold;
XSetICFocus(events->input_context);
return reinterpret_cast<rk_events_t>(events);
}
void rk_destroy_events(
rk_display_t _display,
rk_events_t _events) {
rk_display_x11 const * const display = reinterpret_cast<rk_display_x11 const *>(_display);
rk_events_x11 * const events = reinterpret_cast<rk_events_x11 *>(_events);
if (display && events && display->display && display->display == events->display) {
rk_restore(*events);
if (events->input_context) {
XUnsetICFocus(events->input_context);
XDestroyIC(events->input_context);
}
if (events->input_manager) {
XCloseIM(events->input_manager);
}
}
if (events) {
delete events;
}
}
void rk_set_key_autorepeat(
rk_events_t _events,
rk_bool autorepeat) {
rk_keyboard_autorepeat = autorepeat;
if (rk_focused) {
rk_apply_setup();
rk_events_x11 * const events = reinterpret_cast<rk_events_x11 *>(_events);
if (events) {
events->key_autorepeat = autorepeat;
if (events->focused) {
rk_apply(*events);
}
}
}
void rk_set_acceleration(
void rk_set_motion_acceleration(
rk_events_t _events,
rk_uint numerator,
rk_uint denominator,
rk_uint threshold) {
rk_accel_numerator = numerator;
rk_accel_denominator = denominator;
rk_accel_threshold = threshold;
if (rk_focused) {
rk_apply_setup();
rk_events_x11 * const events = reinterpret_cast<rk_events_x11 *>(_events);
if (events) {
events->motion_numerator = numerator;
events->motion_denominator = denominator;
events->motion_threshold = threshold;
if (events->focused) {
rk_apply(*events);
}
}
}
rk_uint rk_consume_events(
rk_event * events,
rk_events_t _events,
rk_event * buffer,
rk_uint max_events) {
static wchar_t string[256];
if (!events || !max_events) {
rk_events_x11 * const events = reinterpret_cast<rk_events_x11 *>(_events);
if (!events || !buffer || !max_events) {
return 0;
}
XEvent x11_event;
KeySym keysym;
Status status;
unsigned nevents = 0;
while (nevents < max_events && XCheckWindowEvent(rk_display, rk_window, rk_events_mask, &x11_event)) {
while (nevents < max_events && XCheckWindowEvent(events->display, events->window, RK_EVENTS_MASK, &x11_event)) {
if (XFilterEvent(&x11_event, 0)) {
continue;
}
rk_event & event = events[nevents];
rk_event & event = buffer[nevents];
switch (x11_event.type) {
case FocusIn:
if (NotifyNormal == x11_event.xfocus.mode) {
rk_focus_in();
rk_focus_in(*events);
event.type = RK_EVENT_FOCUS_IN;
++nevents;
}
@ -159,7 +183,7 @@ rk_uint rk_consume_events(
case FocusOut:
if (NotifyNormal == x11_event.xfocus.mode) {
rk_focus_out();
rk_focus_out(*events);
event.type = RK_EVENT_FOCUS_OUT;
++nevents;
}
@ -168,7 +192,7 @@ rk_uint rk_consume_events(
case KeyPress:
event.type = RK_EVENT_KEY_PRESS;
event.key.code = x11_event.xkey.keycode;
XwcLookupString(rk_input_context, &x11_event.xkey, string, 256, &keysym, &status);
XwcLookupString(events->input_context, &x11_event.xkey, string, 256, &keysym, &status);
switch (status) {
case XLookupChars:
event.key.symbol = XLookupKeysym(&x11_event.xkey, 0);

44
cpp/events/events_x11.hpp Normal file
View File

@ -0,0 +1,44 @@
// 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_EVENTS_X11_H
#define _RK_ENGINE_EVENTS_X11_H
#include <X11/Xlib.h>
enum : unsigned {
RK_EVENTS_MASK = FocusChangeMask |
KeyPressMask | KeyReleaseMask |
ButtonPressMask | ButtonReleaseMask |
PointerMotionMask
};
struct rk_events_x11 {
Display * display;
Window window;
XIM input_manager;
XIC input_context;
bool focused;
bool key_autorepeat;
bool backup_autorepeat;
int motion_numerator;
int backup_numerator;
int motion_denominator;
int backup_denominator;
int motion_threshold;
int backup_threshold;
};
#endif // _RK_ENGINE_EVENTS_X11_H

View File

@ -1,249 +0,0 @@
// 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 "../events.hpp"
#include <cstdio>
#include <cstring>
#include <GLES3/gl32.h>
#include <X11/Xlib.h>
#include <GL/glx.h>
extern unsigned rk_events_mask;
Display * rk_display = nullptr;
Window rk_window = 0;
static Colormap rk_colormap = 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 *);
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_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) {
XSetLocaleModifiers("");
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 = rk_events_mask;
rk_window = XCreateWindow(rk_display, RootWindow(rk_display, vi->screen),
0, 0, width, height, 0, vi->depth, InputOutput, vi->visual,
CWBorderPixel | CWColormap | CWEventMask, &swa);
XFree(vi);
if (!rk_window) {
rk_printf("Failed to create window.");
rk_destroy_context();
return nullptr;
}
XStoreName(rk_display, rk_window, name);
if (!rk_initialize_events()) {
rk_printf("Failed to initialize events.");
rk_destroy_context();
return nullptr;
}
XMapWindow(rk_display, rk_window);
char const * const glx_exts = glXQueryExtensionsString(rk_display, DefaultScreen(rk_display));
glXCreateContextAttribsARBProc const glXCreateContextAttribsARB =
reinterpret_cast<glXCreateContextAttribsARBProc>(
glXGetProcAddressARB(reinterpret_cast<GLubyte const *>("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<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_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_printf("Using extension GL_EXT_multi_draw_indirect::MultiDrawElementsIndirectEXT.");
}
}
}
}
return reinterpret_cast<rk_window_t>(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;
}
rk_terminate_events();
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;
}
}

View File

@ -19,7 +19,6 @@
#include "types.hpp"
#include "math.hpp"
typedef rk_handle_t rk_window_t;
typedef rk_handle_t rk_shader_t;
typedef rk_handle_t rk_input_t;
typedef rk_handle_t rk_param_t;
@ -28,8 +27,6 @@ typedef rk_handle_t rk_triangles_t;
typedef rk_handle_t rk_vertices_t;
typedef rk_handle_t rk_batch_t;
#define RK_FLAG(bit) (1 << (bit))
enum rk_texture_format : rk_uint {
RK_TEXTURE_FORMAT_SRGB8_A8 = 0,
RK_TEXTURE_FORMAT_RGBA8 = 1,
@ -81,10 +78,9 @@ union rk_mesh {
};
};
RK_EXPORT rk_window_t rk_initialize(
char const * name,
rk_uint width,
rk_uint height);
RK_EXPORT void rk_render_initialize();
RK_EXPORT void rk_render_terminate();
RK_EXPORT rk_shader_t rk_load_shader(
rk_uint const vert_nlines,
@ -194,6 +190,4 @@ RK_EXPORT void rk_destroy_texture(
RK_EXPORT void rk_destroy_shader(
rk_shader_t shader);
RK_EXPORT void rk_terminate();
#endif // _RK_ENGINE_RENDER_H

View File

@ -13,17 +13,19 @@
// 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/>.
#include "../render.hpp"
#include "render_opengles.hpp"
#include "../display/display_glx.hpp"
#include <cstdio>
static rk_shader const * rk_current_shader = nullptr;
static rk_vertices const * rk_current_vertices = nullptr;
static void rk_printf(char const * messsage) {
printf("[RK_ENGINE] %s\n", messsage);
static void rk_printf(char const * message) {
printf("[RK] %s\n", message);
}
#define rk_error(message) { if (glGetError() != GL_NO_ERROR) { rk_printf(message); } }
#define rk_gl_error(_message) { if (glGetError() != GL_NO_ERROR) { printf("[GL] %s\n", (_message)); } }
static void rk_debug_message_callback(
GLenum source,
@ -33,36 +35,32 @@ static void rk_debug_message_callback(
GLsizei length,
GLchar const * message,
void const * userParam) {
printf("[RK_ENGINE][GL] (id=%d) %s\n", id, message);
printf("[GL] (id=%d) %s\n", id, message);
}
rk_window_t rk_initialize(
char const * name,
rk_uint width,
rk_uint height) {
rk_window_t const window = rk_create_context(name, width, height);
if (window) {
GLubyte const * const vendor = glGetString(GL_VENDOR);
GLubyte const * const renderer = glGetString(GL_RENDERER);
printf("[RK_ENGINE] vendor: %s, renderer: %s\n", vendor, renderer);
GLubyte const * const version = glGetString(GL_VERSION);
GLubyte const * const language = glGetString(GL_SHADING_LANGUAGE_VERSION);
printf("[RK_ENGINE] version: %s, language: %s\n", version, language);
void rk_render_initialize() {
GLubyte const * const vendor = glGetString(GL_VENDOR);
GLubyte const * const renderer = glGetString(GL_RENDERER);
printf("[RK] vendor: %s, renderer: %s\n", vendor, renderer);
GLubyte const * const version = glGetString(GL_VERSION);
GLubyte const * const language = glGetString(GL_SHADING_LANGUAGE_VERSION);
printf("[RK] version: %s, language: %s\n", version, language);
glDebugMessageCallback(rk_debug_message_callback, nullptr);
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(rk_debug_message_callback, nullptr);
glEnable(GL_DEBUG_OUTPUT);
glDisable(GL_BLEND);
glEnable(GL_DITHER);
glEnable(GL_DEPTH_TEST);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_STENCIL_TEST);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
}
return window;
glDisable(GL_BLEND);
glEnable(GL_DITHER);
glEnable(GL_DEPTH_TEST);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_STENCIL_TEST);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
}
void rk_render_terminate() {
}
static void rk_print_shader_infolog(GLuint shader) {
@ -104,18 +102,18 @@ rk_shader_t rk_load_shader(
rk_printf("Compiling vertex shader...");
glShaderSource(shader->vertex, vert_nlines, vert_lines, nullptr);
glCompileShader(shader->vertex);
rk_error("glCompileShader() failed.");
rk_gl_error("glCompileShader() failed.");
rk_print_shader_infolog(shader->vertex);
rk_printf("Compiling fragment shader...");
glShaderSource(shader->fragment, frag_nlines, frag_lines, nullptr);
glCompileShader(shader->fragment);
rk_error("glCompileShader() failed.");
rk_gl_error("glCompileShader() failed.");
rk_print_shader_infolog(shader->fragment);
rk_printf("Linking program...");
glAttachShader(shader->program, shader->vertex);
glAttachShader(shader->program, shader->fragment);
glLinkProgram(shader->program);
rk_error("glLinkProgram() failed.");
rk_gl_error("glLinkProgram() failed.");
rk_print_program_infolog(shader->program);
rk_printf("Done.");
glReleaseShaderCompiler();
@ -669,7 +667,6 @@ void rk_unselect_shader(
}
void rk_end_frame() {
rk_swap_buffers();
}
void rk_destroy_batch(
@ -730,7 +727,3 @@ void rk_destroy_shader(
delete shader;
}
}
void rk_terminate() {
rk_destroy_context();
}

View File

@ -16,8 +16,7 @@
#ifndef _RK_ENGINE_RENDER_OPENGLES_H
#define _RK_ENGINE_RENDER_OPENGLES_H
#include "../render.hpp"
#include "render_context.hpp"
#include "../types.hpp"
#include <GLES3/gl32.h>
#include <GLES3/gl3ext.h>
#include <GLES3/gl3platform.h>

View File

@ -20,6 +20,7 @@
#include <cstdint>
#define RK_EXPORT extern "C"
#define RK_FLAG(_bit) (1 << (_bit))
typedef bool rk_bool;
typedef char rk_char;