Repository navigation
Live Editor Testing
Audience: Programmers | Mixed
Thread Context: N/A (development/process reference)
Platform/Backend Scope: Linux host, SDL2 + OpenGL (llvmpipe) primary; Vulkan (Lavapipe) fallback path documented
How to build, run, and test SparkEditor and the SparkEngine runtime with live graphics on Linux using Xvfb + Mesa software rendering — full GUI testing in headless/CI environments without a GPU.
The editor (SparkEditor) uses SDL2 + OpenGL 3.3 + ImGui on Linux. The engine runtime (SparkEngine) uses SDL2 for windowing and the OpenGL RHI backend. Both run on software rendering via Mesa's llvmpipe driver. A Vulkan path via Lavapipe also exists but falls back to OpenGL on most headless hosts (see below).
-
xvfb— virtual framebuffer X server -
libgl-dev— OpenGL development headers (must be installed before CMake configure) -
xdotool— X11 automation (mouse/keyboard simulation) -
python3-pillow— screenshot capture via Python
SDL2 is bundled as a git submodule at ThirdParty/SDL2 (tracks the upstream SDL2 branch via https://github.com/libsdl-org/SDL.git) and built automatically by CMake.
Critical: libgl-dev must be installed before running cmake -B build, otherwise SDL2 compiles without OpenGL/GLX support (SDL not configured with OpenGL/GLX support). If that happens, install libgl-dev, delete the build directory, and reconfigure.
Fallback when libgl-dev is unavailable (no network / sandbox): the repo bundles GL headers under ThirdParty/OpenGL/GL/. Copy them to the system include path and create the dev symlinks manually:
sudo cp ThirdParty/OpenGL/GL/*.h /usr/include/GL/
sudo ln -sf /usr/lib/x86_64-linux-gnu/libGL.so.1 /usr/lib/x86_64-linux-gnu/libGL.so
sudo ln -sf /usr/lib/x86_64-linux-gnu/libGLX.so.0 /usr/lib/x86_64-linux-gnu/libGLX.soThis works as long as the Mesa runtime libraries (libgl1, libglx-mesa0) are already installed (they usually are).
cmake -B build \
-DCMAKE_BUILD_TYPE=Debug \
-DENABLE_OPENGL=ON \
-DENABLE_SDL2=ON \
-DENABLE_EDITOR=ON \
-DSPARK_HEADLESS_SUPPORT=ON \
-DBUILD_TESTS=ON
cmake --build build --parallel $(nproc)# Start Xvfb
Xvfb :99 -screen 0 1920x1080x24 -ac &
# Environment
export DISPLAY=:99
export LIBGL_ALWAYS_SOFTWARE=1
export MESA_GL_VERSION_OVERRIDE=3.3
export GALLIUM_DRIVER=llvmpipe
# Editor (test mode skips the project browser)
./build/bin/SparkEditor --test-mode --debug-console
# Engine runtime
./build/bin/SparkEngine
# Automated live editor test suite
python3 tools/test-editor-live.py build/bin/SparkEditor-
--test-mode— skips the project-browser modal, enables debug console -
--test-frames N— exit after N frames (automated testing) -
--debug-console— print diagnostic output to stdout
tools/test-editor-live.py includes X11 screenshot capture via ctypes (no ImageMagick required).
-
SDL2 must have OpenGL/GLX support — if built before
libgl-dev, SDL2 compiles without GL. Rebuild SDL2 after installing the GL headers. -
Engine GLX bootstrap fails on llvmpipe — the OpenGL device's
CreateBootstrapContextused raw GLX, which fails on some software renderers. Fixed by detecting an existing SDL2 GL context (glXGetCurrentContext() != nullptr) and skipping the GLX bootstrap. -
Engine needs SDL GL attributes — added
SDL_GL_SetAttributecalls (GL 3.3 Core, depth 24, stencil 8) beforeSDL_CreateWindowinRunSDL2Windowed(), matching the editor. -
Engine needs an SDL GL context — added
SDL_GL_CreateContext+SDL_GL_MakeCurrentbeforeGraphicsEngine::Initialize, so the RHI can detect and reuse the existing context. -
GLXBadContext on shutdown — when SDL2 owns the GL context, GLDevice must not call
glXDestroyContext(). Fixed with anm_ownsGLXContextflag — only destroy the context if the engine created it (bootstrap path). -
Process pipe fd aliasing breaks stderr capture —
pipe()can allocate fds overlapping 0–2 when prior tests closed those fds, corruptingdup2redirection. Fixed withpipe2(O_CLOEXEC)and an alias-saferedirectFdhelper.
-
SparkEngine/Source/Core/SparkEngine.cpp— SDL GL attributes + context inRunSDL2Windowed() -
SparkEngine/Source/Graphics/RHI/OpenGL/OpenGLDevice.cpp/.h— detect existing GL context, skip GLX bootstrap, ownership-aware shutdown (m_ownsGLXContext) -
SparkEngine/Source/Utils/ProcessLinux.cpp—pipe2(O_CLOEXEC)+ safe fd redirect -
SparkEditor/Source/Core/EditorApplication.{h,cpp}—testMode/testFrameLimitinEditorConfig -
SparkEditor/Source/Core/EditorUI.cpp— skip project browser in test mode -
SparkEditor/Source/main.cpp—--test-mode/--test-framesCLI args
-
tools/test-editor-live.py— automated live editor test suite
On a headless gVisor host with Mesa Lavapipe, VulkanDevice::Initialize historically SIGSEGV'd (RC=139) shortly after selecting the llvmpipe software device. The root cause was traced under gdb to VulkanSwapChain::CreateSwapChain: VulkanDevice::CreateSwapChain only had a #ifdef _WIN32 surface-creation branch, so on Linux m_surface stayed VK_NULL_HANDLE and vkGetPhysicalDeviceSurfaceCapabilitiesKHR dereferenced a null surface. The Lavapipe ICD was blameless.
-
VulkanDevice.h— enable XCB + Xlib + Wayland surface macros on Linux so the Vulkan header pulls in all three surface-extension name sets. Also#undefXlib's unqualified macros (None,Status,Success,Bool,True,False,Always) that otherwise poison the engine (e.g.RHICullMode::None). -
VulkanDevice.cpp— enable the matching instance extensions when the ICD advertises them; add an#elif defined(SPARK_SDL2_AVAILABLE)branch callingSDL_Vulkan_CreateSurface; and bail early withnullptrwhendesc.windowHandleis null sovkGetPhysicalDeviceSurfaceCapabilitiesKHRis never called withVK_NULL_HANDLE. -
RHIBridge.cpp— fold swap-chain creation into the backend fallback loop. Previously aCreateSwapChain()returning nullptr abortedRHIBridge::Initializewith no retry. Now a swap-chain failure logsBackend 'X' failed to create swap chain — trying next, tears the device down, and loops to the next candidate — which is what lets OpenGL actually get tried when Vulkan can't make a surface.
The Vulkan path fails fast and cleanly (SDL_Vulkan_CreateSurface failed: The specified window isn't a Vulkan window — because RunSDL2Windowed creates the window with SDL_WINDOW_OPENGL, not SDL_WINDOW_VULKAN), RHIBridge falls back to OpenGL, and the engine boots via llvmpipe and runs 120 frames cleanly (RC=0). No more SIGSEGV.
To actually render via Vulkan on Linux, RunSDL2Windowed() would need to pick Vulkan as preferred at startup, create the SDL window with SDL_WINDOW_VULKAN, and SDL_Vulkan_LoadLibrary before instance creation. That is a separate, larger change; OpenGL/llvmpipe is the working headless-CI path today.
RHIBridge.cpp (GetAvailableBackends() / GetRecommendedBackend()) honors three env vars that drop a backend from the candidate list before the fallback loop runs — verified present in SparkEngine/Source/Graphics/RHI/RHIBridge.cpp:
| Env var | Effect |
|---|---|
SPARK_DISABLE_VULKAN=1 |
Vulkan dropped from the backend list |
SPARK_DISABLE_OPENGL=1 |
OpenGL dropped |
SPARK_DISABLE_D3D11=1 |
D3D11 dropped (Windows only) |
When a backend is dropped, RHIBridge logs SPARK_DISABLE_<NAME>=1 — <Name> backend skipped. With the swap-chain fallback fix above, SPARK_DISABLE_VULKAN=1 is no longer required for a crash-free boot — but it remains a clean, self-documenting, slightly faster way to force OpenGL. Passing backend=GraphicsBackend::None with a valid window handle still routes to NullRHIDevice; the env-var filter only affects GPU backends.
DISPLAY=:99 LIBGL_ALWAYS_SOFTWARE=1 GALLIUM_DRIVER=llvmpipe \
MESA_GL_VERSION_OVERRIDE=3.3 SPARK_DISABLE_VULKAN=1 SPARK_MAX_WORKER_THREADS=1 \
./SparkEngine -test-frames 120 -threads 1 -no-subprocess -window-size 1280x720- Mesa llvmpipe reports OpenGL 3.3 Core Profile with GLSL 4.50.
- The editor renders at a usable framerate (~30+ FPS) on software rendering.
-
xdotoolworks for basic menu interaction, but pixel-hunting ImGui buttons is unreliable — prefer--test-modefor automation. - The engine runtime shows a black window without a loaded scene — this is expected.
-
Original entry date: 2026-04-15 (
.claude/knowledge/live-editor-testing.md, type: Pattern), with two same-day updates (env-var escape hatch + Vulkan SIGSEGV root cause/fix) - Verified against codebase 2026-06-08.
-
VERIFIED present:
tools/test-editor-live.pyexists;SPARK_DISABLE_VULKAN/SPARK_DISABLE_OPENGL/SPARK_DISABLE_D3D11are all handled inSparkEngine/Source/Graphics/RHI/RHIBridge.cpp(andSPARK_DISABLE_VULKANis also referenced inSparkEngineLinux.cpp). SDL2 submodule confirmed atThirdParty/SDL2tracking the upstreamSDL2branch. - UPDATED: Reorganized the two 2026-04-15 session-update blocks (originally at the top as raw session logs) into a clean Vulkan-fallback section and an env-var section; stripped session-diary framing.
- FLAGGED — STALE counts: the original test-coverage figures (5660/5661 suite, "21 tests" in the live editor script) are 2026-04-15 snapshots; the unit-test suite has since grown past 6,000 tests, so those numbers are treated as historical and omitted from the body.
-
UNVERIFIED: the SDL2 pin
release-2.30.0in the original entry — the submodule now tracks theSDL2branch generically; the exact checked-out tag was not pinned-down here.
- MinGW-Wine-Cross-Compilation.md — the Wine/D3D11 counterpart for exercising Windows code on Linux
- AI-Bloat-Pattern.md — the wire-it-in-or-delete discipline behind the RHIBridge fallback work
- Clang-Format.md — formatting gate for the files modified here
Published from 2b03dc797148. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Platform Certification
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Fuzz Policy and Parser Security
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Crash Reporting
- Golden Image Testing
- Utilities
- Testing
- Fuzz Policy and Parser Security
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers