From 122939fdf2bc7cdcb78340bc2f0eeaf3e563b5da Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 1 Mar 2026 14:44:19 -0800 Subject: [PATCH] raylib: add PLATFORM_OFFSCREEN support (#38) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * raylib: add PLATFORM_OFFSCREEN support for headless CI rendering - build.sh: use commaai/raylib platform-offscreen commit, build offscreen variant (libraylib_offscreen.a) alongside desktop for x86_64, bundle GLVND dispatchers (libEGL, libOpenGL, libGLdispatch) in install/lib/mesa/ - build.py: add PLATFORM_OFFSCREEN elif — uses offscreen lib + bundled GLVND with RPATH, links -lOpenGL -lEGL instead of -lGL -lX11 - __init__.py: auto-rebuild CFFI when RAYLIB_PLATFORM env changes (tracks built platform in .raylib_platform marker file) - Bump version to 5.5.0.5 This lets openpilot CI render headlessly via EGL surfaceless + Mesa llvmpipe without needing xvfb or any additional apt packages. Co-Authored-By: Claude Opus 4.6 * raylib: auto-detect CI and use PLATFORM_OFFSCREEN __init__.py now auto-detects CI on Linux x86_64 and uses PLATFORM_OFFSCREEN + LIBGL_ALWAYS_SOFTWARE=1 automatically. No env vars needed in the consuming CI workflow. Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- raylib/build.sh | 36 ++++++++++++++++++++++++++++++++++-- raylib/pyproject.toml | 2 +- raylib/raylib/__init__.py | 38 ++++++++++++++++++++++++++++++++++++-- raylib/raylib/build.py | 11 +++++++++++ raylib/raylib/version.py | 2 +- 5 files changed, 83 insertions(+), 6 deletions(-) diff --git a/raylib/build.sh b/raylib/build.sh index 47931c5..702ad7e 100755 --- a/raylib/build.sh +++ b/raylib/build.sh @@ -33,6 +33,15 @@ if [[ "$(uname)" == "Linux" ]]; then sudo apt-get update && sudo apt-get install -y libdrm-dev libgbm-dev libgles2-mesa-dev libegl1-mesa-dev || true fi fi + elif [ "$RAYLIB_PLATFORM" = "PLATFORM_OFFSCREEN" ]; then + # offscreen (CI): needs EGL/GL dev packages (no X11) + if command -v apt-get &>/dev/null; then + if [ "$(id -u)" -eq 0 ]; then + apt-get update && apt-get install -y libegl-dev libgl-dev + else + sudo apt-get update && sudo apt-get install -y libegl-dev libgl-dev + fi + fi else # desktop: needs X11/GL dev packages if command -v dnf &>/dev/null; then @@ -48,10 +57,10 @@ if [[ "$(uname)" == "Linux" ]]; then fi # Clone and build raylib C library -RAYLIB_COMMIT="aa6ade09ac4bfb2847a356535f2d9f87e49ab089" +RAYLIB_COMMIT="d9d7cc1353ec0f73c97e84ddf0973983d1ee25e2" if [ ! -d "raylib-src" ]; then - git clone -b master --no-tags https://github.com/commaai/raylib.git raylib-src + git clone -b platform-offscreen --no-tags https://github.com/commaai/raylib.git raylib-src fi cd raylib-src @@ -71,6 +80,29 @@ mkdir -p "$INSTALL_DIR"/{lib,include} cp raylib-src/src/libraylib.a "$INSTALL_DIR/lib/" cp raylib-src/src/raylib.h raylib-src/src/raymath.h raylib-src/src/rlgl.h "$INSTALL_DIR/include/" +# On x86_64 Linux, also build the offscreen variant for CI headless rendering +if [[ "$(uname)" == "Linux" && "$(uname -m)" == "x86_64" && "$RAYLIB_PLATFORM" != "PLATFORM_OFFSCREEN" ]]; then + echo "Building offscreen variant..." + cd raylib-src/src + make clean + make -j"$NJOBS" PLATFORM=PLATFORM_OFFSCREEN + cp libraylib.a "$INSTALL_DIR/lib/libraylib_offscreen.a" + cd "$DIR" + + # Bundle GLVND dispatchers so offscreen rendering works without extra system packages + MESA_DIR="$INSTALL_DIR/lib/mesa" + mkdir -p "$MESA_DIR" + for lib in libEGL.so.1 libOpenGL.so.0 libGLdispatch.so.0; do + src="$(ldconfig -p 2>/dev/null | grep "$lib" | grep 'x86-64' | awk '{print $NF}' | head -1)" + if [ -n "$src" ] && [ -f "$src" ]; then + cp -L "$src" "$MESA_DIR/" + # Create unversioned symlink for the linker + base="${lib%%.so.*}" + ln -sf "$lib" "$MESA_DIR/${base}.so" + fi + done +fi + # Download raygui header RAYGUI_COMMIT="76b36b597edb70ffaf96f046076adc20d67e7827" curl -fsSLo "$INSTALL_DIR/include/raygui.h" \ diff --git a/raylib/pyproject.toml b/raylib/pyproject.toml index ec51175..154b5ca 100644 --- a/raylib/pyproject.toml +++ b/raylib/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "raylib" -version = "5.5.0.4" +version = "5.5.0.5" description = "raylib + pyray Python bindings (commaai fork)" requires-python = ">=3.8" dependencies = ["cffi>=1.17.1"] diff --git a/raylib/raylib/__init__.py b/raylib/raylib/__init__.py index ecd2eec..cbcc4cc 100644 --- a/raylib/raylib/__init__.py +++ b/raylib/raylib/__init__.py @@ -1,26 +1,60 @@ import os +import platform as _platform DIR = os.path.join(os.path.dirname(__file__), "install") LIB_DIR = os.path.join(DIR, "lib") INCLUDE_DIR = os.path.join(DIR, "include") +def _detect_platform(): + """Auto-detect the raylib platform. In CI on Linux x86_64, use offscreen EGL rendering.""" + explicit = os.environ.get("RAYLIB_PLATFORM", "") + if explicit: + return explicit + if os.environ.get("CI") and _platform.system() == "Linux" and _platform.machine() == "x86_64": + return "PLATFORM_OFFSCREEN" + return "" + + def smoketest(): assert os.path.isfile(os.path.join(LIB_DIR, "libraylib.a")), "libraylib.a not found" assert os.path.isfile(os.path.join(INCLUDE_DIR, "raylib.h")), "raylib.h not found" -# Build CFFI extension on first import if not already compiled +# Build CFFI extension on first import if not already compiled, +# or rebuild if the target platform changed since last build. def _ensure_cffi_built(): import glob import subprocess import sys pkg_dir = os.path.dirname(__file__) - if not glob.glob(os.path.join(pkg_dir, "_raylib_cffi*")): + platform_marker = os.path.join(pkg_dir, ".raylib_platform") + requested = _detect_platform() + + # Export so build.py picks it up + if requested: + os.environ["RAYLIB_PLATFORM"] = requested + # Mesa llvmpipe for software rendering in headless CI + if requested == "PLATFORM_OFFSCREEN": + os.environ.setdefault("LIBGL_ALWAYS_SOFTWARE", "1") + + cffi_files = glob.glob(os.path.join(pkg_dir, "_raylib_cffi*")) + + # Rebuild if platform changed + if cffi_files and requested: + built_for = open(platform_marker).read().strip() if os.path.isfile(platform_marker) else "" + if built_for != requested: + for f in cffi_files: + os.remove(f) + cffi_files = [] + + if not cffi_files: build_script = os.path.join(pkg_dir, "build.py") if os.path.isfile(build_script) and os.path.isfile(os.path.join(LIB_DIR, "libraylib.a")): try: subprocess.check_call([sys.executable, build_script], cwd=os.path.dirname(pkg_dir)) + with open(platform_marker, "w") as f: + f.write(requested) except subprocess.CalledProcessError: pass diff --git a/raylib/raylib/build.py b/raylib/raylib/build.py index 46bf823..74590f3 100644 --- a/raylib/raylib/build.py +++ b/raylib/raylib/build.py @@ -104,6 +104,17 @@ def build_ffi(): if RAYLIB_PLATFORM == "PLATFORM_COMMA": extra_link_args.remove('-lGL') extra_link_args += ['-lGLESv2', '-lEGL', '-lgbm', '-ldrm'] + elif RAYLIB_PLATFORM == "PLATFORM_OFFSCREEN": + # Use offscreen variant if available, otherwise fall back to default + offscreen_lib = os.path.join(RAYLIB_LIB_PATH, 'libraylib_offscreen.a') + if os.path.isfile(offscreen_lib): + extra_link_args[extra_link_args.index('-lraylib')] = '-lraylib_offscreen' + extra_link_args.remove('-lGL') + # Use bundled GLVND dispatchers if available, with RPATH for runtime + mesa_dir = os.path.join(RAYLIB_LIB_PATH, 'mesa') + if os.path.isdir(mesa_dir): + extra_link_args += [f'-L{mesa_dir}', f'-Wl,-rpath,$ORIGIN/install/lib/mesa'] + extra_link_args += ['-lOpenGL', '-lEGL'] else: extra_link_args += ['-lX11'] extra_compile_args = ["-Wno-incompatible-pointer-types"] diff --git a/raylib/raylib/version.py b/raylib/raylib/version.py index 819d349..0d240d9 100644 --- a/raylib/raylib/version.py +++ b/raylib/raylib/version.py @@ -1 +1 @@ -__version__ = "5.5.0.2" \ No newline at end of file +__version__ = "5.5.0.5" \ No newline at end of file