Load GL through sigil-desktop and drop libGL, sokol_app.h and sokol_glue
sigil-app is sigil-desktop 0.10 (GLFW; native Wayland or X11). This package now depends on it as name: "sigil-desktop" at the old URL (sigil-build derives a dependency's name from the URL until the Codeberg repository is renamed) and stops linking GL: sokol_gfx runs with SOKOL_EXTERNAL_GL_LOADER on every native target, and src/c/gl_native_loader.h, generated from the vendored sokol_gfx.h by scripts/gen-gl-loader.sh (sokol's own list of 122 entry points, the GL types and constants, the header guards), fills sokol's GL function pointers through sigil_desktop_gl_get_proc_address in gfx-setup. Linux link-flags are empty (were -lGL); darwin keeps OpenGL.framework and drops Metal and MetalKit, which nothing used.
graphics.c builds the environment and swapchain from sigil_desktop_framebuffer_size with the values sokol_app's GL backend reported, so sokol_glue.h and the sokol_app.h declaration include go; sokol_log's implementation moves here. gfx-setup raises a runtime error instead of crashing when there is no GL context.
test-shader-limits moves to (sigil desktop) and gains verify-wl.mjs, the same assertions captured through a headless sway. Measured: GREEN on Xvfb and on sway, both captures 0 pixels different from the sokol_app baseline; the binary's NEEDED list is libm, libc, ld-linux; under guix shell --pure -m guix/run-wayland.scm (mesa, wayland, libxkbcommon) it runs as a Wayland client with no LD_LIBRARY_PATH. manifest.scm lists the GLFW compile-time headers and drops libglvnd, whose vendorless libGLX.so.0 breaks GLFW's GLX on Guix. Version 0.11.4.
CHANGELOG.md | 38 +
README.md | 10 +-
dev-redirects.sgl | 6 +-
manifest.scm | 32 +-
package.sgl | 37 +-
scripts/gen-gl-loader.sh | 138 ++
src/c/gl_native_loader.h | 451 +++++
src/c/graphics.c | 77 +-
src/c/sokol-graphics.c | 62 +-
test/test-shader-limits/README.md | 17 +
test/test-shader-limits/dev-redirects.sgl | 9 +-
test/test-shader-limits/package.sgl | 4 +-
test/test-shader-limits/src/test-shader-limits/shell-native.sgl | 4 +-
test/test-shader-limits/verify-wl.mjs | 446 ++++
vendor/sokol/sokol_app.h | 14047 ------------------------------------------------------------------------------------------------------------------------------
vendor/sokol/sokol_glue.h | 206 --
16 files changed, 1250 insertions(+), 14334 deletions(-)CHANGELOG.mdmodified
The format is based on [Keep a Changelog](https://keepachangelog.com/),and this project adheres to [Semantic Versioning](https://semver.org/).## [0.11.4] - unreleased### Changed- The window package is **sigil-desktop 0.10** (sigil-app renamed and rebuilt on GLFW: native Wayland or X11 on Linux, picked at startup). The dependency is `(from-git url: "codeberg:sigil/sigil-app" name: "sigil-desktop" version: "^0.10.0")` until the repository rename lands; the Sigil API of this package is unchanged.- **No libGL link, no GL headers.** sokol_gfx runs with `SOKOL_EXTERNAL_GL_LOADER` on every native target: `src/c/gl_native_loader.h` (generated from the vendored `sokol_gfx.h` by `scripts/gen-gl-loader.sh`, 122 entry points) fills sokol's GL function pointers through `sigil_desktop_gl_get_proc_address` in `gfx-setup`. Linux `link-flags` are empty (were `-lGL`); Windows unchanged (none); darwin keeps `OpenGL.framework` and drops `Metal` and `MetalKit`, which nothing used. A game's NEEDED list is now libm, libc and the loader (plus whatever sigil-audio links).- `sokol_glue.h` and the `sokol_app.h` declaration include are gone. The native environment and swapchain are built in `graphics.c` from `sigil_desktop_framebuffer_size` with the values sokol_app's GL backend reported (RGBA8, depth-stencil, no MSAA, framebuffer 0). `sokol_log`'s implementation moved into this package.- `gfx-setup` raises `gfx-setup: no OpenGL context` when called outside `app-run` / `run-game` instead of crashing inside sokol.- `manifest.scm` lists the GLFW compile-time headers and drops `libglvnd` (Guix's mesa is built without it, and a vendorless `libGLX.so.0` on the library path breaks GLX at run time).### Requires- Sigil 0.20+ and sigil-desktop 0.10.0+.### Upgrade notes- Relock, delete `build/` once (a stale `sokol-app.o` otherwise stays in the consumer's `libsigil-app.a` archive), rebuild.## [0.11.0] - 2026-06-25### ChangedREADME.mdmodified
## System Prerequisites- OpenGL development headers (`libgl-dev` / `mesa-dev` on Linux, included with Xcode on macOS)- No GL headers: the GL entry points sokol_gfx calls are loaded at run time through sigil-desktop (`src/c/gl_native_loader.h`, generated from the vendored `sokol_gfx.h` by `scripts/gen-gl-loader.sh`), and nothing links libGL. Building sigil-desktop needs the X11 and Wayland headers listed in `manifest.scm`.## Dependencies- sigil-stdlib- sigil-app (sokol_app windowing)- sigil-desktop (the window, GL context and input; GLFW. Named sigil-app through 0.9.x, pinned here as `name: "sigil-desktop"` at the old URL until the Codeberg repository is renamed)## Builddev-redirects.sglmodified
;; Development redirects — point dependencies at local checkouts;; Development redirects — point dependencies at local checkouts.;; sigil-desktop was named sigil-app through 0.9.x; the Codeberg repo is;; being renamed in place, so its URL stays until then.(redirects repos: (list (for-repo use: (from-path dir: "../sigil")) (for-repo url: "codeberg:sigil/sigil-app" use: (from-path dir: "../sigil-app")))) use: (from-path dir: "../sigil-desktop"))))manifest.scmmodified
;; sigil-graphics - Development Environment;; Use with: guix shell -m manifest.scm;;;; Mirrors cinder-cantata's runtime surface (mesa, X11 stack, alsa,;; ogg/vorbis) so the smoke tests under test/ launch cleanly under;; sokol_app on Guix. libglvnd is what supplies <GL/gl.h> on modern;; Guix; mesa alone is not enough for headers, only for the runtime;; libGL.;; Build side: the X11 and Wayland headers sigil-desktop's vendored GLFW;; compiles against (it links none of them). No GL headers: the GL entry;; points come from src/c/gl_native_loader.h and are loaded at run time.;;;; Run side, for the smoke tests under test/: mesa (libGL, libEGL and the;; llvmpipe driver GLFW loads at run time), the X libraries GLFW dlopens;; under Xvfb, wayland + libxkbcommon for a Wayland compositor. libglvnd;; must NOT be here: Guix's mesa is built without it, and a vendorless;; libGLX.so.0 on the library path makes GLFW's GLX fail ("No GLXFBConfigs;; returned"). LD_LIBRARY_PATH=$GUIX_ENVIRONMENT/lib is still needed for;; libasound until sigil-audio stops linking it; the rest resolve through;; sigil-desktop's dlopen fallback ($GUIX_ENVIRONMENT/lib).(specifications->manifest '("gcc-toolchain" "pkg-config" "binutils" ;; Graphics (sokol_gfx + sokol_app on Linux) "mesa" ; OpenGL implementation (libGL) "libglvnd" ; OpenGL dispatcher; provides GL/gl.h on modern Guix "libx11" "libxi" "libxcursor" ;; GLFW compile-time headers (sigil-desktop) "libx11" "libxcursor" "libxrandr" "libxinerama" "libxi" "libxext" "xorgproto" "wayland" "libxkbcommon" ;; Run time: GL and the platform libraries GLFW loads "mesa" "libxrender" ;; Audio (sokol_audio runtime — sigil-app pulls it in transitively) ;; Audio (sokol_audio runtime — pulled in by test apps that use sigil-audio) "alsa-lib" ;; OGG Vorbis (carried for parity with cinder's manifest, sincepackage.sglmodified
(package name: "sigil-graphics" version: "0.11.3" version: "0.11.4" sigil: "0.20" description: "2D graphics, image loading, and font rendering for Sigil" url: "https://codeberg.org/sigil/sigil-graphics" ;; Web (wasm32-wasi): the first-class graphics-on-web build. sokol_gfx's ;; GLES3 backend over the sigil-wasm-gles3 raw-WebGL2 binding, no Emscripten, ;; no sokol_app (the app shell comes from the wasm bridge). sigil-app is ;; no window package (the app shell comes from the wasm bridge). sigil-desktop is ;; excluded from this config's deps (config-conditional gating); the sokol ;; backend is self-selected in sokol-graphics.c via __wasm__. (config features: '(web wasm graphics))) dependencies: (list ;; sigil-app provides the native sokol_app window/loop + slog_func. It is a ;; NATIVE-ONLY dep: its C (SOKOL_GLCORE + X11/Cocoa) cannot build for ;; wasm32-wasi, and on web sigil-graphics needs nothing from it (the app ;; shell + slog_func come from the wasm path). Config-conditional gating ;; excludes it from the `web` build. (from-git url: "codeberg:sigil/sigil-app" version: "^0.9.0" ;; sigil-desktop (the package named sigil-app through 0.9.x; the repo is ;; being renamed in place on Codeberg, the old URL redirects) provides ;; the window and GL context (GLFW) and the C surface this package ;; builds its swapchain and GL loader on (include/sigil-desktop.h). It ;; is a NATIVE-ONLY dep: its C cannot build for wasm32-wasi, and on web ;; sigil-graphics needs nothing from it (the app shell comes from the ;; wasm path). Config-conditional gating excludes it from `web`. ;; name: is needed while the URL still says sigil-app: sigil-build derives ;; the dependency name from the URL and looks the package up by it. (from-git url: "codeberg:sigil/sigil-app" name: "sigil-desktop" version: "^0.10.0" configs: '(dev release windows-amd64)) ;; Web-only: the raw-GLES3 WebGL2 binding. sigil-graphics' web C ;; (sokol-graphics.c under __wasm__) includes gl_shim.h — propagated here via "src/c/graphics.c" "src/c/image.c" "src/c/font.c") c-include-dirs: '("vendor/sokol" "vendor/stb") native-init: "sigil__init_sigil_graphics_module" ;; Platform-specific linker flags for OpenGL. ;; Windows: sokol_gfx's GL backend loads opengl32.dll dynamically via ;; LoadLibraryA + GetProcAddress (kernel32 — linked via sigil-app), so no ;; extra link libs are needed beyond what sigil-app provides. ;; Platform linker flags. ;; ;; Linux and Windows: none. GL entry points are loaded through ;; sigil-desktop's proc loader (GLFW's glfwGetProcAddress) into ;; gl_native_loader.h's pointers, and GLFW dlopens libGL / libEGL / ;; opengl32.dll itself, so nothing GL-related is linked (was -lGL on ;; Linux). Darwin: OpenGL.framework stays so GLFW's NSGL finds the ;; bundle loaded (CFBundleGetBundleWithIdentifier); Metal and MetalKit ;; are gone, nothing here is Metal (no macOS build has run yet). ;; ;; Procedural (lambda ctx) form so cross-compile configs evaluate ;; the right branch at link time. (case (host-os) ...) was evaluated ;; cross builds. link-flags: (lambda (ctx) (case (target-os ctx) ((linux) '("-lGL")) ((darwin) '("-framework" "OpenGL" "-framework" "Metal" "-framework" "MetalKit")) ((linux) '()) ((darwin) '("-framework" "OpenGL")) ((windows) '()) (else '())))))scripts/gen-gl-loader.shadded
#!/bin/sh# gen-gl-loader.sh - regenerate src/c/gl_native_loader.h from vendor/sokol/sokol_gfx.h## sokol_gfx's GL backend either includes the system GL headers (and links# libGL) or, on Win32, carries its own loader: GL types, constants and an# X-macro list of every GL function it calls. This script lifts those two# blocks out of the vendored header and emits a loader that fills the same# function pointers through a caller-supplied proc loader# (sigil_desktop_gl_get_proc_address, i.e. glfwGetProcAddress). The result# is what SOKOL_EXTERNAL_GL_LOADER expects on every native target, so no# GL header is needed to build and no libGL is linked.## Run after every sokol_gfx.h bump; the counts below refuse a header whose# shape moved. Anchors are patterns, and each extraction is guarded by a# line count so a silent no-match cannot produce an empty header.set -eucd "$(dirname "$0")/.."SRC=vendor/sokol/sokol_gfx.hOUT=src/c/gl_native_loader.hT=$(mktemp -d); trap 'rm -rf "$T"' EXIT# 1. the types + constants block: from the first GL typedef after# "#define GL_APIENTRY APIENTRY" to the "#endif" that closes the# win32-loader-definitions blockawk ' /#define GL_APIENTRY APIENTRY/ { armed = 1; next } armed && !started && /typedef unsigned int GLenum;/ { started = 1 } started && /^ #endif/ { exit } started { sub(/^ /, ""); print }' "$SRC" > "$T/defs.h"N1=$(wc -l < "$T/defs.h")[ "$N1" -gt 200 ] && [ "$N1" -lt 400 ] || { echo "REFUSING: types+constants block is $N1 lines (expected 200..400)"; exit 1; }grep -q '^typedef unsigned int GLenum;' "$T/defs.h" || { echo "REFUSING: no GLenum typedef"; exit 1; }grep -q '^#define GL_RGBA16 ' "$T/defs.h" || { echo "REFUSING: constants block does not end where expected"; exit 1; }# 2. the X-macro list: the body of _SG_GL_FUNCS, minus the SG_GL_FUNCS_EXT hookawk ' /^#define _SG_GL_FUNCS \\$/ { started = 1; next } started && /SG_GL_FUNCS_EXT/ { next } started { print; if ($0 !~ /\\$/) exit }' "$SRC" | sed 's/_SG_XMACRO(/SIGIL_GL_XMACRO(/' > "$T/funcs.h"N2=$(grep -c 'SIGIL_GL_XMACRO(' "$T/funcs.h")[ "$N2" -gt 100 ] && [ "$N2" -lt 200 ] || { echo "REFUSING: function list has $N2 entries (expected 100..200)"; exit 1; }grep -q 'SIGIL_GL_XMACRO(glGenBuffers,' "$T/funcs.h" || { echo "REFUSING: glGenBuffers not in the function list"; exit 1; }# the last line must not end in a backslash (the macro closes there)tail -1 "$T/funcs.h" | grep -q '\\$' && { echo "REFUSING: function list did not close"; exit 1; }{cat <<HDR/* * gl_native_loader.h - GL types, constants and entry points for sokol_gfx * without a system GL header or a libGL link. * * GENERATED by scripts/gen-gl-loader.sh from vendor/sokol/sokol_gfx.h * (sokol's own Win32 loader block); do not edit, regenerate after a sokol * bump. Included once, by sokol-graphics.c, ahead of sokol_gfx.h with * SOKOL_EXTERNAL_GL_LOADER defined. Every gl* sokol_gfx calls is a static * function pointer here, filled by sigil_gl_load() through a proc loader * (sigil_desktop_gl_get_proc_address, GLFW's glfwGetProcAddress) once the * window's GL context is current. $N2 functions, $N1 lines of definitions. */#ifndef SIGIL_GL_NATIVE_LOADER_H#define SIGIL_GL_NATIVE_LOADER_H#include <stddef.h>#include <stdint.h>#if defined(_WIN32) && !defined(_WIN64)#define GL_APIENTRY __stdcall#else#define GL_APIENTRY#endif/* The guards the system GL headers use, so a later include of any of them * (glfw3.h without GLFW_INCLUDE_NONE, say) defines nothing twice. */#define __gl_h_ 1#define __gl32_h_ 1#define __gl31_h_ 1#define __GL_H__ 1#define __glext_h_ 1#define __GLEXT_H_ 1#define __gltypes_h_ 1#define __glcorearb_h_ 1#define __gl_glcorearb_h_ 1/* sokol_gfx enables its 3.2 / 4.2 / 4.3 code paths on these, as the system * headers (Mesa's glext.h) defined them before; the runtime check on the * context's reported version still gates their use. */#define GL_VERSION_3_2 1#define GL_VERSION_4_2 1#define GL_VERSION_4_3 1/* ---- types and constants (from sokol_gfx.h) ---- */HDRcat "$T/defs.h"cat <<MID/* ---- the entry points sokol_gfx calls (from sokol_gfx.h) ---- */#define SIGIL_GL_FUNCS \\MIDcat "$T/funcs.h"cat <<'FTR'#define SIGIL_GL_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args;SIGIL_GL_FUNCS#undef SIGIL_GL_XMACRO#define SIGIL_GL_XMACRO(name, ret, args) static PFN_ ## name name;SIGIL_GL_FUNCS#undef SIGIL_GL_XMACROtypedef void *(*sigil_gl_proc_loader)(const char *name);/* Fill every pointer through `load`. Returns the number left NULL: entry * points newer than the context (glTextureView on a 4.1 context, the * base-instance draws before 4.2) are expected to be missing and sokol_gfx * only calls them behind its feature checks; glGenBuffers or glGetIntegerv * missing means there is no usable context, and sigil_gl_ready() says so. */static int sigil_gl_load(sigil_gl_proc_loader load){ int missing = 0;#define SIGIL_GL_XMACRO(name, ret, args) \ name = (PFN_ ## name)load(#name); if (!name) missing++; SIGIL_GL_FUNCS#undef SIGIL_GL_XMACRO return missing;}static int sigil_gl_ready(void){ return glGetIntegerv != NULL && glClearBufferfv != NULL && glGenBuffers != NULL && glCreateShader != NULL && glBindFramebuffer != NULL;}#endif /* SIGIL_GL_NATIVE_LOADER_H */FTR} > "$OUT"echo "wrote $OUT: $N1 definition lines, $N2 functions, $(wc -l < "$OUT") lines total"src/c/gl_native_loader.hadded
/* * gl_native_loader.h - GL types, constants and entry points for sokol_gfx * without a system GL header or a libGL link. * * GENERATED by scripts/gen-gl-loader.sh from vendor/sokol/sokol_gfx.h * (sokol's own Win32 loader block); do not edit, regenerate after a sokol * bump. Included once, by sokol-graphics.c, ahead of sokol_gfx.h with * SOKOL_EXTERNAL_GL_LOADER defined. Every gl* sokol_gfx calls is a static * function pointer here, filled by sigil_gl_load() through a proc loader * (sigil_desktop_gl_get_proc_address, GLFW's glfwGetProcAddress) once the * window's GL context is current. 122 functions, 249 lines of definitions. */#ifndef SIGIL_GL_NATIVE_LOADER_H#define SIGIL_GL_NATIVE_LOADER_H#include <stddef.h>#include <stdint.h>#if defined(_WIN32) && !defined(_WIN64)#define GL_APIENTRY __stdcall#else#define GL_APIENTRY#endif/* The guards the system GL headers use, so a later include of any of them * (glfw3.h without GLFW_INCLUDE_NONE, say) defines nothing twice. */#define __gl_h_ 1#define __gl32_h_ 1#define __gl31_h_ 1#define __GL_H__ 1#define __glext_h_ 1#define __GLEXT_H_ 1#define __gltypes_h_ 1#define __glcorearb_h_ 1#define __gl_glcorearb_h_ 1/* sokol_gfx enables its 3.2 / 4.2 / 4.3 code paths on these, as the system * headers (Mesa's glext.h) defined them before; the runtime check on the * context's reported version still gates their use. */#define GL_VERSION_3_2 1#define GL_VERSION_4_2 1#define GL_VERSION_4_3 1/* ---- types and constants (from sokol_gfx.h) ---- */typedef unsigned int GLenum;typedef unsigned int GLuint;typedef int GLsizei;typedef char GLchar;typedef ptrdiff_t GLintptr;typedef ptrdiff_t GLsizeiptr;typedef double GLclampd;typedef unsigned short GLushort;typedef unsigned char GLubyte;typedef unsigned char GLboolean;typedef uint64_t GLuint64;typedef double GLdouble;typedef unsigned short GLhalf;typedef float GLclampf;typedef unsigned int GLbitfield;typedef signed char GLbyte;typedef short GLshort;typedef void GLvoid;typedef int64_t GLint64;typedef float GLfloat;typedef int GLint;#define GL_INT_2_10_10_10_REV 0x8D9F#define GL_R32F 0x822E#define GL_PROGRAM_POINT_SIZE 0x8642#define GL_DEPTH_ATTACHMENT 0x8D00#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A#define GL_COLOR_ATTACHMENT0 0x8CE0#define GL_R16F 0x822D#define GL_DRAW_FRAMEBUFFER 0x8CA9#define GL_FRAMEBUFFER_COMPLETE 0x8CD5#define GL_NUM_EXTENSIONS 0x821D#define GL_INFO_LOG_LENGTH 0x8B84#define GL_VERTEX_SHADER 0x8B31#define GL_INCR 0x1E02#define GL_DYNAMIC_DRAW 0x88E8#define GL_STATIC_DRAW 0x88E4#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519#define GL_TEXTURE_CUBE_MAP 0x8513#define GL_FUNC_SUBTRACT 0x800A#define GL_FUNC_REVERSE_SUBTRACT 0x800B#define GL_CONSTANT_COLOR 0x8001#define GL_DECR_WRAP 0x8508#define GL_R8 0x8229#define GL_LINEAR_MIPMAP_LINEAR 0x2703#define GL_ELEMENT_ARRAY_BUFFER 0x8893#define GL_SHORT 0x1402#define GL_DEPTH_TEST 0x0B71#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518#define GL_LINK_STATUS 0x8B82#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E#define GL_RGBA16F 0x881A#define GL_CONSTANT_ALPHA 0x8003#define GL_READ_FRAMEBUFFER 0x8CA8#define GL_TEXTURE0 0x84C0#define GL_TEXTURE_MIN_LOD 0x813A#define GL_CLAMP_TO_EDGE 0x812F#define GL_UNSIGNED_SHORT_5_6_5 0x8363#define GL_TEXTURE_WRAP_R 0x8072#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034#define GL_NEAREST_MIPMAP_NEAREST 0x2700#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033#define GL_SRC_ALPHA_SATURATE 0x0308#define GL_STREAM_DRAW 0x88E0#define GL_ONE 1#define GL_NEAREST_MIPMAP_LINEAR 0x2702#define GL_RGB10_A2 0x8059#define GL_RGBA8 0x8058#define GL_SRGB8_ALPHA8 0x8C43#define GL_RGBA4 0x8056#define GL_RGB8 0x8051#define GL_ARRAY_BUFFER 0x8892#define GL_STENCIL 0x1802#define GL_TEXTURE_2D 0x0DE1#define GL_DEPTH 0x1801#define GL_FRONT 0x0404#define GL_STENCIL_BUFFER_BIT 0x00000400#define GL_REPEAT 0x2901#define GL_RGBA 0x1908#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515#define GL_DECR 0x1E03#define GL_FRAGMENT_SHADER 0x8B30#define GL_COMPUTE_SHADER 0x91B9#define GL_FLOAT 0x1406#define GL_TEXTURE_MAX_LOD 0x813B#define GL_DEPTH_COMPONENT 0x1902#define GL_ONE_MINUS_DST_ALPHA 0x0305#define GL_COLOR 0x1800#define GL_TEXTURE_2D_ARRAY 0x8C1A#define GL_TRIANGLES 0x0004#define GL_UNSIGNED_BYTE 0x1401#define GL_TEXTURE_MAG_FILTER 0x2800#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004#define GL_NONE 0#define GL_SRC_COLOR 0x0300#define GL_BYTE 0x1400#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A#define GL_LINE_STRIP 0x0003#define GL_TEXTURE_3D 0x806F#define GL_CW 0x0900#define GL_LINEAR 0x2601#define GL_RENDERBUFFER 0x8D41#define GL_GEQUAL 0x0206#define GL_COLOR_BUFFER_BIT 0x00004000#define GL_RGBA32F 0x8814#define GL_BLEND 0x0BE2#define GL_ONE_MINUS_SRC_ALPHA 0x0303#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002#define GL_TEXTURE_WRAP_T 0x2803#define GL_TEXTURE_WRAP_S 0x2802#define GL_TEXTURE_MIN_FILTER 0x2801#define GL_LINEAR_MIPMAP_NEAREST 0x2701#define GL_EXTENSIONS 0x1F03#define GL_NO_ERROR 0#define GL_REPLACE 0x1E01#define GL_KEEP 0x1E00#define GL_CCW 0x0901#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516#define GL_RGB 0x1907#define GL_TRIANGLE_STRIP 0x0005#define GL_FALSE 0#define GL_ZERO 0#define GL_CULL_FACE 0x0B44#define GL_INVERT 0x150A#define GL_INT 0x1404#define GL_UNSIGNED_INT 0x1405#define GL_UNSIGNED_SHORT 0x1403#define GL_NEAREST 0x2600#define GL_SCISSOR_TEST 0x0C11#define GL_LEQUAL 0x0203#define GL_STENCIL_TEST 0x0B90#define GL_DITHER 0x0BD0#define GL_DEPTH_COMPONENT32F 0x8CAC#define GL_EQUAL 0x0202#define GL_FRAMEBUFFER 0x8D40#define GL_RGB5 0x8050#define GL_LINES 0x0001#define GL_DEPTH_BUFFER_BIT 0x00000100#define GL_SRC_ALPHA 0x0302#define GL_INCR_WRAP 0x8507#define GL_LESS 0x0201#define GL_MULTISAMPLE 0x809D#define GL_FRAMEBUFFER_BINDING 0x8CA6#define GL_BACK 0x0405#define GL_ALWAYS 0x0207#define GL_FUNC_ADD 0x8006#define GL_ONE_MINUS_DST_COLOR 0x0307#define GL_NOTEQUAL 0x0205#define GL_DST_COLOR 0x0306#define GL_COMPILE_STATUS 0x8B81#define GL_RED 0x1903#define GL_DST_ALPHA 0x0304#define GL_RGB5_A1 0x8057#define GL_GREATER 0x0204#define GL_POLYGON_OFFSET_FILL 0x8037#define GL_TRUE 1#define GL_NEVER 0x0200#define GL_POINTS 0x0000#define GL_ONE_MINUS_SRC_COLOR 0x0301#define GL_MIRRORED_REPEAT 0x8370#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D#define GL_R11F_G11F_B10F 0x8C3A#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B#define GL_RGB9_E5 0x8C3D#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E#define GL_RGBA32UI 0x8D70#define GL_RGB32UI 0x8D71#define GL_RGBA16UI 0x8D76#define GL_RGB16UI 0x8D77#define GL_RGBA8UI 0x8D7C#define GL_RGB8UI 0x8D7D#define GL_RGBA32I 0x8D82#define GL_RGB32I 0x8D83#define GL_RGBA16I 0x8D88#define GL_RGB16I 0x8D89#define GL_RGBA8I 0x8D8E#define GL_RGB8I 0x8D8F#define GL_RED_INTEGER 0x8D94#define GL_RG 0x8227#define GL_RG_INTEGER 0x8228#define GL_R8 0x8229#define GL_R16 0x822A#define GL_RG8 0x822B#define GL_RG16 0x822C#define GL_R16F 0x822D#define GL_R32F 0x822E#define GL_RG16F 0x822F#define GL_RG32F 0x8230#define GL_R8I 0x8231#define GL_R8UI 0x8232#define GL_R16I 0x8233#define GL_R16UI 0x8234#define GL_R32I 0x8235#define GL_R32UI 0x8236#define GL_RG8I 0x8237#define GL_RG8UI 0x8238#define GL_RG16I 0x8239#define GL_RG16UI 0x823A#define GL_RG32I 0x823B#define GL_RG32UI 0x823C#define GL_RGBA_INTEGER 0x8D99#define GL_R8_SNORM 0x8F94#define GL_RG8_SNORM 0x8F95#define GL_RGB8_SNORM 0x8F96#define GL_RGBA8_SNORM 0x8F97#define GL_R16_SNORM 0x8F98#define GL_RG16_SNORM 0x8F99#define GL_RGB16_SNORM 0x8F9A#define GL_RGBA16_SNORM 0x8F9B#define GL_RGBA16 0x805B#define GL_MAX_TEXTURE_SIZE 0x0D33#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C#define GL_MAX_3D_TEXTURE_SIZE 0x8073#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF#define GL_MAX_VERTEX_ATTRIBS 0x8869#define GL_CLAMP_TO_BORDER 0x812D#define GL_TEXTURE_BORDER_COLOR 0x1004#define GL_CURRENT_PROGRAM 0x8B8D#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A#define GL_UNPACK_ALIGNMENT 0x0CF5#define GL_FRAMEBUFFER_SRGB 0x8DB9#define GL_TEXTURE_COMPARE_MODE 0x884C#define GL_TEXTURE_COMPARE_FUNC 0x884D#define GL_COMPARE_REF_TO_TEXTURE 0x884E#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F#define GL_TEXTURE_MAX_LEVEL 0x813D#define GL_FRAMEBUFFER_UNDEFINED 0x8219#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56#define GL_MAJOR_VERSION 0x821B#define GL_MINOR_VERSION 0x821C#define GL_TEXTURE_2D_MULTISAMPLE 0x9100#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102#define GL_SHADER_STORAGE_BARRIER_BIT 0x2000#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400#define GL_MIN 0x8007#define GL_MAX 0x8008#define GL_WRITE_ONLY 0x88B9#define GL_READ_WRITE 0x88BA#define GL_MAX_DRAW_BUFFERS 0x8824#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD#define GL_MAX_IMAGE_UNITS 0x8F38/* ---- the entry points sokol_gfx calls (from sokol_gfx.h) ---- */#define SIGIL_GL_FUNCS \ SIGIL_GL_XMACRO(glBindVertexArray, void, (GLuint array)) \ SIGIL_GL_XMACRO(glFramebufferTextureLayer, void, (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer)) \ SIGIL_GL_XMACRO(glGenFramebuffers, void, (GLsizei n, GLuint * framebuffers)) \ SIGIL_GL_XMACRO(glBindFramebuffer, void, (GLenum target, GLuint framebuffer)) \ SIGIL_GL_XMACRO(glBindRenderbuffer, void, (GLenum target, GLuint renderbuffer)) \ SIGIL_GL_XMACRO(glGetStringi, const GLubyte *, (GLenum name, GLuint index)) \ SIGIL_GL_XMACRO(glClearBufferfi, void, (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil)) \ SIGIL_GL_XMACRO(glClearBufferfv, void, (GLenum buffer, GLint drawbuffer, const GLfloat * value)) \ SIGIL_GL_XMACRO(glClearBufferuiv, void, (GLenum buffer, GLint drawbuffer, const GLuint * value)) \ SIGIL_GL_XMACRO(glClearBufferiv, void, (GLenum buffer, GLint drawbuffer, const GLint * value)) \ SIGIL_GL_XMACRO(glDeleteRenderbuffers, void, (GLsizei n, const GLuint * renderbuffers)) \ SIGIL_GL_XMACRO(glUniform1fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ SIGIL_GL_XMACRO(glUniform2fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ SIGIL_GL_XMACRO(glUniform3fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ SIGIL_GL_XMACRO(glUniform4fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ SIGIL_GL_XMACRO(glUniform1iv, void, (GLint location, GLsizei count, const GLint * value)) \ SIGIL_GL_XMACRO(glUniform2iv, void, (GLint location, GLsizei count, const GLint * value)) \ SIGIL_GL_XMACRO(glUniform3iv, void, (GLint location, GLsizei count, const GLint * value)) \ SIGIL_GL_XMACRO(glUniform4iv, void, (GLint location, GLsizei count, const GLint * value)) \ SIGIL_GL_XMACRO(glUniformMatrix4fv, void, (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)) \ SIGIL_GL_XMACRO(glUseProgram, void, (GLuint program)) \ SIGIL_GL_XMACRO(glShaderSource, void, (GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length)) \ SIGIL_GL_XMACRO(glLinkProgram, void, (GLuint program)) \ SIGIL_GL_XMACRO(glGetUniformLocation, GLint, (GLuint program, const GLchar * name)) \ SIGIL_GL_XMACRO(glGetShaderiv, void, (GLuint shader, GLenum pname, GLint * params)) \ SIGIL_GL_XMACRO(glGetProgramInfoLog, void, (GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ SIGIL_GL_XMACRO(glGetAttribLocation, GLint, (GLuint program, const GLchar * name)) \ SIGIL_GL_XMACRO(glDisableVertexAttribArray, void, (GLuint index)) \ SIGIL_GL_XMACRO(glDeleteShader, void, (GLuint shader)) \ SIGIL_GL_XMACRO(glDeleteProgram, void, (GLuint program)) \ SIGIL_GL_XMACRO(glCompileShader, void, (GLuint shader)) \ SIGIL_GL_XMACRO(glStencilFuncSeparate, void, (GLenum face, GLenum func, GLint ref, GLuint mask)) \ SIGIL_GL_XMACRO(glStencilOpSeparate, void, (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)) \ SIGIL_GL_XMACRO(glRenderbufferStorageMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)) \ SIGIL_GL_XMACRO(glDrawBuffers, void, (GLsizei n, const GLenum * bufs)) \ SIGIL_GL_XMACRO(glVertexAttribDivisor, void, (GLuint index, GLuint divisor)) \ SIGIL_GL_XMACRO(glBufferSubData, void, (GLenum target, GLintptr offset, GLsizeiptr size, const void * data)) \ SIGIL_GL_XMACRO(glGenBuffers, void, (GLsizei n, GLuint * buffers)) \ SIGIL_GL_XMACRO(glCheckFramebufferStatus, GLenum, (GLenum target)) \ SIGIL_GL_XMACRO(glFramebufferRenderbuffer, void, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)) \ SIGIL_GL_XMACRO(glCompressedTexImage2D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data)) \ SIGIL_GL_XMACRO(glCompressedTexImage3D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data)) \ SIGIL_GL_XMACRO(glActiveTexture, void, (GLenum texture)) \ SIGIL_GL_XMACRO(glTexSubImage3D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels)) \ SIGIL_GL_XMACRO(glRenderbufferStorage, void, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height)) \ SIGIL_GL_XMACRO(glGenTextures, void, (GLsizei n, GLuint * textures)) \ SIGIL_GL_XMACRO(glPolygonOffset, void, (GLfloat factor, GLfloat units)) \ SIGIL_GL_XMACRO(glDrawElements, void, (GLenum mode, GLsizei count, GLenum type, const void * indices)) \ SIGIL_GL_XMACRO(glDeleteFramebuffers, void, (GLsizei n, const GLuint * framebuffers)) \ SIGIL_GL_XMACRO(glBlendEquationSeparate, void, (GLenum modeRGB, GLenum modeAlpha)) \ SIGIL_GL_XMACRO(glDeleteTextures, void, (GLsizei n, const GLuint * textures)) \ SIGIL_GL_XMACRO(glGetProgramiv, void, (GLuint program, GLenum pname, GLint * params)) \ SIGIL_GL_XMACRO(glBindTexture, void, (GLenum target, GLuint texture)) \ SIGIL_GL_XMACRO(glTexImage3D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels)) \ SIGIL_GL_XMACRO(glCreateShader, GLuint, (GLenum type)) \ SIGIL_GL_XMACRO(glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels)) \ SIGIL_GL_XMACRO(glFramebufferTexture2D, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) \ SIGIL_GL_XMACRO(glCreateProgram, GLuint, (void)) \ SIGIL_GL_XMACRO(glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ SIGIL_GL_XMACRO(glDeleteBuffers, void, (GLsizei n, const GLuint * buffers)) \ SIGIL_GL_XMACRO(glDrawArrays, void, (GLenum mode, GLint first, GLsizei count)) \ SIGIL_GL_XMACRO(glDrawElementsInstanced, void, (GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount)) \ SIGIL_GL_XMACRO(glVertexAttribPointer, void, (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer)) \ SIGIL_GL_XMACRO(glVertexAttribIPointer, void, (GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer)) \ SIGIL_GL_XMACRO(glUniform1i, void, (GLint location, GLint v0)) \ SIGIL_GL_XMACRO(glDisable, void, (GLenum cap)) \ SIGIL_GL_XMACRO(glColorMask, void, (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) \ SIGIL_GL_XMACRO(glColorMaski, void, (GLuint buf, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) \ SIGIL_GL_XMACRO(glBindBuffer, void, (GLenum target, GLuint buffer)) \ SIGIL_GL_XMACRO(glDeleteVertexArrays, void, (GLsizei n, const GLuint * arrays)) \ SIGIL_GL_XMACRO(glDepthMask, void, (GLboolean flag)) \ SIGIL_GL_XMACRO(glDrawArraysInstanced, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount)) \ SIGIL_GL_XMACRO(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ SIGIL_GL_XMACRO(glGenRenderbuffers, void, (GLsizei n, GLuint * renderbuffers)) \ SIGIL_GL_XMACRO(glBufferData, void, (GLenum target, GLsizeiptr size, const void * data, GLenum usage)) \ SIGIL_GL_XMACRO(glBlendFuncSeparate, void, (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha)) \ SIGIL_GL_XMACRO(glTexParameteri, void, (GLenum target, GLenum pname, GLint param)) \ SIGIL_GL_XMACRO(glGetIntegerv, void, (GLenum pname, GLint * data)) \ SIGIL_GL_XMACRO(glEnable, void, (GLenum cap)) \ SIGIL_GL_XMACRO(glBlitFramebuffer, void, (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)) \ SIGIL_GL_XMACRO(glStencilMask, void, (GLuint mask)) \ SIGIL_GL_XMACRO(glAttachShader, void, (GLuint program, GLuint shader)) \ SIGIL_GL_XMACRO(glGetError, GLenum, (void)) \ SIGIL_GL_XMACRO(glBlendColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \ SIGIL_GL_XMACRO(glTexParameterf, void, (GLenum target, GLenum pname, GLfloat param)) \ SIGIL_GL_XMACRO(glTexParameterfv, void, (GLenum target, GLenum pname, const GLfloat* params)) \ SIGIL_GL_XMACRO(glGetShaderInfoLog, void, (GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ SIGIL_GL_XMACRO(glDepthFunc, void, (GLenum func)) \ SIGIL_GL_XMACRO(glStencilOp , void, (GLenum fail, GLenum zfail, GLenum zpass)) \ SIGIL_GL_XMACRO(glStencilFunc, void, (GLenum func, GLint ref, GLuint mask)) \ SIGIL_GL_XMACRO(glEnableVertexAttribArray, void, (GLuint index)) \ SIGIL_GL_XMACRO(glBlendFunc, void, (GLenum sfactor, GLenum dfactor)) \ SIGIL_GL_XMACRO(glReadBuffer, void, (GLenum src)) \ SIGIL_GL_XMACRO(glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels)) \ SIGIL_GL_XMACRO(glGenVertexArrays, void, (GLsizei n, GLuint * arrays)) \ SIGIL_GL_XMACRO(glFrontFace, void, (GLenum mode)) \ SIGIL_GL_XMACRO(glCullFace, void, (GLenum mode)) \ SIGIL_GL_XMACRO(glPixelStorei, void, (GLenum pname, GLint param)) \ SIGIL_GL_XMACRO(glBindSampler, void, (GLuint unit, GLuint sampler)) \ SIGIL_GL_XMACRO(glGenSamplers, void, (GLsizei n, GLuint* samplers)) \ SIGIL_GL_XMACRO(glSamplerParameteri, void, (GLuint sampler, GLenum pname, GLint param)) \ SIGIL_GL_XMACRO(glSamplerParameterf, void, (GLuint sampler, GLenum pname, GLfloat param)) \ SIGIL_GL_XMACRO(glSamplerParameterfv, void, (GLuint sampler, GLenum pname, const GLfloat* params)) \ SIGIL_GL_XMACRO(glDeleteSamplers, void, (GLsizei n, const GLuint* samplers)) \ SIGIL_GL_XMACRO(glBindBufferBase, void, (GLenum target, GLuint index, GLuint buffer)) \ SIGIL_GL_XMACRO(glBindBufferRange, void, (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size)) \ SIGIL_GL_XMACRO(glTexImage2DMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations)) \ SIGIL_GL_XMACRO(glTexImage3DMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations)) \ SIGIL_GL_XMACRO(glDispatchCompute, void, (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z)) \ SIGIL_GL_XMACRO(glMemoryBarrier, void, (GLbitfield barriers)) \ SIGIL_GL_XMACRO(glBindImageTexture, void, (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format)) \ SIGIL_GL_XMACRO(glTexStorage2DMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations)) \ SIGIL_GL_XMACRO(glTexStorage2D, void, (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)) \ SIGIL_GL_XMACRO(glTexStorage3DMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations)) \ SIGIL_GL_XMACRO(glTexStorage3D, void, (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)) \ SIGIL_GL_XMACRO(glCompressedTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data)) \ SIGIL_GL_XMACRO(glCompressedTexSubImage3D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data)) \ SIGIL_GL_XMACRO(glTextureView, void, (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers)) \ SIGIL_GL_XMACRO(glDrawElementsBaseVertex, void, (GLenum mode, GLsizei count, GLenum type, const void* indices, GLint basevertex)) \ SIGIL_GL_XMACRO(glDrawElementsInstancedBaseVertex, void, (GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount, GLint basevertex)) \ SIGIL_GL_XMACRO(glDrawElementsInstancedBaseVertexBaseInstance, void, (GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance)) \ SIGIL_GL_XMACRO(glDrawArraysInstancedBaseInstance, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance))#define SIGIL_GL_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args;SIGIL_GL_FUNCS#undef SIGIL_GL_XMACRO#define SIGIL_GL_XMACRO(name, ret, args) static PFN_ ## name name;SIGIL_GL_FUNCS#undef SIGIL_GL_XMACROtypedef void *(*sigil_gl_proc_loader)(const char *name);/* Fill every pointer through `load`. Returns the number left NULL: entry * points newer than the context (glTextureView on a 4.1 context, the * base-instance draws before 4.2) are expected to be missing and sokol_gfx * only calls them behind its feature checks; glGenBuffers or glGetIntegerv * missing means there is no usable context, and sigil_gl_ready() says so. */static int sigil_gl_load(sigil_gl_proc_loader load){ int missing = 0;#define SIGIL_GL_XMACRO(name, ret, args) \ name = (PFN_ ## name)load(#name); if (!name) missing++; SIGIL_GL_FUNCS#undef SIGIL_GL_XMACRO return missing;}static int sigil_gl_ready(void){ return glGetIntegerv != NULL && glClearBufferfv != NULL && glGenBuffers != NULL && glCreateShader != NULL && glBindFramebuffer != NULL;}#endif /* SIGIL_GL_NATIVE_LOADER_H */src/c/graphics.cmodified
/* Sokol headers (implementation is in sokol-graphics.c). * * On web (wasm32-wasi) there is no windowing system: sokol_app / sokol_glue are * replaced by the sigil-wasm-gles3 JS bridge (canvas + WebGL2 context + * swapchain). Those two headers are therefore excluded on the web build; every * sapp / sglue touch point is routed through the sig_gfx_ abstraction below, * whose native branch expands to the exact same calls as before. */#if !defined(__wasm__)#include "sokol_app.h"#endif * The window is a platform concern this file reaches only through the * sig_gfx_ abstraction below: natively sigil-desktop's C surface * (framebuffer size; the GL context is current from the init callback on), * on web (wasm32-wasi) the sigil-wasm-gles3 JS bridge (canvas + WebGL2 * context + swapchain). Both build the sokol_gfx environment and swapchain * by hand; there is no sokol_glue. */#include "sokol_gfx.h"#if !defined(__wasm__)#include "sokol_glue.h"#endif#include "sokol_gp.h"#include "sokol_log.h"#if !defined(__wasm__)#include "sigil-desktop.h"#endif#include <stdio.h>#include <stdlib.h>#include <time.h>#include <stdint.h>/* ---- platform abstraction: native (sokol_app/glue) vs web (GL bridge) ------- * These four accessors are the ONLY sokol_app/sokol_glue touch points in this * file. Native expands to the exact sapp / sglue calls used before, so the * native build is behaviour-identical. Web supplies the default framebuffer * swapchain manually (lifted from the Milestone-1 wasm-sokol-sprite example) and * sources the canvas size from the sigil_wasm_gles3 app-shell import module. *//* ---- platform abstraction: native (sigil-desktop) vs web (GL bridge) -------- * These four accessors are the ONLY window touch points in this file. Web * supplies the default framebuffer swapchain manually (lifted from the * Milestone-1 wasm-sokol-sprite example) and sources the canvas size from * the sigil_wasm_gles3 app-shell import module; native asks sigil-desktop * for the framebuffer size and reports the same formats sokol_app did. */#if defined(__wasm__)__attribute__((import_module("sigil_wasm_gles3"), import_name("canvas_width")))extern int sigil_wasm_gles3_canvas_width(void); return swap;}#elsestatic inline int sig_gfx_width(void) { return sapp_width(); }static inline int sig_gfx_height(void) { return sapp_height(); }static inline sg_environment sig_gfx_environment(void) { return sglue_environment(); }static inline sg_swapchain sig_gfx_swapchain(void) { return sglue_swapchain(); }/* Native: sigil-desktop's framebuffer, and the values sokol_app's GL * backend used to report (RGBA8, a combined 24/8 depth-stencil buffer, * which is GLFW's default framebuffer, no MSAA, GL framebuffer 0). */static int sig_gfx_width(void) { int w = 1, h = 1; sigil_desktop_framebuffer_size(&w, &h); return w; }static int sig_gfx_height(void) { int w = 1, h = 1; sigil_desktop_framebuffer_size(&w, &h); return h; }static sg_environment sig_gfx_environment(void) { sg_environment env = {0}; env.defaults.color_format = SG_PIXELFORMAT_RGBA8; env.defaults.depth_format = SG_PIXELFORMAT_DEPTH_STENCIL; env.defaults.sample_count = 1; return env;}static sg_swapchain sig_gfx_swapchain(void) { sg_swapchain swap = {0}; swap.width = sig_gfx_width(); swap.height = sig_gfx_height(); swap.sample_count = 1; swap.color_format = SG_PIXELFORMAT_RGBA8; swap.depth_format = SG_PIXELFORMAT_DEPTH_STENCIL; swap.gl.framebuffer = 0; return swap;}/* sokol-graphics.c: fill sokol's GL entry points from the current context. */int sigil_graphics_load_gl(void);#endif#ifndef M_PI return SIGIL_NIL; }#if !defined(__wasm__) /* Fill sokol's GL entry points from the window's context, which * sigil-desktop made current before the init callback ran. Without a * context (gfx-setup outside app-run) every pointer stays NULL and * sg_setup would crash on the first call, so refuse here instead. */ { int missing = sigil_graphics_load_gl(); if (missing) { sigil__vm_error(vm, SIGIL_ERR_RUNTIME, "gfx-setup: no OpenGL context; call it from the game thunk (inside run-game / app-run)"); return SIGIL_UNDEFINED; } }#endif /* Initialize sokol_gfx. Install slog_func so validation failures * print a useful diagnostic before sokol aborts. */ sg_desc desc = {src/c/sokol-graphics.cmodified
/* * sokol-graphics.c - Sokol implementation for sigil-graphics * * Implements sokol_gfx and sokol_gp (and, on native, sokol_glue). * Implements sokol_gfx and sokol_gp (and, on native, sokol_log), with GL * entry points supplied by an external loader on both targets. * * NATIVE (default): SOKOL_GLCORE. sokol_glue bridges sokol_app (implemented in * sigil-app) to sokol_gfx; sokol_app.h is included for declarations only, and * slog_func comes from sigil-app. * NATIVE (default): SOKOL_GLCORE + SOKOL_EXTERNAL_GL_LOADER. The window and * context are sigil-desktop's (GLFW); gl_native_loader.h fills sokol's GL * function pointers through sigil_desktop_gl_get_proc_address. No system GL * header, no libGL link. * * WEB (wasm32-wasi, __wasm__): SOKOL_GLES3 + SOKOL_EXTERNAL_GL_LOADER (set via * the web build's -D flags). sokol's gl* calls resolve to WASM imports in the * "gl" module via gl_shim.h (shipped by sigil-wasm-gles3). There is no * sokol_app / sokol_glue on web — the canvas, WebGL2 context, and swapchain come * from the sigil-wasm-gles3 JS bridge (see graphics.c's sig_gfx_* abstraction), * so those headers are excluded and slog_func is provided here since sigil-app * is not linked on web. * WEB (wasm32-wasi, __wasm__): SOKOL_GLES3 + SOKOL_EXTERNAL_GL_LOADER. sokol's * gl* calls resolve to WASM imports in the "gl" module via gl_shim.h (shipped * by sigil-wasm-gles3). There is no window package on web: the canvas, WebGL2 * context, and swapchain come from the sigil-wasm-gles3 JS bridge (see * graphics.c's sig_gfx_* abstraction), and slog_func is provided here. *//* sgp's per-draw uniform buffer: 128 floats (512 B). Must equal#else/* Native: desktop GL core backend. Self-configured here (was previously set via * a -DSOKOL_GLCORE compiler flag; moved into the source so one build task serves * both native and web). #ifndef-guarded so a build/example still passing * -DSOKOL_GLCORE is conflict-free. Behaviour is identical: SOKOL_GLCORE ends up * defined either way. *//* Native: desktop GL core backend over sigil-desktop's window. GL comes in * through the generated loader (gl_native_loader.h: sokol's own list of * entry points, filled from sigil_desktop_gl_get_proc_address, which is * GLFW's glfwGetProcAddress), so this file needs no system GL header and * the package links no libGL; GLFW loads libGL/libEGL at run time. The * loader is the same shape as the web build's gl_shim.h, hence * SOKOL_EXTERNAL_GL_LOADER on both. */#ifndef SOKOL_GLCORE#define SOKOL_GLCORE#endif#ifndef SOKOL_EXTERNAL_GL_LOADER#define SOKOL_EXTERNAL_GL_LOADER#endif/* Include sokol_app declarations WITHOUT implementation (implemented in sigil-app) */#include "sokol_app.h"#include "gl_native_loader.h"#include "sigil-desktop.h"/* Now define SOKOL_IMPL for the headers we implement *//* sokol_log's implementation lives here now (it was in sigil-app when the * window was sokol_app); graphics.c declares slog_func through sokol_log.h. */#define SOKOL_IMPL#include "sokol_log.h"/* Order matters: gfx before glue, glue before gp *//* Order matters: gfx before gp. No sokol_glue: graphics.c builds the * environment and swapchain from sigil-desktop's framebuffer size. */#include "sokol_gfx.h"#include "sokol_glue.h"#include "sokol_gp.h"/* slog_func is implemented in sigil-app's archive (sigil-app's * sokol-app.c picks up SOKOL_LOG_IMPL via SOKOL_IMPL). graphics.c * just declares it via sokol_log.h. *//* Called by graphics.c before sg_setup, with the window's context current. * Returns 0 when the core entry points are all present, else the count of * missing ones (and sokol cannot run). */int sigil_graphics_load_gl(void){ int missing = sigil_gl_load(sigil_desktop_gl_get_proc_address); if (!sigil_gl_ready()) return missing > 0 ? missing : 1; return 0;}#endiftest/test-shader-limits/README.mdmodified
`tasks/sigil-graphics-shader-limits`. Frame timing under Xvfb (llvmpipe) andSwiftShader is software rendering on both arms: it bounds the CPU-side cost of astep, not what a phone's GPU will do.## Wayland arm`verify-wl.mjs` is the native arm for a Wayland compositor the caller owns(a headless sway); it captures the window through `swaymsg` and `grim`instead of `xdotool` and `import`, and pins the window to the child's pid.```shWAYLAND_DISPLAY=/run/user/$UID/<private>/wayland-1 SWAYSOCK=<its ipc socket> \ guix shell -m ../../manifest.scm imagemagick grim jq -- \ sh -c 'LD_LIBRARY_PATH=$GUIX_ENVIRONMENT/lib node verify-wl.mjs --native'```With sigil-desktop 0.10 the binary's NEEDED list is libm and libc only, so`guix shell --pure -m <sigil-desktop>/guix/run-wayland.scm -- build/dev/bin/test-shader-limits`runs it with no LD_LIBRARY_PATH at all (the runtime libraries resolvethrough sigil-desktop's dlopen fallback).test/test-shader-limits/dev-redirects.sglmodified
;; Local sigil-graphics is the in-progress version under development.;; Local sigil-graphics is the in-progress version under development;;; sigil-desktop (URL still sigil-app until the Codeberg rename) is the;; sibling checkout.(redirects repos: (list (for-repo url: "codeberg:sigil/sigil-graphics" use: (from-path dir: "../..")))) use: (from-path dir: "../..")) (for-repo url: "codeberg:sigil/sigil-app" use: (from-path dir: "../../../sigil-desktop"))))test/test-shader-limits/package.sglmodified
features: '(web wasm graphics))) dependencies: (list ;; Windowing, native only (sokol_app + X11 cannot build for wasm). (from-git url: "codeberg:sigil/sigil-app" version: "^0.9.0" configs: '(dev)) ;; Windowing, native only (GLFW cannot build for wasm). (from-git url: "codeberg:sigil/sigil-app" name: "sigil-desktop" version: "^0.10.0" configs: '(dev)) ;; The package under test; dev-redirects.sgl points it at ../.. (from-git url: "codeberg:sigil/sigil-graphics" version: "^0.11.2") ;; Web only: the wasm runtime (loader + entry ABI) and the raw-WebGL2test/test-shader-limits/src/test-shader-limits/shell-native.sglmodified
;;; (test-shader-limits shell-native) - The sigil-app window.;;; (test-shader-limits shell-native) - The sigil-desktop window.;;;;;; test-shader-limits [--grid N] [--steps N] [--rule life|reaction] [--substeps N];;; [--bench] [--hold SECONDS](define-library (test-shader-limits shell-native) (import (sigil core) (sigil process) (sigil app) (sigil desktop) (sigil graphics) (test-shader-limits scene))test/test-shader-limits/verify-wl.mjsadded
// verify-wl.mjs - verify.mjs's native arm for a Wayland compositor: the same// scene, the same assertions, the window captured through the compositor// instead of an X server.//// node verify-wl.mjs --native [--bin ...] [--grid 64] [--steps 8] [--expect-steps N] [--shot PATH]//// Runs the binary with WAYLAND_DISPLAY pointing at a compositor the caller// owns (a headless sway: WLR_BACKENDS=headless sway -c <config>, see the// sigil-desktop notes), waits for "shader-limits: ready", finds the window// in `swaymsg -t get_tree` by name AND by the child's pid (a leftover window// from an earlier run must not be captured), and reads its rect with// `grim -g`. swaymsg, jq, grim, convert and identify must be on PATH; the// window must float without a border (a `for_window ... floating enable,// border none` rule) so the rect is the framebuffer. The assertions scale// with the capture, so an output at scale 2 (a 1280x960 capture of the// 640x480 window) passes the same checks.//// Everything below this header is verify.mjs; keep the two in step.import http from "node:http";import fs from "node:fs";import path from "node:path";import { spawn, execFileSync } from "node:child_process";const args = process.argv.slice(2);const flag = (name) => args.includes(name);const opt = (name, dflt) => { const i = args.indexOf(name); return i >= 0 ? args[i + 1] : dflt; };const VALUED = ["--bin", "--grid", "--steps", "--expect-steps", "--shot", "--port", "--cdp", "--reports", "--hold", "--rule", "--substeps"];const positional = args.filter((a, i) => !a.startsWith("--") && !(i > 0 && VALUED.includes(args[i - 1])));const NATIVE = flag("--native");const WEB = flag("--web");const BENCH = flag("--bench");if (NATIVE === WEB) { console.log("usage: node verify.mjs --native | --web [options]"); process.exit(2); }const GRID = parseInt(opt("--grid", BENCH ? "512" : "64"), 10);const STEPS = parseInt(opt("--steps", "8"), 10);const EXPECT_STEPS = parseInt(opt("--expect-steps", String(STEPS)), 10);const SHOT = opt("--shot", `/tmp/test-shader-limits-${NATIVE ? "native" : "web"}.png`);const REPORTS = parseInt(opt("--reports", "3"), 10);const HOLD = parseInt(opt("--hold", "60"), 10);const RULE = opt("--rule", "life");const SUBSTEPS = parseInt(opt("--substeps", "1"), 10);const REACTION = RULE === "reaction";// ---- the scene's geometry, mirrored from scene.sgl ---------------------------const VW = 640, VH = 480;const FIELD_X = 8, FIELD_Y = 8, FIELD_PX = 384;const PAL_X = 408, LEV_X = 440, STRIP = 24, STRIP_GAP = 28;const PIX_X = 408, PIX_Y = 260, PIX_SCALE = 16;const PALETTE = [[255, 0, 0], [0, 255, 0], [0, 0, 255], [255, 255, 0], [0, 255, 255], [255, 0, 255], [255, 128, 0], [128, 0, 255]];const LEVELS = [0, 64, 128, 255];const PATTERN_B = [ [0, 255, 0], [255, 255, 0], [0, 255, 0], [255, 255, 0], [255, 255, 0], [255, 255, 255], [255, 255, 0], [0, 255, 0], [0, 255, 0], [255, 255, 0], [0, 0, 0], [255, 255, 0], [255, 255, 0], [0, 255, 0], [255, 255, 0], [0, 255, 0],];const PROBES = ["make-texture-size", "update-texture-size", "update-same-frame", "update-immutable", "shader-compile-error", "uniform-block-too-large", "uniform-too-many", "uniform-count", "uniform-scalar-for-array", "filter-name", "uniform-nested", "uniform-flat-vector"];// The seed, as scene.sgl draws it, and a CPU Life on a torus.function seedGrid(n) { const g = new Uint8Array(n * n); const set = (x, y) => { g[((y + n) % n) * n + ((x + n) % n)] = 1; }; const s = n - 3; set(s + 1, s); set(s + 2, s + 1); set(s, s + 2); set(s + 1, s + 2); set(s + 2, s + 2); set(9, 10); set(10, 10); set(11, 10); set(20, 20); set(21, 20); set(20, 21); set(21, 21); return g;}function lifeStep(g, n) { const out = new Uint8Array(n * n); for (let y = 0; y < n; y++) for (let x = 0; x < n; x++) { let c = 0; for (let dy = -1; dy <= 1; dy++) for (let dx = -1; dx <= 1; dx++) { if (dx || dy) c += g[((y + dy + n) % n) * n + ((x + dx + n) % n)]; } const alive = g[y * n + x]; out[y * n + x] = (c === 3 || (c === 2 && alive)) ? 1 : 0; } return out;}function expectedField(n, steps) { let g = seedGrid(n); for (let i = 0; i < steps; i++) g = lifeStep(g, n); return g; }// ---- verdicts -------------------------------------------------------------------const results = [];const planned = (WEB ? ["imports"] : []).concat(["probes", "update", "ready", "field", "palette", "pixels", "gl-log"]).concat(WEB ? ["console"] : []);function pass(name, detail) { results.push([name, "PASS"]); console.log(`PASS ${name}${detail ? ": " + detail : ""}`); }function fail(name, detail) { results.push([name, "FAIL"]); console.log(`FAIL ${name}: ${detail}`); }function notRun() { const done = new Set(results.map((r) => r[0])); return planned.filter((p) => !done.has(p)); }const sleep = (ms) => new Promise((r) => setTimeout(r, ms));// A captured frame: RGBA bytes, buffer size, and the virtual-to-buffer scale.// px(vx, vy) samples the buffer pixel under the centre of virtual pixel (vx, vy).function makeFrame(data, w, h) { const sx = w / VW, sy = h / VH; return { w, h, sx, sy, px(vx, vy) { const bx = Math.min(w - 1, Math.floor((vx + 0.5) * sx)), by = Math.min(h - 1, Math.floor((vy + 0.5) * sy)); const i = (by * w + bx) * 4; return [data[i], data[i + 1], data[i + 2]]; }, };}const near = (a, b, tol) => Math.abs(a[0] - b[0]) <= tol && Math.abs(a[1] - b[1]) <= tol && Math.abs(a[2] - b[2]) <= tol;function checkLines(lines) { // probes const seen = new Map(); for (const l of lines) { const m = l.match(/^shader-limits: probe (\S+) (ok|FAIL)(.*)$/); if (m) seen.set(m[1], m[2] + m[3]); } const missing = PROBES.filter((p) => !seen.has(p)); const failed = [...seen].filter(([, v]) => v.startsWith("FAIL")).map(([k, v]) => `${k}${v.slice(4)}`); if (!missing.length && !failed.length) pass("probes", `${PROBES.length} probes ok`); else fail("probes", `${missing.length ? "missing " + missing.join(",") + "; " : ""}${failed.length ? "failed: " + failed.join(" | ") : ""}`); // update const upd = lines.find((l) => /^shader-limits: update /.test(l)); if (upd === "shader-limits: update ok") pass("update", "update-texture landed on frame 2"); else fail("update", upd || "no update line"); // ready const rdy = lines.map((l) => l.match(/^shader-limits: ready grid=(\d+) steps=(\d+)$/)).find(Boolean); if (rdy && parseInt(rdy[1], 10) === GRID && parseInt(rdy[2], 10) === EXPECT_STEPS) pass("ready", rdy[0]); else fail("ready", rdy ? `${rdy[0]} (expected grid=${GRID} steps=${EXPECT_STEPS})` : "no ready line");}function checkFrame(frame) { if (REACTION) { checkReaction(frame); checkStripsAndPixels(frame); return; } // field const exp = expectedField(GRID, EXPECT_STEPS); const cell = FIELD_PX / GRID; let mismatches = 0, live = 0, seenLive = 0; const firstBad = []; for (let y = 0; y < GRID; y++) for (let x = 0; x < GRID; x++) { const c = frame.px(FIELD_X + x * cell + (cell - 1) / 2, FIELD_Y + y * cell + (cell - 1) / 2); const lit = c[0] > 128 ? 1 : 0; live += exp[y * GRID + x]; seenLive += lit; if (lit !== exp[y * GRID + x]) { mismatches++; if (firstBad.length < 6) firstBad.push(`(${x},${y}) got ${lit} want ${exp[y * GRID + x]}`); } } if (mismatches === 0 && live >= 12) pass("field", `${GRID}x${GRID} cells match a CPU Life after ${EXPECT_STEPS} steps; ${live} live cells (glider, blinker, block)`); else fail("field", `${mismatches} of ${GRID * GRID} cells differ (expected ${live} live, saw ${seenLive}); first: ${firstBad.join("; ")}`); checkStripsAndPixels(frame);}function checkStripsAndPixels(frame) { // palette const bad = []; for (let i = 0; i < 8; i++) { const c = frame.px(PAL_X + STRIP / 2, 8 + i * STRIP_GAP + STRIP / 2); if (!near(c, PALETTE[i], 12)) bad.push(`palette[${i}] got ${c} want ${PALETTE[i]}`); } for (let i = 0; i < 4; i++) { const c = frame.px(LEV_X + STRIP / 2, 8 + i * STRIP_GAP + STRIP / 2); if (!near(c, [LEVELS[i], LEVELS[i], LEVELS[i]], 12)) bad.push(`levels[${i}] got ${c} want ${LEVELS[i]}`); } if (!bad.length) pass("palette", "8 vec4 palette entries and 4 float levels read back through the shader"); else fail("palette", bad.join("; ")); // pixels const badPx = []; for (let t = 0; t < 16; t++) { const tx = t % 4, ty = Math.floor(t / 4); const c = frame.px(PIX_X + tx * PIX_SCALE + PIX_SCALE / 2, PIX_Y + ty * PIX_SCALE + PIX_SCALE / 2); if (!near(c, PATTERN_B[t], 12)) badPx.push(`texel ${tx},${ty} got ${c} want ${PATTERN_B[t]}`); } if (!badPx.length) pass("pixels", "16 texels show the update-texture pattern"); else fail("pixels", badPx.join("; "));}// Gray-Scott has no closed form to compare against, so the reaction field is// judged by shape: v (the G channel) must have grown from three seed squares// (together 4.7% of the field) into a pattern covering between 6% and// 70% of the cells, while u (R) is still high somewhere (the field has not// saturated), and the pattern must not be one flat value.function checkReaction(frame) { const cell = FIELD_PX / GRID; let vHigh = 0, uHigh = 0; const vBins = new Set(); for (let y = 0; y < GRID; y++) for (let x = 0; x < GRID; x++) { const c = frame.px(FIELD_X + x * cell + (cell - 1) / 2, FIELD_Y + y * cell + (cell - 1) / 2); if (c[1] > 64) vHigh++; if (c[0] > 192) uHigh++; vBins.add(c[1] >> 4); } const total = GRID * GRID, vf = vHigh / total, uf = uHigh / total; const detail = `v>0.25 on ${(vf * 100).toFixed(1)}% of cells, u>0.75 on ${(uf * 100).toFixed(1)}%, ${vBins.size} v levels`; // the three seed squares cover 3/64 = 4.7% of the field; growth past 6% is the pattern if (vf >= 0.06 && vf <= 0.7 && uf >= 0.2 && vBins.size >= 6) pass("field", `reaction pattern grew: ${detail}`); else fail("field", `no reaction pattern: ${detail}`);}// sokol logs through slog_func: natively sigil-app's format `[sg][error][id:N] ...`// on stderr, on the web sigil-graphics' own `sokol[level=L] file:line [tag] msg`// on the console (level 0 panic, 1 error, 2 warning, 3 info; the web build// has no SOKOL_DEBUG (zig cc defines NDEBUG at -Oz, measured 2026-09-19 with// `zig cc -Oz -target wasm32-wasi -E` on an #ifdef NDEBUG probe) so the// message text is empty and the line number is// the pointer). A render target whose pass sokol refuses draws nothing and// says so only here, so any error-level line is a failure.// The shader-compile-error probe fails one GLSL compile on purpose, which// sokol reports as GL_SHADER_COMPILATION_FAILED (item id 7; sokol_gfx.h:10828// in the vendored header): exactly one such line is expected and exempt.const COMPILE_FAIL = (l) => /\[sg\]\[error\]\[id:7\]/.test(l) || /^sokol\[level=1\] \S+:10828 \[sg\]/.test(l);function checkGlLog(lines) { const all = lines.filter((l) => /\[(sg|sgp|sapp)\]\[(error|panic)\]/.test(l) || /^sokol\[level=[01]\]/.test(l)); const expected = all.filter(COMPILE_FAIL); const bad = all.filter((l) => !COMPILE_FAIL(l)).concat(expected.slice(1)); if (expected.length !== 1) bad.push(`expected exactly one deliberate compile failure line, saw ${expected.length}`); const warn = lines.filter((l) => /\[(sg|sgp)\]\[warning\]/.test(l) || /^sokol\[level=2\]/.test(l)); if (!bad.length) pass("gl-log", `no sokol error lines beyond the one deliberate compile failure${warn.length ? "; " + warn.length + " warning(s): " + warn.slice(0, 3).join(" | ") : ""}`); else fail("gl-log", `${bad.length} sokol error line(s): ${bad.slice(0, 4).join(" | ")}`);}function finish() { const failed = results.filter((r) => r[1] === "FAIL").length; const nr = notRun(); console.log(`${failed ? "RED" : "GREEN"}: ${results.length - failed} pass, ${failed} fail${nr.length ? "; did not run: " + nr.join(" ") : ""}`); return failed || nr.length ? 1 : 0;}// =============================================================================// NATIVE ARM// =============================================================================if (NATIVE) { const bin = path.resolve(opt("--bin", "build/dev/bin/test-shader-limits")); if (!fs.existsSync(bin)) { console.log(`SETUP-FAILED: ${bin} missing; build first`); process.exit(2); } if (!process.env.WAYLAND_DISPLAY) { console.log("SETUP-FAILED: WAYLAND_DISPLAY is not set; point it at a worker-owned headless sway"); process.exit(2); } for (const tool of ["swaymsg", "jq", "grim", "convert", "identify"]) { try { execFileSync("sh", ["-c", `command -v ${tool}`], { stdio: "ignore" }); } catch { console.log(`SETUP-FAILED: ${tool} is not on PATH (run inside guix shell -m ../../manifest.scm imagemagick xdotool)`); process.exit(2); } } const appArgs = ["--grid", String(GRID), "--steps", String(STEPS), "--hold", String(HOLD), "--rule", RULE, "--substeps", String(SUBSTEPS)].concat(BENCH ? ["--bench"] : []); const child = spawn(bin, appArgs, { stdio: ["ignore", "pipe", "pipe"], detached: true }); const lines = []; let buf = ""; child.stdout.on("data", (d) => { buf += d.toString(); let i; while ((i = buf.indexOf("\n")) >= 0) { lines.push(buf.slice(0, i)); buf = buf.slice(i + 1); } }); const stderrLines = []; child.stderr.on("data", (d) => { for (const l of d.toString().split("\n")) if (l.trim()) stderrLines.push(l); }); let exited = null; child.on("exit", (code, sig) => { exited = { code, sig }; }); const stop = () => { try { process.kill(-child.pid, "SIGTERM"); } catch { /* gone */ } }; process.on("exit", stop); const waitLine = async (re, ms) => { const t0 = Date.now(); while (Date.now() - t0 < ms) { const hit = lines.find((l) => re.test(l)); if (hit) return hit; if (exited) return null; await sleep(100); } return null; }; if (BENCH) { const t0 = Date.now(); while (Date.now() - t0 < HOLD * 1000 + 5000) { const reports = lines.filter((l) => /^shader-limits: frame-ms /.test(l)); if (reports.length >= REPORTS || exited) break; await sleep(200); } console.log(`bench native grid=${GRID} (renderer: ${glRenderer()})`); for (const l of lines.filter((l) => /^shader-limits: (init|frame-ms) /.test(l))) console.log(" " + l); stop(); process.exit(0); } const ready = await waitLine(/^shader-limits: ready /, 30000); if (!ready) { console.log(`TIMED-OUT ready: no ready line in 30 s${exited ? ` (exited ${JSON.stringify(exited)})` : ""}; did not run: ${notRun().join(" ")}`); console.log("stdout so far:\n " + lines.join("\n ")); console.log("stderr so far:\n " + stderrLines.join("\n ")); stop(); process.exit(2); } checkLines(lines); // The frame that shows the settled state was presented before the line // was printed, and the app keeps presenting it; one more frame's worth of // time makes sure the X server has it before the capture. await sleep(300); // WAYLAND ARM (scratch copy for the sigil-desktop-glfw spike): capture the // window through the compositor. swaymsg gives the window's rect on the // headless output; grim reads that region in output pixels. let rect = ""; for (let i = 0; i < 50 && !rect; i++) { try { rect = execFileSync("sh", ["-c", `swaymsg -t get_tree | jq -r '.. | objects | select(.name? == "test-shader-limits" and .pid? == ${child.pid}) | "\\(.rect.x),\\(.rect.y) \\(.rect.width)x\\(.rect.height)"' | head -1`]).toString().trim(); } catch { /* not yet */ } if (!rect) await sleep(100); } if (!rect) { console.log("SETUP-FAILED: no sway window named test-shader-limits; did not run: " + notRun().join(" ")); stop(); process.exit(2); } console.log(`note: sway window rect ${rect}`); execFileSync("grim", ["-g", rect, SHOT]); const [w, h] = execFileSync("identify", ["-format", "%w %h", SHOT]).toString().trim().split(" ").map(Number); const raw = execFileSync("convert", [SHOT, "-depth", "8", "rgba:-"], { maxBuffer: 64 * 1024 * 1024 }); if (raw.length !== w * h * 4) { console.log(`SETUP-FAILED: raw dump is ${raw.length} bytes for ${w}x${h}`); stop(); process.exit(2); } console.log(`note: captured ${w}x${h} window to ${SHOT}`); checkFrame(makeFrame(raw, w, h)); checkGlLog(stderrLines.concat(lines)); stop(); process.exit(finish());}function glRenderer() { try { return execFileSync("sh", ["-c", "glxinfo -B 2>/dev/null | grep -i 'renderer string' | head -1"]).toString().trim() || "glxinfo unavailable"; } catch { return "glxinfo unavailable"; }}// =============================================================================// WEB ARM// =============================================================================const ROOT = path.resolve(positional[0] || "build/web");const PORT = parseInt(opt("--port", "8097"), 10);const CDP = parseInt(opt("--cdp", "9237"), 10);const TYPES = { ".html": "text/html;charset=utf-8", ".js": "text/javascript;charset=utf-8", ".wasm": "application/wasm", ".json": "application/json;charset=utf-8", ".css": "text/css;charset=utf-8", ".png": "image/png" };// ---- imports, from the file on disk ------------------------------------------{ const wasmPath = path.join(ROOT, "test-shader-limits.wasm"); if (!fs.existsSync(wasmPath)) { console.log(`SETUP-FAILED imports: ${wasmPath} missing; build --config web first`); process.exit(2); } const mod = new WebAssembly.Module(fs.readFileSync(wasmPath)); const byModule = {}; for (const imp of WebAssembly.Module.imports(mod)) (byModule[imp.module] = byModule[imp.module] || []).push(imp.name); const modules = Object.keys(byModule).sort(); const expected = ["gl", "sigil_wasm_gles3", "wasi_snapshot_preview1"]; const listing = modules.map((m) => `${m}(${byModule[m].length})`).join(" "); if (JSON.stringify(modules) === JSON.stringify(expected)) pass("imports", listing); else fail("imports", `expected modules ${expected.join(",")} got ${listing}`);}const server = http.createServer((req, res) => { const urlPath = decodeURIComponent((req.url || "/").split("?")[0]); const fp = path.join(ROOT, urlPath === "/" ? "/index.html" : urlPath); if (fp !== ROOT && !fp.startsWith(ROOT + path.sep)) { res.writeHead(403).end(); return; } fs.readFile(fp, (err, buf) => { if (err) { res.writeHead(404).end("not found: " + urlPath); return; } res.writeHead(200, { "Content-Type": TYPES[path.extname(fp)] || "application/octet-stream", "Cache-Control": "no-store" }); res.end(buf); });});await new Promise((r) => server.listen(PORT, "127.0.0.1", r));const udd = fs.mkdtempSync("/tmp/test-shader-limits-chrome-");const chrome = spawn("google-chrome", [ "--headless=new", "--no-sandbox", "--disable-dev-shm-usage", "--use-gl=angle", "--use-angle=swiftshader", "--enable-unsafe-swiftshader", "--enable-webgl", "--ignore-gpu-blocklist", `--remote-debugging-port=${CDP}`, `--user-data-dir=${udd}`, "--window-size=800,600", "about:blank",], { stdio: "ignore", detached: true });function killChromeGroup(sig) { try { process.kill(-chrome.pid, sig); } catch { /* gone */ } }let exiting = false;function shutdown(code) { if (exiting) return; exiting = true; try { server.close(); } catch { /* not listening */ } killChromeGroup("SIGTERM"); setTimeout(() => { killChromeGroup("SIGKILL"); try { fs.rmSync(udd, { recursive: true, force: true }); } catch { /* scratch */ } process.exit(code); }, 1500).unref(); const done = () => { try { fs.rmSync(udd, { recursive: true, force: true }); } catch { /* scratch */ } process.exit(code); }; if (chrome.exitCode !== null) done(); else chrome.once("exit", done);}process.on("exit", () => { killChromeGroup("SIGKILL"); try { fs.rmSync(udd, { recursive: true, force: true }); } catch { /* scratch */ } });process.on("SIGINT", () => shutdown(130));process.on("SIGTERM", () => shutdown(143));process.on("unhandledRejection", (err) => { console.log("EXCEPTION: " + (err && err.stack || err)); shutdown(2); });process.on("uncaughtException", (err) => { console.log("EXCEPTION: " + (err && err.stack || err)); shutdown(2); });setTimeout(() => { console.log(`TIMED-OUT whole run after 120 s; did not run: ${notRun().join(" ")}`); shutdown(2); }, 120000).unref();let pageWs = null;for (let i = 0; i < 80 && !pageWs; i++) { try { const ts = await (await fetch(`http://127.0.0.1:${CDP}/json`)).json(); const p = ts.find((t) => t.type === "page"); if (p && p.webSocketDebuggerUrl) pageWs = p.webSocketDebuggerUrl; } catch { /* not up yet */ } await sleep(250);}if (!pageWs) { console.log("SETUP-FAILED: no chrome page target after 20 s; did not run: " + notRun().join(" ")); shutdown(2); }const ws = new WebSocket(pageWs);let msgId = 0; const pending = new Map();const consoleLines = []; const consoleErrors = [];function send(method, params = {}) { return new Promise((res, rej) => { const id = ++msgId; pending.set(id, { res, rej }); ws.send(JSON.stringify({ id, method, params })); });}ws.addEventListener("message", (ev) => { const msg = JSON.parse(ev.data); if (msg.id && pending.has(msg.id)) { const { res, rej } = pending.get(msg.id); pending.delete(msg.id); msg.error ? rej(new Error(JSON.stringify(msg.error))) : res(msg.result); return; } if (msg.method === "Runtime.consoleAPICalled") { const text = (msg.params.args || []).map((a) => a.value ?? a.description ?? "").join(" "); consoleLines.push(text); if (msg.params.type === "error") consoleErrors.push("console.error: " + text); } if (msg.method === "Log.entryAdded" && msg.params.entry.level === "error") consoleErrors.push("log: " + msg.params.entry.text); if (msg.method === "Runtime.exceptionThrown") consoleErrors.push("exception: " + (msg.params.exceptionDetails?.exception?.description || msg.params.exceptionDetails?.text));});await new Promise((res, rej) => { ws.addEventListener("open", res); ws.addEventListener("error", rej); });await send("Page.enable"); await send("Runtime.enable"); await send("Log.enable");// A devicePixelRatio of 2 so the canvas buffer (1280x960) is not the virtual// size: the sampler's scale mapping is then exercised, not the identity.await send("Emulation.setDeviceMetricsOverride", { width: 800, height: 600, deviceScaleFactor: 2, mobile: false });async function evalJS(expr) { const r = await send("Runtime.evaluate", { expression: expr, returnByValue: true, awaitPromise: true }); if (r.exceptionDetails) throw new Error(JSON.stringify(r.exceptionDetails)); return r.result.value;}async function waitLine(re, ms) { const t0 = Date.now(); while (Date.now() - t0 < ms) { const hit = consoleLines.find((l) => re.test(l)); if (hit) return hit; await sleep(100); } return null;}const query = `?grid=${GRID}&steps=${STEPS}&rule=${RULE}&substeps=${SUBSTEPS}${BENCH ? "&bench" : ""}`;await send("Page.navigate", { url: `http://127.0.0.1:${PORT}/index.html${query}` });if (BENCH) { const t0 = Date.now(); while (Date.now() - t0 < 90000) { if (consoleLines.filter((l) => /^shader-limits: frame-ms /.test(l)).length >= REPORTS) break; await sleep(200); } const renderer = await evalJS(`(() => { const c = document.createElement("canvas"); const gl = c.getContext("webgl2"); const d = gl && gl.getExtension("WEBGL_debug_renderer_info"); return d ? gl.getParameter(d.UNMASKED_RENDERER_WEBGL) : "unknown"; })()`); console.log(`bench web grid=${GRID} (renderer: ${renderer})`); for (const l of consoleLines.filter((l) => /^shader-limits: (init|frame-ms) /.test(l))) console.log(" " + l); shutdown(0);} else { const ready = await waitLine(/^shader-limits: ready /, 30000); if (!ready) { console.log(`TIMED-OUT ready: no ready line in 30 s; did not run: ${notRun().join(" ")}`); console.log("console so far:\n " + consoleLines.join("\n ") + "\nerrors:\n " + consoleErrors.join("\n ")); shutdown(2); } else { checkLines(consoleLines); if (flag("--verbose")) console.log("console:\n " + consoleLines.join("\n ")); // Read the canvas inside an animation frame: the app's tick registered // its next frame first, so a callback registered now runs after the draw // and before the (non-preserved) buffer is discarded. const px = await evalJS(`new Promise((resolve) => requestAnimationFrame(() => { const c = document.getElementById("stage"); const off = document.createElement("canvas"); off.width = c.width; off.height = c.height; const g = off.getContext("2d"); g.drawImage(c, 0, 0); const d = g.getImageData(0, 0, c.width, c.height).data; resolve({ w: c.width, h: c.height, data: Array.from(d) }); }))`); const shot = await send("Page.captureScreenshot", { format: "png" }); fs.writeFileSync(SHOT, Buffer.from(shot.data, "base64")); console.log(`note: canvas buffer ${px.w}x${px.h}; page screenshot at ${SHOT}`); checkFrame(makeFrame(Uint8Array.from(px.data), px.w, px.h)); checkGlLog(consoleLines); if (!consoleErrors.length) pass("console", "zero error entries, zero exceptions"); else fail("console", consoleErrors.slice(0, 5).join(" | ")); shutdown(finish()); }}vendor/sokol/sokol_app.hdeleted
#if defined(SOKOL_IMPL) && !defined(SOKOL_APP_IMPL)#define SOKOL_APP_IMPL#endif#ifndef SOKOL_APP_INCLUDED/* sokol_app.h -- cross-platform application wrapper Project URL: https://github.com/floooh/sokol Do this: #define SOKOL_IMPL or #define SOKOL_APP_IMPL before you include this file in *one* C or C++ file to create the implementation. In the same place define one of the following to select the 3D-API which should be initialized by sokol_app.h (this must also match the backend selected for sokol_gfx.h if both are used in the same project): #define SOKOL_GLCORE #define SOKOL_GLES3 #define SOKOL_D3D11 #define SOKOL_METAL #define SOKOL_WGPU #define SOKOL_NOAPI Optionally provide the following defines with your own implementations: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) SOKOL_WIN32_FORCE_MAIN - define this on Win32 to add a main() entry point SOKOL_WIN32_FORCE_WINMAIN - define this on Win32 to add a WinMain() entry point (enabled by default unless SOKOL_WIN32_FORCE_MAIN or SOKOL_NO_ENTRY is defined) SOKOL_NO_ENTRY - define this if sokol_app.h shouldn't "hijack" the main() function SOKOL_APP_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_APP_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) Optionally define the following to force debug checks and validations even in release mode: SOKOL_DEBUG - by default this is defined if NDEBUG is not defined If sokol_app.h is compiled as a DLL, define the following before including the declaration or implementation: SOKOL_DLL On Windows, SOKOL_DLL will define SOKOL_APP_API_DECL as __declspec(dllexport) or __declspec(dllimport) as needed. if SOKOL_WIN32_FORCE_MAIN and SOKOL_WIN32_FORCE_WINMAIN are both defined, it is up to the developer to define the desired subsystem. On Linux, SOKOL_GLCORE 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 have been taken from GLFW (http://www.glfw.org/). iOS onscreen keyboard support 'inspired' by libgdx. Link with the following system libraries: - on macOS: - all backends: Foundation, Cocoa, QuartzCore - with SOKOL_METAL: Metal, MetalKit - with SOKOL_GLCORE: OpenGL - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn) - on iOS: - all backends: Foundation, UIKit - with SOKOL_METAL: Metal, MetalKit - with SOKOL_GLES3: OpenGLES, GLKit - on Linux: - all backends: X11, Xi, Xcursor, dl, pthread, m - with SOKOL_GLCORE: GL - with SOKOL_GLES3: GLESv2 - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn) - with EGL: EGL - on Android: GLESv3, EGL, log, android - on Windows: - with MSVC or Clang: library dependencies are defined via `#pragma comment` - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn) - with MINGW/MSYS2 gcc: - compile with '-mwin32' so that _WIN32 is defined - link with the following libs: -lkernel32 -luser32 -lshell32 - additionally with the GL backend: -lgdi32 - additionally with the D3D11 backend: -ld3d11 -ldxgi On Linux, you also need to use the -pthread compiler and linker option, otherwise weird things will happen, see here for details: https://github.com/floooh/sokol/issues/376 On macOS and iOS, the implementation must be compiled as Objective-C. On Emscripten: - for WebGL2: add the linker option `-s USE_WEBGL2=1` - for WebGPU: compile and link with `--use-port=emdawnwebgpu` (for more exotic situations read: https://dawn.googlesource.com/dawn/+/refs/heads/main/src/emdawnwebgpu/pkg/README.md) FEATURE OVERVIEW ================ sokol_app.h provides a minimalistic cross-platform API which implements the 'application-wrapper' parts of a 3D application: - a common application entry function - creates a window and 3D-API context/device with a swapchain surface, depth-stencil-buffer surface and optionally MSAA surface - makes the rendered frame visible - provides keyboard-, mouse- and low-level touch-events - platforms: MacOS, iOS, HTML5, Win32, Linux/RaspberryPi, Android - 3D-APIs: Metal, D3D11, GL4.1, GL4.3, GLES3, WebGL2, WebGPU, NOAPI FEATURE/PLATFORM MATRIX ======================= | Windows | macOS | Linux | iOS | Android | HTML5 --------------------+---------+-------+-------+-------+---------+-------- gl 4.x | YES | YES | YES | --- | --- | --- gles3/webgl2 | --- | --- | YES(2)| YES | YES | YES metal | --- | YES | --- | YES | --- | --- d3d11 | YES | --- | --- | --- | --- | --- webgpu | YES(4) | YES(4)| YES(4)| NO | NO | YES noapi | YES | TODO | TODO | --- | TODO | --- KEY_DOWN | YES | YES | YES | SOME | TODO | YES KEY_UP | YES | YES | YES | SOME | TODO | YES CHAR | YES | YES | YES | YES | TODO | YES MOUSE_DOWN | YES | YES | YES | --- | --- | YES MOUSE_UP | YES | YES | YES | --- | --- | YES MOUSE_SCROLL | YES | YES | YES | --- | --- | YES MOUSE_MOVE | YES | YES | YES | --- | --- | YES MOUSE_ENTER | YES | YES | YES | --- | --- | YES MOUSE_LEAVE | YES | YES | YES | --- | --- | YES TOUCHES_BEGAN | --- | --- | --- | YES | YES | YES TOUCHES_MOVED | --- | --- | --- | YES | YES | YES TOUCHES_ENDED | --- | --- | --- | YES | YES | YES TOUCHES_CANCELLED | --- | --- | --- | YES | YES | YES RESIZED | YES | YES | YES | YES | YES | YES ICONIFIED | YES | YES | YES | --- | --- | --- RESTORED | YES | YES | YES | --- | --- | --- FOCUSED | YES | YES | YES | --- | --- | YES UNFOCUSED | YES | YES | YES | --- | --- | YES SUSPENDED | --- | --- | --- | YES | YES | TODO RESUMED | --- | --- | --- | YES | YES | TODO QUIT_REQUESTED | YES | YES | YES | --- | --- | YES IME | TODO | TODO? | TODO | ??? | TODO | ??? key repeat flag | YES | YES | YES | --- | --- | YES windowed | YES | YES | YES | --- | --- | YES fullscreen | YES | YES | YES | YES | YES | YES(3) mouse hide | YES | YES | YES | --- | --- | YES mouse lock | YES | YES | YES | --- | --- | YES set cursor type | YES | YES | YES | --- | --- | YES screen keyboard | --- | --- | --- | YES | TODO | YES swap interval | YES | YES | YES | YES | TODO | YES high-dpi | YES | YES | TODO | YES | YES | YES clipboard | YES | YES | YES | --- | --- | YES MSAA | YES | YES | YES | YES | YES | YES drag'n'drop | YES | YES | YES | --- | --- | YES window icon | YES | YES(1)| YES | --- | --- | YES (1) macOS has no regular window icons, instead the dock icon is changed (2) supported with EGL only (not GLX) (3) fullscreen in the browser not supported on iphones (4) WebGPU on native desktop platforms should be considered experimental and mainly useful for debugging and benchmarking STEP BY STEP ============ --- Add a sokol_main() function to your code which returns a sapp_desc structure with initialization parameters and callback function pointers. This function is called very early, usually at the start of the platform's entry function (e.g. main or WinMain). You should do as little as possible here, since the rest of your code might be called from another thread (this depends on the platform): sapp_desc sokol_main(int argc, char* argv[]) { return (sapp_desc) { .width = 640, .height = 480, .init_cb = my_init_func, .frame_cb = my_frame_func, .cleanup_cb = my_cleanup_func, .event_cb = my_event_func, ... }; } To get any logging output in case of errors you need to provide a log callback. The easiest way is via sokol_log.h: #include "sokol_log.h" sapp_desc sokol_main(int argc, char* argv[]) { return (sapp_desc) { ... .logger.func = slog_func, }; } There are many more setup parameters, but these are the most important. For a complete list search for the sapp_desc structure declaration below. DO NOT call any sokol-app function from inside sokol_main(), since sokol-app will not be initialized at this point. The .width and .height parameters are the preferred size of the 3D rendering canvas. The actual size may differ from this depending on platform and other circumstances. Also the canvas size may change at any time (for instance when the user resizes the application window, or rotates the mobile device). You can just keep .width and .height zero-initialized to open a default-sized window (what "default-size" exactly means is platform-specific, but usually it's a size that covers most of, but not all, of the display). All provided function callbacks will be called from the same thread, but this may be different from the thread where sokol_main() was called. .init_cb (void (*)(void)) This function is called once after the application window, 3D rendering context and swap chain have been created. The function takes no arguments and has no return value. .frame_cb (void (*)(void)) This is the per-frame callback, which is usually called 60 times per second. This is where your application would update most of its state and perform all rendering. .cleanup_cb (void (*)(void)) The cleanup callback is called once right before the application quits. .event_cb (void (*)(const sapp_event* event)) The event callback is mainly for input handling, but is also used to communicate other types of events to the application. Keep the event_cb struct member zero-initialized if your application doesn't require event handling. As you can see, those 'standard callbacks' don't have a user_data argument, so any data that needs to be preserved between callbacks must live in global variables. If keeping state in global variables is not an option, there's an alternative set of callbacks with an additional user_data pointer argument: .user_data (void*) The user-data argument for the callbacks below .init_userdata_cb (void (*)(void* user_data)) .frame_userdata_cb (void (*)(void* user_data)) .cleanup_userdata_cb (void (*)(void* user_data)) .event_userdata_cb (void(*)(const sapp_event* event, void* user_data)) The function sapp_userdata() can be used to query the user_data pointer provided in the sapp_desc struct. You can also call sapp_query_desc() to get a copy of the original sapp_desc structure. NOTE that there's also an alternative compile mode where sokol_app.h doesn't "hijack" the main() function. Search below for SOKOL_NO_ENTRY. --- Implement the initialization callback function (init_cb), this is called once after the rendering surface, 3D API and swap chain have been initialized by sokol_app. All sokol-app functions can be called from inside the initialization callback, the most useful functions at this point are: int sapp_width(void) int sapp_height(void) Returns the current width and height of the default framebuffer in pixels, this may change from one frame to the next, and it may be different from the initial size provided in the sapp_desc struct. float sapp_widthf(void) float sapp_heightf(void) These are alternatives to sapp_width() and sapp_height() which return the default framebuffer size as float values instead of integer. This may help to prevent casting back and forth between int and float in more strongly typed languages than C and C++. double sapp_frame_duration(void) Returns the frame duration in seconds averaged over a number of frames to smooth out any jittering spikes. int sapp_color_format(void) int sapp_depth_format(void) The color and depth-stencil pixelformats of the default framebuffer, as integer values which are compatible with sokol-gfx's sg_pixel_format enum (so that they can be plugged directly in places where sg_pixel_format is expected). Possible values are: 23 == SG_PIXELFORMAT_RGBA8 28 == SG_PIXELFORMAT_BGRA8 42 == SG_PIXELFORMAT_DEPTH 43 == SG_PIXELFORMAT_DEPTH_STENCIL int sapp_sample_count(void) Return the MSAA sample count of the default framebuffer. const void* sapp_metal_get_device(void) const void* sapp_metal_get_current_drawable(void) const void* sapp_metal_get_depth_stencil_texture(void) const void* sapp_metal_get_msaa_color_texture(void) If the Metal backend has been selected, these functions return pointers to various Metal API objects required for rendering, otherwise they return a null pointer. These void pointers are actually Objective-C ids converted with a (ARC) __bridge cast so that the ids can be tunneled through C code. Also note that the returned pointers may change from one frame to the next, only the Metal device object is guaranteed to stay the same. const void* sapp_macos_get_window(void) On macOS, get the NSWindow object pointer, otherwise a null pointer. Before being used as Objective-C object, the void* must be converted back with a (ARC) __bridge cast. const void* sapp_ios_get_window(void) On iOS, get the UIWindow object pointer, otherwise a null pointer. Before being used as Objective-C object, the void* must be converted back with a (ARC) __bridge cast. const void* sapp_d3d11_get_device(void) const void* sapp_d3d11_get_device_context(void) const void* sapp_d3d11_get_render_view(void) const void* sapp_d3d11_get_resolve_view(void); const void* sapp_d3d11_get_depth_stencil_view(void) Similar to the sapp_metal_* functions, the sapp_d3d11_* functions return pointers to D3D11 API objects required for rendering, only if the D3D11 backend has been selected. Otherwise they return a null pointer. Note that the returned pointers to the render-target-view and depth-stencil-view may change from one frame to the next! const void* sapp_win32_get_hwnd(void) On Windows, get the window's HWND, otherwise a null pointer. The HWND has been cast to a void pointer in order to be tunneled through code which doesn't include Windows.h. const void* sapp_x11_get_window(void) On Linux, get the X11 Window, otherwise a null pointer. The Window has been cast to a void pointer in order to be tunneled through code which doesn't include X11/Xlib.h. const void* sapp_x11_get_display(void) On Linux, get the X11 Display, otherwise a null pointer. The Display has been cast to a void pointer in order to be tunneled through code which doesn't include X11/Xlib.h. const void* sapp_wgpu_get_device(void) const void* sapp_wgpu_get_render_view(void) const void* sapp_wgpu_get_resolve_view(void) const void* sapp_wgpu_get_depth_stencil_view(void) These are the WebGPU-specific functions to get the WebGPU objects and values required for rendering. If sokol_app.h is not compiled with SOKOL_WGPU, these functions return null. uint32_t sapp_gl_get_framebuffer(void) This returns the 'default framebuffer' of the GL context. Typically this will be zero. int sapp_gl_get_major_version(void) int sapp_gl_get_minor_version(void) bool sapp_gl_is_gles(void) Returns the major and minor version of the GL context and whether the GL context is a GLES context const void* sapp_android_get_native_activity(void); On Android, get the native activity ANativeActivity pointer, otherwise a null pointer. --- Implement the frame-callback function, this function will be called on the same thread as the init callback, but might be on a different thread than the sokol_main() function. Note that the size of the rendering framebuffer might have changed since the frame callback was called last. Call the functions sapp_width() and sapp_height() each frame to get the current size. --- Optionally implement the event-callback to handle input events. sokol-app provides the following type of input events: - a 'virtual key' was pressed down or released - a single text character was entered (provided as UTF-32 encoded UNICODE code point) - a mouse button was pressed down or released (left, right, middle) - mouse-wheel or 2D scrolling events - the mouse was moved - the mouse has entered or left the application window boundaries - low-level, portable multi-touch events (began, moved, ended, cancelled) - the application window was resized, iconified or restored - the application was suspended or restored (on mobile platforms) - the user or application code has asked to quit the application - a string was pasted to the system clipboard - one or more files have been dropped onto the application window To explicitly 'consume' an event and prevent that the event is forwarded for further handling to the operating system, call sapp_consume_event() from inside the event handler (NOTE that this behaviour is currently only implemented for some HTML5 events, support for other platforms and event types will be added as needed, please open a GitHub ticket and/or provide a PR if needed). NOTE: Do *not* call any 3D API rendering functions in the event callback function, since the 3D API context may not be active when the event callback is called (it may work on some platforms and 3D APIs, but not others, and the exact behaviour may change between sokol-app versions). --- Implement the cleanup-callback function, this is called once after the user quits the application (see the section "APPLICATION QUIT" for detailed information on quitting behaviour, and how to intercept a pending quit - for instance to show a "Really Quit?" dialog box). Note that the cleanup-callback isn't guaranteed to be called on the web and mobile platforms. MOUSE CURSOR TYPE AND VISIBILITY ================================ You can show and hide the mouse cursor with void sapp_show_mouse(bool show) And to get the current shown status: bool sapp_mouse_shown(void) NOTE that hiding the mouse cursor is different and independent from the MOUSE/POINTER LOCK feature which will also hide the mouse pointer when active (MOUSE LOCK is described below). To change the mouse cursor to one of several predefined types, call the function: void sapp_set_mouse_cursor(sapp_mouse_cursor cursor) Setting the default mouse cursor SAPP_MOUSECURSOR_DEFAULT will restore the standard look. To get the currently active mouse cursor type, call: sapp_mouse_cursor sapp_get_mouse_cursor(void) MOUSE LOCK (AKA POINTER LOCK, AKA MOUSE CAPTURE) ================================================ In normal mouse mode, no mouse movement events are reported when the mouse leaves the windows client area or hits the screen border (whether it's one or the other depends on the platform), and the mouse move events (SAPP_EVENTTYPE_MOUSE_MOVE) contain absolute mouse positions in framebuffer pixels in the sapp_event items mouse_x and mouse_y, and relative movement in framebuffer pixels in the sapp_event items mouse_dx and mouse_dy. To get continuous mouse movement (also when the mouse leaves the window client area or hits the screen border), activate mouse-lock mode by calling: sapp_lock_mouse(true) When mouse lock is activated, the mouse pointer is hidden, the reported absolute mouse position (sapp_event.mouse_x/y) appears frozen, and the relative mouse movement in sapp_event.mouse_dx/dy no longer has a direct relation to framebuffer pixels but instead uses "raw mouse input" (what "raw mouse input" exactly means also differs by platform). To deactivate mouse lock and return to normal mouse mode, call sapp_lock_mouse(false) And finally, to check if mouse lock is currently active, call if (sapp_mouse_locked()) { ... } Note that mouse-lock state may not change immediately after sapp_lock_mouse(true/false) is called, instead on some platforms the actual state switch may be delayed to the end of the current frame or even to a later frame. The mouse may also be unlocked automatically without calling sapp_lock_mouse(false), most notably when the application window becomes inactive. On the web platform there are further restrictions to be aware of, caused by the limitations of the HTML5 Pointer Lock API: - sapp_lock_mouse(true) can be called at any time, but it will only take effect in a 'short-lived input event handler of a specific type', meaning when one of the following events happens: - SAPP_EVENTTYPE_MOUSE_DOWN - SAPP_EVENTTYPE_MOUSE_UP - SAPP_EVENTTYPE_MOUSE_SCROLL - SAPP_EVENTTYPE_KEY_UP - SAPP_EVENTTYPE_KEY_DOWN - The mouse lock/unlock action on the web platform is asynchronous, this means that sapp_mouse_locked() won't immediately return the new status after calling sapp_lock_mouse(), instead the reported status will only change when the pointer lock has actually been activated or deactivated in the browser. - On the web, mouse lock can be deactivated by the user at any time by pressing the Esc key. When this happens, sokol_app.h behaves the same as if sapp_lock_mouse(false) is called. For things like camera manipulation it's most straightforward to lock and unlock the mouse right from the sokol_app.h event handler, for instance the following code enters and leaves mouse lock when the left mouse button is pressed and released, and then uses the relativeShowing the first 500 of 14048 diff lines for this file. This diff is INCOMPLETE; read the file or clone the repository for the rest.
vendor/sokol/sokol_glue.hdeleted
#if defined(SOKOL_IMPL) && !defined(SOKOL_GLUE_IMPL)#define SOKOL_GLUE_IMPL#endif#ifndef SOKOL_GLUE_INCLUDED/* sokol_glue.h -- glue helper functions for sokol headers Project URL: https://github.com/floooh/sokol Do this: #define SOKOL_IMPL or #define SOKOL_GLUE_IMPL before you include this file in *one* C or C++ file to create the implementation. ...optionally provide the following macros to override defaults: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) SOKOL_GLUE_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_GLUE_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) If sokol_glue.h is compiled as a DLL, define the following before including the declaration or implementation: SOKOL_DLL On Windows, SOKOL_DLL will define SOKOL_GLUE_API_DECL as __declspec(dllexport) or __declspec(dllimport) as needed. OVERVIEW ======== sokol_glue.h provides glue helper functions between sokol_gfx.h and sokol_app.h, so that sokol_gfx.h doesn't need to depend on sokol_app.h but can be used with different window system glue libraries. PROVIDED FUNCTIONS ================== sg_environment sglue_environment(void) Returns an sg_environment struct initialized by calling sokol_app.h functions. Use this in the sg_setup() call like this: sg_setup(&(sg_desc){ .environment = sglue_environment(), ... }); sg_swapchain sglue_swapchain(void) Returns an sg_swapchain struct initialized by calling sokol_app.h functions. Use this in sg_begin_pass() for a 'swapchain pass' like this: sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain(), ... }); LICENSE ======= zlib/libpng license Copyright (c) 2018 Andre Weissflog This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution.*/#define SOKOL_GLUE_INCLUDED#if defined(SOKOL_API_DECL) && !defined(SOKOL_GLUE_API_DECL)#define SOKOL_GLUE_API_DECL SOKOL_API_DECL#endif#ifndef SOKOL_GLUE_API_DECL#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GLUE_IMPL)#define SOKOL_GLUE_API_DECL __declspec(dllexport)#elif defined(_WIN32) && defined(SOKOL_DLL)#define SOKOL_GLUE_API_DECL __declspec(dllimport)#else#define SOKOL_GLUE_API_DECL extern#endif#endif#ifndef SOKOL_GFX_INCLUDED#error "Please include sokol_gfx.h before sokol_glue.h"#endif#ifdef __cplusplusextern "C" {#endifSOKOL_GLUE_API_DECL sg_environment sglue_environment(void);SOKOL_GLUE_API_DECL sg_swapchain sglue_swapchain(void);#ifdef __cplusplus} /* extern "C" */#endif#endif /* SOKOL_GLUE_INCLUDED *//*-- IMPLEMENTATION ----------------------------------------------------------*/#ifdef SOKOL_GLUE_IMPL#define SOKOL_GLUE_IMPL_INCLUDED (1)#include <string.h> /* memset */#ifndef SOKOL_APP_INCLUDED#error "Please include sokol_app.h before the sokol_glue.h implementation"#endif#ifndef SOKOL_API_IMPL#define SOKOL_API_IMPL#endif#ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) #define _SOKOL_PRIVATE __attribute__((unused)) static #else #define _SOKOL_PRIVATE static #endif#endif#ifndef SOKOL_ASSERT #include <assert.h> #define SOKOL_ASSERT(c) assert(c)#endif#ifndef SOKOL_UNREACHABLE #define SOKOL_UNREACHABLE SOKOL_ASSERT(false)#endif_SOKOL_PRIVATE sg_pixel_format _sglue_to_sgpixelformat(sapp_pixel_format fmt) { switch (fmt) { case SAPP_PIXELFORMAT_NONE: return SG_PIXELFORMAT_NONE; case SAPP_PIXELFORMAT_RGBA8: return SG_PIXELFORMAT_RGBA8; case SAPP_PIXELFORMAT_SRGB8A8: return SG_PIXELFORMAT_SRGB8A8; case SAPP_PIXELFORMAT_BGRA8: return SG_PIXELFORMAT_BGRA8; case SAPP_PIXELFORMAT_DEPTH_STENCIL: return SG_PIXELFORMAT_DEPTH_STENCIL; case SAPP_PIXELFORMAT_DEPTH: return SG_PIXELFORMAT_DEPTH; case SAPP_PIXELFORMAT_SBGRA8: // FIXME! default: SOKOL_UNREACHABLE; return SG_PIXELFORMAT_NONE; }}SOKOL_API_IMPL sg_environment sglue_environment(void) { sg_environment res; memset(&res, 0, sizeof(res)); const sapp_environment env = sapp_get_environment(); res.defaults.color_format = _sglue_to_sgpixelformat(env.defaults.color_format); res.defaults.depth_format = _sglue_to_sgpixelformat(env.defaults.depth_format); res.defaults.sample_count = env.defaults.sample_count; res.metal.device = env.metal.device; res.d3d11.device = env.d3d11.device; res.d3d11.device_context = env.d3d11.device_context; res.wgpu.device = env.wgpu.device; res.vulkan.physical_device = env.vulkan.physical_device; res.vulkan.device = env.vulkan.device; res.vulkan.queue = env.vulkan.queue; res.vulkan.queue_family_index = env.vulkan.queue_family_index; return res;}SOKOL_API_IMPL sg_swapchain sglue_swapchain(void) { sg_swapchain res; memset(&res, 0, sizeof(res)); const sapp_swapchain sc = sapp_get_swapchain(); res.width = sc.width; res.height = sc.height; res.sample_count = sc.sample_count; res.color_format = _sglue_to_sgpixelformat(sc.color_format); res.depth_format = _sglue_to_sgpixelformat(sc.depth_format); res.metal.current_drawable = sc.metal.current_drawable; res.metal.depth_stencil_texture = sc.metal.depth_stencil_texture; res.metal.msaa_color_texture = sc.metal.msaa_color_texture; res.d3d11.render_view = sc.d3d11.render_view; res.d3d11.resolve_view = sc.d3d11.resolve_view; res.d3d11.depth_stencil_view = sc.d3d11.depth_stencil_view; res.wgpu.render_view = sc.wgpu.render_view; res.wgpu.resolve_view = sc.wgpu.resolve_view; res.wgpu.depth_stencil_view = sc.wgpu.depth_stencil_view; res.vulkan.render_image = sc.vulkan.render_image; res.vulkan.render_view = sc.vulkan.render_view; res.vulkan.resolve_image = sc.vulkan.resolve_image; res.vulkan.resolve_view = sc.vulkan.resolve_view; res.vulkan.depth_stencil_image = sc.vulkan.depth_stencil_image; res.vulkan.depth_stencil_view = sc.vulkan.depth_stencil_view; res.vulkan.render_finished_semaphore = sc.vulkan.render_finished_semaphore; res.vulkan.present_complete_semaphore = sc.vulkan.present_complete_semaphore; res.gl.framebuffer = sc.gl.framebuffer; return res;}#endif /* SOKOL_GLUE_IMPL */