aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Nagel <bill@axzez.com>2022-09-11 22:11:25 -0400
committerBill Nagel <bill@axzez.com>2022-09-11 22:11:25 -0400
commitf97bb10bcfe20ee7fedebb162a4ac66f060e4624 (patch)
treea43002c8b5c209691a52370195feb4b93844a92f
parent4238be9d53f7a817f7ddf1b8d10560142052c6c7 (diff)
Linux EGL and GLES2/3 support
-rw-r--r--sokol_app.h308
1 files changed, 259 insertions, 49 deletions
diff --git a/sokol_app.h b/sokol_app.h
index 201acfc6..eaa2dfe6 100644
--- a/sokol_app.h
+++ b/sokol_app.h
@@ -50,6 +50,9 @@
On Windows, SOKOL_DLL will define SOKOL_APP_API_DECL as __declspec(dllexport)
or __declspec(dllimport) as needed.
+ On Linux, SOKOL_GLCORE33 can use either GLX or EGL.
+ GLX is default, set SOKOL_FORCE_EGL to override.
+
For example code, see https://github.com/floooh/sokol-samples/tree/master/sapp
Portions of the Windows and Linux GL initialization, event-, icon- etc... code
@@ -63,7 +66,8 @@
- on macOS with GL: Cocoa, QuartzCore, OpenGL
- on iOS with Metal: Foundation, UIKit, Metal, MetalKit
- on iOS with GL: Foundation, UIKit, OpenGLES, GLKit
- - on Linux: X11, Xi, Xcursor, GL, dl, pthread, m(?)
+ - on Linux with EGL: X11, Xi, Xcursor, EGL, GL (or GLESv2), dl, pthread, m(?)
+ - on Linux with GLX: X11, Xi, Xcursor, GL, dl, pthread, m(?)
- on Android: GLESv3, EGL, log, android
- on Windows with the MSVC or Clang toolchains: no action needed, libs are defined in-source via pragma-comment-lib
- on Windows with MINGW/MSYS2 gcc: compile with '-mwin32' so that _WIN32 is defined
@@ -89,55 +93,56 @@
- creates a window and 3D-API context/device with a 'default framebuffer'
- makes the rendered frame visible
- provides keyboard-, mouse- and low-level touch-events
- - platforms: MacOS, iOS, HTML5, Win32, Linux, Android (TODO: RaspberryPi)
+ - platforms: MacOS, iOS, HTML5, Win32, Linux/RaspberryPi, Android
- 3D-APIs: Metal, D3D11, GL3.2, GLES2, GLES3, WebGL, WebGL2
FEATURE/PLATFORM MATRIX
=======================
- | Windows | macOS | Linux | iOS | Android | UWP | Raspi | HTML5
- --------------------+---------+-------+-------+-------+---------+------+-------+-------
- gl 3.x | YES | YES | YES | --- | --- | --- | --- | ---
- gles2/webgl | --- | --- | --- | YES | YES | --- | TODO | YES
- gles3/webgl2 | --- | --- | --- | YES | YES | --- | --- | YES
- metal | --- | YES | --- | YES | --- | --- | --- | ---
- d3d11 | YES | --- | --- | --- | --- | YES | --- | ---
- KEY_DOWN | YES | YES | YES | SOME | TODO | YES | TODO | YES
- KEY_UP | YES | YES | YES | SOME | TODO | YES | TODO | YES
- CHAR | YES | YES | YES | YES | TODO | YES | TODO | YES
- MOUSE_DOWN | YES | YES | YES | --- | --- | YES | TODO | YES
- MOUSE_UP | YES | YES | YES | --- | --- | YES | TODO | YES
- MOUSE_SCROLL | YES | YES | YES | --- | --- | YES | TODO | YES
- MOUSE_MOVE | YES | YES | YES | --- | --- | YES | TODO | YES
- MOUSE_ENTER | YES | YES | YES | --- | --- | YES | TODO | YES
- MOUSE_LEAVE | YES | YES | YES | --- | --- | YES | TODO | YES
- TOUCHES_BEGAN | --- | --- | --- | YES | YES | TODO | --- | YES
- TOUCHES_MOVED | --- | --- | --- | YES | YES | TODO | --- | YES
- TOUCHES_ENDED | --- | --- | --- | YES | YES | TODO | --- | YES
- TOUCHES_CANCELLED | --- | --- | --- | YES | YES | TODO | --- | YES
- RESIZED | YES | YES | YES | YES | YES | YES | --- | YES
- ICONIFIED | YES | YES | YES | --- | --- | YES | --- | ---
- RESTORED | YES | YES | YES | --- | --- | YES | --- | ---
- FOCUSED | YES | YES | YES | --- | --- | --- | --- | YES
- UNFOCUSED | YES | YES | YES | --- | --- | --- | --- | YES
- SUSPENDED | --- | --- | --- | YES | YES | YES | --- | TODO
- RESUMED | --- | --- | --- | YES | YES | YES | --- | TODO
- QUIT_REQUESTED | YES | YES | YES | --- | --- | --- | TODO | YES
- IME | TODO | TODO? | TODO | ??? | TODO | --- | ??? | ???
- key repeat flag | YES | YES | YES | --- | --- | YES | TODO | YES
- windowed | YES | YES | YES | --- | --- | YES | TODO | YES
- fullscreen | YES | YES | YES | YES | YES | YES | TODO | ---
- mouse hide | YES | YES | YES | --- | --- | YES | TODO | YES
- mouse lock | YES | YES | YES | --- | --- | TODO | TODO | YES
- set cursor type | YES | YES | YES | --- | --- | YES | TODO | YES
- screen keyboard | --- | --- | --- | YES | TODO | TODO | --- | YES
- swap interval | YES | YES | YES | YES | TODO | --- | TODO | YES
- high-dpi | YES | YES | TODO | YES | YES | YES | TODO | YES
- clipboard | YES | YES | TODO | --- | --- | TODO | --- | YES
- MSAA | YES | YES | YES | YES | YES | TODO | TODO | YES
- drag'n'drop | YES | YES | YES | --- | --- | TODO | TODO | YES
- window icon | YES | YES(1)| YES | --- | --- | TODO | TODO | YES
+ | Windows | macOS | Linux | iOS | Android | UWP | HTML5
+ --------------------+---------+-------+-------+-------+---------+------+-------
+ gl 3.x | YES | YES | YES | --- | --- | --- | ---
+ gles2/webgl | --- | --- | YES(2)| YES | YES | --- | YES
+ gles3/webgl2 | --- | --- | YES(2)| YES | YES | --- | YES
+ metal | --- | YES | --- | YES | --- | --- | ---
+ d3d11 | YES | --- | --- | --- | --- | YES | ---
+ KEY_DOWN | YES | YES | YES | SOME | TODO | YES | YES
+ KEY_UP | YES | YES | YES | SOME | TODO | YES | YES
+ CHAR | YES | YES | YES | YES | TODO | YES | YES
+ MOUSE_DOWN | YES | YES | YES | --- | --- | YES | YES
+ MOUSE_UP | YES | YES | YES | --- | --- | YES | YES
+ MOUSE_SCROLL | YES | YES | YES | --- | --- | YES | YES
+ MOUSE_MOVE | YES | YES | YES | --- | --- | YES | YES
+ MOUSE_ENTER | YES | YES | YES | --- | --- | YES | YES
+ MOUSE_LEAVE | YES | YES | YES | --- | --- | YES | YES
+ TOUCHES_BEGAN | --- | --- | --- | YES | YES | TODO | YES
+ TOUCHES_MOVED | --- | --- | --- | YES | YES | TODO | YES
+ TOUCHES_ENDED | --- | --- | --- | YES | YES | TODO | YES
+ TOUCHES_CANCELLED | --- | --- | --- | YES | YES | TODO | YES
+ RESIZED | YES | YES | YES | YES | YES | YES | YES
+ ICONIFIED | YES | YES | YES | --- | --- | YES | ---
+ RESTORED | YES | YES | YES | --- | --- | YES | ---
+ FOCUSED | YES | YES | YES | --- | --- | --- | YES
+ UNFOCUSED | YES | YES | YES | --- | --- | --- | YES
+ SUSPENDED | --- | --- | --- | YES | YES | YES | TODO
+ RESUMED | --- | --- | --- | YES | YES | YES | TODO
+ QUIT_REQUESTED | YES | YES | YES | --- | --- | --- | YES
+ IME | TODO | TODO? | TODO | ??? | TODO | --- | ???
+ key repeat flag | YES | YES | YES | --- | --- | YES | YES
+ windowed | YES | YES | YES | --- | --- | YES | YES
+ fullscreen | YES | YES | YES | YES | YES | YES | ---
+ mouse hide | YES | YES | YES | --- | --- | YES | YES
+ mouse lock | YES | YES | YES | --- | --- | TODO | YES
+ set cursor type | YES | YES | YES | --- | --- | YES | YES
+ screen keyboard | --- | --- | --- | YES | TODO | TODO | YES
+ swap interval | YES | YES | YES | YES | TODO | --- | YES
+ high-dpi | YES | YES | TODO | YES | YES | YES | YES
+ clipboard | YES | YES | TODO | --- | --- | TODO | YES
+ MSAA | YES | YES | YES | YES | YES | TODO | YES
+ drag'n'drop | YES | YES | YES | --- | --- | TODO | YES
+ window icon | YES | YES(1)| YES | --- | --- | TODO | YES
(1) macOS has no regular window icons, instead the dock icon is changed
+ (2) supported with EGL only (not GLX)
STEP BY STEP
============
@@ -1586,6 +1591,11 @@ SOKOL_APP_API_DECL const char* sapp_get_dropped_file_path(int index);
/* special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub) */
SOKOL_APP_API_DECL void sapp_run(const sapp_desc* desc);
+/* EGL: get EGLDisplay object */
+SOKOL_APP_API_DECL const void* sapp_egl_get_display(void);
+/* EGL: get EGLContext object */
+SOKOL_APP_API_DECL const void* sapp_egl_get_context(void);
+
/* GL: return true when GLES2 fallback is active (to detect fallback from GLES3) */
SOKOL_APP_API_DECL bool sapp_gles2(void);
@@ -1721,8 +1731,12 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
#elif defined(__linux__) || defined(__unix__)
/* Linux */
#define _SAPP_LINUX (1)
- #if !defined(SOKOL_GLCORE33)
- #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33")
+ #if defined(SOKOL_GLCORE33)
+ #if !defined(SOKOL_FORCE_EGL)
+ #define _SAPP_GLX (1)
+ #endif
+ #elif !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2)
+ #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33, SOKOL_GLES3 or SOKOL_GLES2")
#endif
#else
#error "sokol_app.h: Unknown platform"
@@ -1899,6 +1913,9 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
#include <X11/Xcursor/Xcursor.h>
#include <X11/cursorfont.h> /* XC_* font cursors */
#include <X11/Xmd.h> /* CARD32 */
+ #if !defined(_SAPP_GLX)
+ #include <EGL/egl.h>
+ #endif
#include <dlfcn.h> /* dlopen, dlsym, dlclose */
#include <limits.h> /* LONG_MAX */
#include <pthread.h> /* only used a linker-guard, search for _sapp_linux_run() and see first comment */
@@ -2503,6 +2520,8 @@ typedef struct {
_sapp_xdnd_t xdnd;
} _sapp_x11_t;
+#if defined(_SAPP_GLX)
+
typedef struct {
void* libgl;
int major;
@@ -2541,6 +2560,16 @@ typedef struct {
bool ARB_create_context_profile;
} _sapp_glx_t;
+#else
+
+typedef struct {
+ EGLDisplay display;
+ EGLContext context;
+ EGLSurface surface;
+} _sapp_egl_t;
+
+#endif // _SAPP_GLX
+
#endif // _SAPP_LINUX
/*== COMMON DECLARATIONS =====================================================*/
@@ -2643,7 +2672,11 @@ typedef struct {
_sapp_android_t android;
#elif defined(_SAPP_LINUX)
_sapp_x11_t x11;
- _sapp_glx_t glx;
+ #if defined(_SAPP_GLX)
+ _sapp_glx_t glx;
+ #else
+ _sapp_egl_t egl;
+ #endif
#endif
char html5_canvas_selector[_SAPP_MAX_TITLE_LENGTH];
char window_title[_SAPP_MAX_TITLE_LENGTH]; /* UTF-8 */
@@ -10183,6 +10216,8 @@ _SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) {
}
}
+#if defined(_SAPP_GLX)
+
_SOKOL_PRIVATE bool _sapp_glx_has_ext(const char* ext, const char* extensions) {
SOKOL_ASSERT(ext);
const char* start = extensions;
@@ -10442,6 +10477,8 @@ _SOKOL_PRIVATE void _sapp_glx_swapinterval(int interval) {
}
}
+#endif /* _SAPP_GLX */
+
_SOKOL_PRIVATE void _sapp_x11_send_event(Atom type, int a, int b, int c, int d, int e) {
XEvent event;
_sapp_clear(&event, sizeof(event));
@@ -11463,6 +11500,146 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) {
}
}
+#if !defined(_SAPP_GLX)
+
+_SOKOL_PRIVATE void _sapp_egl_init() {
+#if defined(SOKOL_GLCORE33)
+ if (!eglBindAPI(EGL_OPENGL_API)) {
+ _sapp_fail("EGL: failed to bind API");
+ }
+#else
+ if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+ _sapp_fail("EGL: failed to bind API");
+ }
+#endif
+
+ _sapp.egl.display = eglGetDisplay((EGLNativeDisplayType)_sapp.x11.display);
+ if (EGL_NO_DISPLAY == _sapp.egl.display) {
+ _sapp_fail("EGL: failed to get display");
+ }
+
+ EGLint major, minor;
+ if (!eglInitialize(_sapp.egl.display, &major, &minor)) {
+ _sapp_fail("EGL: failed to initialize");
+ }
+
+ EGLint sample_count = _sapp.desc.sample_count > 1 ? _sapp.desc.sample_count : 0;
+ EGLint alpha_size = _sapp.desc.alpha ? 8 : 0;
+ const EGLint config_attrs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ #if defined(SOKOL_GLCORE33)
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
+ #elif defined(SOKOL_GLES3)
+ EGL_RENDERABLE_TYPE, _sapp.desc.gl_force_gles2 ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES3_BIT,
+ #else
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ #endif
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, alpha_size,
+ EGL_DEPTH_SIZE, 24,
+ EGL_STENCIL_SIZE, 8,
+ EGL_SAMPLE_BUFFERS, _sapp.desc.sample_count > 1 ? 1 : 0,
+ EGL_SAMPLES, sample_count,
+ EGL_NONE,
+ };
+
+ EGLConfig egl_configs[32];
+ EGLint config_count;
+ if (!eglChooseConfig(_sapp.egl.display, config_attrs, egl_configs, 32, &config_count) || config_count == 0) {
+ _sapp_fail("EGL: no available configs");
+ }
+
+ EGLConfig config = egl_configs[0];
+ for (int i = 0; i < config_count; ++i) {
+ EGLConfig c = egl_configs[i];
+ EGLint r, g, b, a, d, s, n;
+ if (eglGetConfigAttrib(_sapp.egl.display, c, EGL_RED_SIZE, &r) &&
+ eglGetConfigAttrib(_sapp.egl.display, c, EGL_GREEN_SIZE, &g) &&
+ eglGetConfigAttrib(_sapp.egl.display, c, EGL_BLUE_SIZE, &b) &&
+ eglGetConfigAttrib(_sapp.egl.display, c, EGL_ALPHA_SIZE, &a) &&
+ eglGetConfigAttrib(_sapp.egl.display, c, EGL_DEPTH_SIZE, &d) &&
+ eglGetConfigAttrib(_sapp.egl.display, c, EGL_STENCIL_SIZE, &s) &&
+ eglGetConfigAttrib(_sapp.egl.display, c, EGL_SAMPLES, &s) &&
+ r == 8 && g == 8 && b == 8 && a == alpha_size && d == 24 && s == 8 && n == sample_count) {
+ config = c;
+ break;
+ }
+ }
+
+ EGLint visual_id;
+ if (!eglGetConfigAttrib(_sapp.egl.display, config, EGL_NATIVE_VISUAL_ID, &visual_id)) {
+ _sapp_fail("EGL: failed to get native visual");
+ }
+
+ XVisualInfo visual_info_template;
+ visual_info_template.visualid = visual_id;
+
+ int num_visuals;
+ XVisualInfo* visual_info = XGetVisualInfo(_sapp.x11.display, VisualIDMask, &visual_info_template, &num_visuals);
+ if (!visual_info) {
+ _sapp_fail("EGL: failed to get x11 visual");
+ }
+
+ _sapp_x11_create_window(visual_info->visual, visual_info->depth);
+ XFree(visual_info);
+
+ _sapp.egl.surface = eglCreateWindowSurface(_sapp.egl.display, config, (EGLNativeWindowType)_sapp.x11.window, NULL);
+ if (EGL_NO_SURFACE == _sapp.egl.surface) {
+ _sapp_fail("EGL: failed to create EGL surface");
+ }
+
+ EGLint ctx_attrs[] = {
+ #if defined(SOKOL_GLCORE33)
+ EGL_CONTEXT_MAJOR_VERSION, _sapp.desc.gl_major_version,
+ EGL_CONTEXT_MINOR_VERSION, _sapp.desc.gl_minor_version,
+ EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
+ #elif defined(SOKOL_GLES3)
+ EGL_CONTEXT_CLIENT_VERSION, _sapp.desc.gl_force_gles2 ? 2 : 3,
+ #else
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ #endif
+ EGL_NONE,
+ };
+
+ _sapp.egl.context = eglCreateContext(_sapp.egl.display, config, EGL_NO_CONTEXT, ctx_attrs);
+ if (EGL_NO_CONTEXT == _sapp.egl.context) {
+ _sapp_fail("EGL: failed to create GL context");
+ }
+
+ if (!eglMakeCurrent(_sapp.egl.display, _sapp.egl.surface, _sapp.egl.surface, _sapp.egl.context)) {
+ _sapp_fail("EGL: failed to set current context");
+ }
+
+ eglSwapInterval(_sapp.egl.display, _sapp.swap_interval);
+
+#if defined(SOKOL_GLES3)
+ _sapp.gles2_fallback = _sapp.desc.gl_force_gles2;
+#endif
+}
+
+_SOKOL_PRIVATE void _sapp_egl_destroy(void) {
+ if (_sapp.egl.display != EGL_NO_DISPLAY) {
+ eglMakeCurrent(_sapp.egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+ if (_sapp.egl.context != EGL_NO_CONTEXT) {
+ eglDestroyContext(_sapp.egl.display, _sapp.egl.context);
+ _sapp.egl.context = EGL_NO_CONTEXT;
+ }
+
+ if (_sapp.egl.surface != EGL_NO_SURFACE) {
+ eglDestroySurface(_sapp.egl.display, _sapp.egl.surface);
+ _sapp.egl.surface = EGL_NO_SURFACE;
+ }
+
+ eglTerminate(_sapp.egl.display);
+ _sapp.egl.display = EGL_NO_DISPLAY;
+ }
+}
+
+#endif /* _SAPP_GLX */
+
_SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) {
/* The following lines are here to trigger a linker error instead of an
obscure runtime error if the user has forgotten to add -pthread to
@@ -11488,24 +11665,27 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) {
_sapp.dpi_scale = _sapp.x11.dpi / 96.0f;
_sapp_x11_init_extensions();
_sapp_x11_create_cursors();
+#if defined(_SAPP_GLX)
_sapp_glx_init();
Visual* visual = 0;
int depth = 0;
_sapp_glx_choose_visual(&visual, &depth);
_sapp_x11_create_window(visual, depth);
_sapp_glx_create_context();
+ _sapp_glx_swapinterval(_sapp.swap_interval);
+#else
+ _sapp_egl_init();
+#endif
sapp_set_icon(&desc->icon);
_sapp.valid = true;
_sapp_x11_show_window();
if (_sapp.fullscreen) {
_sapp_x11_set_fullscreen(true);
}
- _sapp_glx_swapinterval(_sapp.swap_interval);
XFlush(_sapp.x11.display);
while (!_sapp.quit_ordered) {
_sapp_timing_measure(&_sapp.timing);
- _sapp_glx_make_current();
int count = XPending(_sapp.x11.display);
while (count--) {
XEvent event;
@@ -11513,7 +11693,11 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) {
_sapp_x11_process_event(&event);
}
_sapp_frame();
+#if defined(_SAPP_GLX)
_sapp_glx_swap_buffers();
+#else
+ eglSwapBuffers(_sapp.egl.display, _sapp.egl.surface);
+#endif
XFlush(_sapp.x11.display);
/* handle quit-requested, either from window or from sapp_request_quit() */
if (_sapp.quit_requested && !_sapp.quit_ordered) {
@@ -11526,7 +11710,11 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) {
}
}
_sapp_call_cleanup();
+#if defined(_SAPP_GLX)
_sapp_glx_destroy_context();
+#else
+ _sapp_egl_destroy();
+#endif
_sapp_x11_destroy_window();
_sapp_x11_destroy_cursors();
XCloseDisplay(_sapp.x11.display);
@@ -11649,6 +11837,28 @@ SOKOL_API_IMPL float sapp_dpi_scale(void) {
return _sapp.dpi_scale;
}
+SOKOL_APP_IMPL const void* sapp_egl_get_display(void) {
+ SOKOL_ASSERT(_sapp.valid);
+ #if defined(_SAPP_ANDROID)
+ return _sapp.android.display;
+ #elif defined(_SAPP_LINUX) && !defined(_SAPP_GLX)
+ return _sapp.egl.display;
+ #else
+ return 0;
+ #endif
+}
+
+SOKOL_APP_IMPL const void* sapp_egl_get_context(void) {
+ SOKOL_ASSERT(_sapp.valid);
+ #if defined(_SAPP_ANDROID)
+ return _sapp.android.context;
+ #elif defined(_SAPP_LINUX) && !defined(_SAPP_GLX)
+ return _sapp.egl.context;
+ #else
+ return 0;
+ #endif
+}
+
SOKOL_API_IMPL bool sapp_gles2(void) {
return _sapp.gles2_fallback;
}