![]() |
Lysa
0.0
Lysa 3D Engine
|
Lysa is organized as a set of loosely coupled subsystems, each exposed through a C++23 module interface (.ixx) paired with a .cpp implementation. The top-level module lysa re-exports all public interfaces and serves as the single include point for downstream code.
All GPU interaction is delegated to Vireo RHI : an external graphics abstraction layer. The engine never calls a graphics API (Vulkan, DirectX 12) directly
lysa::Lysa is the main entry class. It owns the application context, starts the main loop (run()), and coordinates the fixed-timestep physics update with the variable-timestep render frame.
lysa::Context is a singleton that aggregates every engine-wide service:
| Field | Role |
|---|---|
vireo | Vireo backend : device, instance, GPU resource factory |
fs | Virtual File System |
events | Centralized event dispatcher |
defer | Deferred command buffer (main-thread deferred tasks) |
threads | Async task pool (multi-threaded work) |
res | Resources registry (live resource tracking by type) |
samplers | Global GPU samplers |
graphicQueue | Submit queue for rendering work |
transferQueue | Submit queue for DMA transfers |
asyncQueue | Async transfer command submission |
physicsEngine | Pluggable physics backend (Jolt or PhysX, optional) |
lua | Lua scripting environment (optional) |
The context is accessed globally through the lysa::ctx() free function.
The main loop in lysa::Lysa::run follows a fixed-timestep accumulator pattern:
processPlatformEvents().MainLoopEvent::PHYSICS_PROCESS fired at a fixed delta time (default 1/60 s), potentially multiple times per frame to drain the accumulator.MainLoopEvent::PROCESS fired once with the remaining frame time.uploadData().MainLoopEvent::QUIT fired before resource teardown when Context::exit is set.Platform-specific code is isolated under src/platform/:
win32/ : Win32 API window, input, DirectoryWatcher, VFS root resolution.posix/ : POSIX file I/O and directory watching for Linux.sdl/ : SDL3 windowing and input (Linux only, controlled by USE_SDL3 CMake option).Engine code above this layer is platform-agnostic. Platform backends are selected at compile time through CMake.
src/VirtualFS.ixx provides URI-based path resolution for portable asset loading:
app:// : read-only application assets (typically bundled with the executable).user:// : user-writable data directory.lysa::ResourcesPack and lysa::AssetsPack layer packed file support on top of the VFS, allowing assets to be shipped as single archive files without changing any loading code.
All engine resources (meshes, images, materials, textures, samplers…) live in fixed-capacity pools managed by lysa::ResourcesManager :
unique_id.The lysa::ResourcesRegistry tracks all live resources by type and provides a single discovery point for cross-subsystem queries.
Dedicated manager types : ImageManager, MaterialManager, MeshManager : are instantiated by lysa::Lysa and own the corresponding GPU-side buffers.
src/Event.ixx provides a centralized observer-based dispatcher (lysa::EventManager).
All engine subsystems route their notifications through this system:
MainLoopEvent::PROCESS, PHYSICS_PROCESS, QUIT).Lua callbacks are registered through the same dispatcher, unifying C++ and script-side event handling.
The rendering stack is built from composable lysa::Renderpass objects. Each pass owns its attachments, pipeline state, and descriptor sets. Passes are assembled into a lysa::Renderer instance that drives the full frame.
Both rendering paths share the following passes:
| Pass | Description |
|---|---|
DepthPrepass | Early depth fill to reduce overdraw |
ShadowMapPass | Cascaded/cube shadow maps for directional & point lights |
TransparencyPass | Weighted Blended Order-Independent Transparency (WBOIT) |
ShaderMaterialPass | User-defined Slang shader materials |
BloomPass | Separable Gaussian bloom |
SMAAPass | Subpixel Morphological Anti-Aliasing |
FXAAPass | Fast Approximate Anti-Aliasing |
GammaCorrectionPass | Linear → sRGB conversion |
PostProcessingCompute | Compute-based post-processing chain (FXAA, SMAA, ACES, FSR…) |
lysa::ForwardRenderer with a single ForwardColorPass that writes directly to the color/depth attachments. Suitable for scenes with many unique materials. Shading (PBR, alpha-test) is computed per draw call in the fragment shader.
lysa::DeferredRenderer with a G-Buffer pipeline:
| Pass | Output |
|---|---|
GBufferPass | Position, Normal, Albedo, Emissive into separate attachments |
GTAOPass | Ground-Truth Ambient Occlusion |
SSAOPass | Screen-Space Ambient Occlusion |
LightingPass | Full-screen lighting resolve (PBR, shadow reads, AO) |
TAAPass | Temporal Anti-Aliasing |
FrameSharpeningPass | CAS sharpening after TAA |
Deferred rendering is selected by setting the DEFERRED_RENDERER CMake option (default ON).
Two compute passes run before the main color pass:
VkDrawIndexedIndirectCommand / D3D12 indirect draw arguments into a GPU buffer for use by the frustum culling passes.The main color passes consume these culled indirect draw buffers, keeping the CPU out of the per-draw loop.
lysa::Scene owns the list of lysa::MeshInstance objects, lights, and the environment settings. It computes per-frame lysa::SceneFrameData : GPU-ready view/projection matrices, instance transforms, material bindings, shadow map parameters : consumed by every render pass.
Shaders are written in Slang and compiled to both DXIL (DirectX 12) and SPIR-V (Vulkan) at build time by cmake/shaders.cmake. Compiled binaries ship under shaders/.
The math library in CPU code (src/depends/hlslpp/) uses the same HLSL-compatible types (float3, float4x4, …) as the shaders, eliminating type-conversion boilerplate at the CPU–GPU boundary.
The physics layer exposes a common interface (lysa::PhysicsEngine, src/physics/PhysicsEngine.ixx) implemented by two pluggable backends, selected exclusively at compile time:
src/physics/jolt/) : default backend (PHYSIC_ENGINE_JOLT=ON).src/physics/physx/) : alternative backend (PHYSIC_ENGINE_PHYSX=ON).Both backends expose:
A lysa::PhysicsDebugRenderer visualizes physics shapes and constraints in real time (enabled via DebugConfiguration in lysa::ContextConfiguration).
When compiled with LUA_BINDINGS=ON, the engine exposes its full public API to Lua via LuaBridge.
Lua callbacks integrate with the engine event system using the same EventManager::on() mechanism as C++ listeners, allowing hybrid C++/Lua classes.
| Component | Description |
|---|---|
AsyncTasksPool | Fixed thread pool for fire-and-forget parallel work |
AsyncQueue | Serialized async submission of GPU transfer commands |
DeferredTasksBuffer | Main-thread deferred command queue (execute-next-frame tasks) |
These three primitives cover the main concurrency patterns in the engine: CPU parallel work, background GPU uploads, and safe main-thread-deferred mutations of renderer state.
tools/blender/blender_lysa_addon.py) : exports scenes, meshes, materials, lights, and cameras directly into a Lysa-compatible format.tools/gen_pack_list.py) : produces asset pack manifests for lysa::ResourcesPack.| Library | Role | Location |
|---|---|---|
| Vireo RHI | Graphics abstraction (Vulkan / DX12) | External (VIREO_RHI_PROJECT_DIR) |
| Jolt Physics | Default physics backend | CMake FetchContent |
| NVIDIA PhysX | Optional physics backend | External (PHYSX_INCLUDE) |
| Slang | Shader language & compiler | System PATH |
| LuaBridge | Lua ↔ C++ bindings | CMake FetchContent |
| hlslpp | HLSL-compatible CPU math | Vendored (src/depends/hlslpp/) |
| nlohmann/json | JSON parsing | Vendored (src/depends/json/) |
| stb | Image loading/writing, TrueType | Vendored (src/depends/stb/) |
| SDL3 | Windowing on Linux | System / CMake FetchContent |