-
Notifications
You must be signed in to change notification settings - Fork 0
Modules
The LplPlugin engine is organized into 16 independent static libraries (lpl-<name>), each with its own xmake.lua, include/lpl/<name>/ headers, and src/ implementations. The engine/ module serves as the top-level facade that aggregates all others.
Build target naming: Each module compiles to
lpl-<name>(e.g.,lpl-core,lpl-math,lpl-ecs). Code style: SOLID principles, Doxygen/**@documentation,.inlinline files, guard clauses, no inline comments.
Target: lpl-core — No dependencies
Platform types, assertions, compile-time utilities, and logging foundations. This is the leaf dependency of the entire module graph.
Target: lpl-math → lpl-core
Vec3, Quat, BoundaryBox, Fixed32, CORDIC trigonometry — all without external dependencies (no GLM).
Features:
- All operators annotated
__host__ __device__(LPL_HD) for CUDA compatibility - Deterministic fixed-point arithmetic (
Fixed32) for cross-platform physics validation - Custom CORDIC implementations for
sin/cos/atan2
API:
-
Vec3:+,-,*,/,+=,dot(),cross() -
Quat:identity(),*(Hamilton),rotate(Vec3) -
BoundaryBox:contains(Vec3)— inclusion test for migration
Target: lpl-memory → lpl-core
PinnedAllocator: STL allocator using cudaHostAlloc (mapped + portable) so that std::vector buffers are directly GPU-accessible via PCIe zero-copy.
Fallback: Without nvcc, uses standard malloc/free.
Usage: All SoA vectors in Partition use PinnedAllocator<T>:
std::vector<Vec3, PinnedAllocator<Vec3>> _positions[2];Target: lpl-container → lpl-core, lpl-math
Lock-free hash map specialized for world chunk storage.
-
Contiguous pool: all
Partitionobjects live in a contiguous array (cache-friendly) -
64-bit atomic packed entries:
[Morton Key (42 bits) | Pool Index (22 bits)] - Open addressing: linear probing with tombstones
| Operation | Guarantee | Mechanism |
|---|---|---|
get() |
Wait-Free | Atomic read |
insert() |
Lock-Free | Atomic CAS + SpinLock for pool |
remove() |
Lock-Free | Atomic TOMBSTONE + slot recycling |
forEach() |
Thread-Safe | Copy _activeSlots under lock |
Encode 2D/3D coordinates into scalar keys preserving spatial locality (Z-order curve).
-
encode2D(x, y)→ 32-bit key (world chunks) -
encode3D(x, y, z)→ 63-bit key (octree) - Bias for negative coordinates:
+2²⁰offset
Target: lpl-concurrency → lpl-core
std::atomic_flag + _mm_pause() — RAII LocalGuard, ~100ns latency under short contention. One per chunk.
Fixed workers (hardware_concurrency()), supports enqueue (returns std::future) and enqueueDetached (fire-and-forget). Used by SystemScheduler and WorldPartition.
Target: lpl-ecs → lpl-core, lpl-math, lpl-memory, lpl-container, lpl-concurrency
Central registry — sparse set with generational IDs.
-
smartId = (generation << 14) | slot(18-bit gen + 14-bit slot) - O(1) lookup:
publicId → slot → chunkKey - Capacity: 10,000 simultaneous entities, 1,000,000 max public IDs
Per-chunk SoA container with double buffering, physics, and migration.
SoA Structure:
-
Hot data
[2](PinnedAllocator): positions, velocities, forces - Cold data: ids, rotations, masses, sizes, health, neuralControls
-
Metadata: adaptive sparse set
_idToLocal, BoundaryBox, SpinLock, FlatDynamicOctree
| Method | Description |
|---|---|
addEntity(snapshot) |
Thread-safe insertion into BOTH buffers |
removeEntityById(id, writeIdx) |
O(1) swap-and-pop on BOTH buffers |
physicsTick(dt, out, wIdx) |
Gravity + semi-implicit Euler + migration (CPU) |
syncBuffers(writeIdx) |
Copy hot data write→read (memcpy) |
setNeuralControl(id, ...) |
Update BCI data for an entity |
Full rebuild each frame. Internal radix sort (4 passes × 16-bit) on 3D Morton keys. Adaptive: brute-force N² for ≤32 entities, octree beyond.
Target: lpl-physics → lpl-ecs, lpl-math, lpl-container
Main orchestrator — manages chunks, migration, entity registry, and global double buffering.
step(deltatime) Flow:
forEach chunk → physicsTick (CPU, 2 passes forward+backward)
Final phase: Re-insert migrating entities (getOrCreateChunk + addEntity + updateChunkKey)
GC: Remove empty chunks
- Minimum penetration axis → collision normal
- Newton impulse with restitution (
e=0.5) - Iterative solver (4 complete passes)
- Auto wake on collision
Target: lpl-net → lpl-core, lpl-math, lpl-memory, lpl-container, lpl-concurrency, lpl-ecs
Three modes (résolution automatique) :
-
Kernel driver (production):
open("/dev/lpl0")→mmap(LplSharedMemory)→ zero-copy RX/TX - Socket fallback (serveur dev): socket UDP standard
-
Socket forcé (
-DLPL_USE_SOCKET, client/Android): socket UDP uniquement
| Method | Description |
|---|---|
network_init() |
Open driver (or create socket) |
network_consume_packets(queue) |
Drain RX ring/socket → PacketQueue
|
send_connect/welcome/inputs/state() |
Typed send methods |
Thread-safe typed queues: ConnectEvent, WelcomeEvent, StateUpdateEvent, InputEvent.
Handles client connections server-side. handleConnections() + broadcast_state().
Factory functions (Systems:: namespace) returning SystemDescriptor for the scheduler:
-
NetworkReceiveSystem,SessionSystem,InputProcessingSystem,MovementSystem,PhysicsSystem,WelcomeSystem,StateReconciliationSystem,BroadcastSystem
Target: lpl-gpu → lpl-core, lpl-math
CUDA lifecycle and physics kernel implementation. PhysicsKernel.cu handles gravity and semi-implicit Euler integration on SoA buffers.
Ported:
PhysicsGPU.cufrom legacy →PhysicsKernel.cu(SoA float arrays).
Target: lpl-input → lpl-core, lpl-math
Unified input state per entity (keys, axes, neural). Used on both server (authoritative from network) and client (local from input devices + BCI).
| Method | Description |
|---|---|
setKeyState(entityId, key, pressed) |
Update key state |
setNeural(entityId, alpha, beta, conc, blink) |
Update neural data |
computeMovementVelocity(entityId, ...) |
WASD → velocity + neural modulation |
Neural speed scale: concentration [0..1] → scale [0.70x .. 1.30x]. Blink jump with rising-edge detection.
Target: lpl-render → lpl-core, lpl-math, lpl-memory (+ vulkan-hpp, ImGui)
Vulkan rendering pipeline ported from the VkWrapper project. Includes:
- Vulkan device/instance/swapchain management
- Graphics pipeline setup
- ImGui integration (GLFW + Vulkan backend)
- Texture loading, model loading
Note: The Vulkan dependency is optional. Use
xmake f --renderer=nto completely disable Vulkan and GLFW for headless server builds.
Target: lpl-audio → lpl-core
Architecture stub for spatial audio integration. Not yet implemented.
Target: lpl-haptic → lpl-core
Architecture stub for haptic/vestibular feedback (GVS, tFUS). Not yet implemented.
Target: lpl-bci → lpl-core, lpl-math, lpl-input
Merged module combining the adapter layer (src/bci/) and the full DSP pipeline from plugins/bci/. Organized into sub-directories:
| Sub-directory | Content |
|---|---|
src/source/ |
OpenBCI driver, BrainFlow, LSL, CSV sources |
src/dsp/ |
FFT, windowing, PSD extraction |
src/metric/ |
SignalMetrics (Schumacher), StabilityMetric |
src/math/ |
Covariance, Riemannian geometry |
src/stream/ |
LSL outlet |
src/openvibe/ |
OpenViBE box algorithm stubs |
tests/ |
24 unit tests (SignalMetrics + RiemannianGeometry) |
Key APIs:
- Serial capture: 33-byte packets, 8 channels, 250 Hz
- 24-bit ADS1299 parsing with sign extension
- FFT (256 pts, Hann window) → PSD per channel
- Concentration ratio (
Beta / (Alpha + Beta)) with EMA smoothing - Eye blink detection via thresholding
-
schumacher()—$R(t) = \frac{1}{N_{ch}} \sum_{i=1}^{N_{ch}} \int_{40}^{70} PSD_i(f,t), df$ — muscle tension -
integrale()— discrete PSD integration -
sliding_window_rms()— RMS over sliding window -
compute_baseline()— mean + σ calibration
-
compute_covariance()— SPD matrix with Bessel correction -
riemannian_distance()—$\delta_R = \sqrt{\sum_i \ln^2(\lambda_i)}$ — cognitive state change -
mahalanobis_distance()—$D_M$ anomaly detection - Jacobi eigenvalue decomposition — no external dependency
-
from_state()— normalized struct withmuscle_alertthreshold
Note: Several sources (
BrainFlowSource,LslSource,OpenBciSource) and advanced math (Covariance,Riemannian) are excluded from the build pending Eigen/Boost/liblsl/BrainFlow integration.
Target: lpl-serial → lpl-core
Serial port abstraction for USB communication (used by BCI driver for /dev/ttyUSB0).
Target: lpl-engine → ALL other modules
Top-level facade that depends on all 15 other modules. Provides the Core class — the single entry point for the engine.
Core owns by composition:
-
WorldPartition,Network,SystemScheduler,InputManager,PacketQueue,SessionManager
Game loops:
-
run(threaded)— fixed 60Hz server loop -
runVariableDt(computeDt, onPostLoop)— variable client loop
Lifecycle: Constructor → registerSystem() → buildSchedule() → run() → stop() → Destructor
ECS scheduler with automatic component dependency analysis (DAG) and two execution phases:
- PreSwap: mutation of write buffer (network, inputs, physics)
- PostSwap: reading of stable read buffer (broadcast, render, camera)
Systems declaring the same write component are serialized; reads are parallelized.
Not an xmake target — compiled separately via kernel build system.
Bidirectional zero-copy network gateway:
RX Path: Netfilter hook NF_INET_PRE_ROUTING → skb_copy_bits() → RxPacket → NF_DROP
TX Path: kernel thread lpl_tx_worker → kernel_sendmsg() → UDP
Shared Memory (LplSharedMemory):
-
mmap()on char device/dev/lpl0 -
vmalloc_user()allocation -
RxRingBuffer rx(4096 slots) +TxRingBuffer tx(4096 slots) - Atomic
head/tail+ cache-line padding
Shared structures between kernel (C) and userspace (C++23):
| Structure | Description |
|---|---|
RingHeader |
head + tail + cache-line padding |
RxPacket |
src_ip + src_port + length + data[256]
|
TxPacket |
dst_ip + dst_port + length + data[256]
|
LplSharedMemory |
RxRingBuffer rx + TxRingBuffer tx
|
ComponentID |
TRANSFORM, HEALTH, VELOCITY, MASS, SIZE, NEURAL
|
ioctl: LPL_IOC_KICK_TX — wakes the kernel TX thread.
Messages: MSG_CONNECT (0x10), MSG_WELCOME (0x11), MSG_INPUT (0x12), MSG_STATE (0x13)
← Tech Stack & Build | Next: Architectural Decisions →
LplPlugin — Zero-Copy Real-Time VR Engine | Author: MasterLaplace | License: GPL-3.0 | Source Code
This project is a marathon, not a sprint.