Skip to content

Architecture

MasterLaplace edited this page Jul 14, 2026 · 10 revisions

Architecture

Repository Layout

LplPlugin/
├── xmake.lua             # Root orchestrator
├── core/                 # lpl-core: Platform types, assertions, logging
├── math/                 # lpl-math: Vec3, Quat, Fixed32, CORDIC
├── memory/               # lpl-memory: PinnedAllocator, pools
├── concurrency/          # lpl-concurrency: ThreadPool, SpinLock
├── container/            # lpl-container: FlatAtomicsHashMap, Morton
├── gpu/                  # lpl-gpu: CUDA lifecycle, physics kernels
├── ecs/                  # lpl-ecs: EntityRegistry, Partition (SoA chunks)
├── physics/              # lpl-physics: WorldPartition, collision, octree
├── net/                  # lpl-net: transport, protocol, session, netcode
├── input/                # lpl-input: InputManager (keys, axes, neural)
├── platform/             # lpl-platform: HW backends (clock/display/gpu/input)
├── render/               # lpl-render: software + Vulkan renderer, ImGui
├── shaders/              # lpl-shaders: shader sources for the renderer
├── image/                # lpl-image: 2D software imaging (Surface, Painter, Codec)
├── scene/                # lpl-scene: scene graph (Scene, Transform2D)
├── audio/                # lpl-audio: Spatial audio (stub)
├── haptic/               # lpl-haptic: Haptic/vestibular (stub)
├── bci/                  # lpl-bci: OpenBCI, FFT, DSP, metrics, calibration
├── serial/               # lpl-serial: state serialization & deterministic replay
├── bench/                # lpl-bench: benchmark harness (Harness, SystemInfo)
├── engine/               # lpl-engine: Facade aggregating all modules
├── kernel/               # lpl_kmod.c (Netfilter + ring buffers)
├── apps/                 # lpl-server, lpl-client, lpl-benchmark
└── samples/              # example code

Dependency Graph

lpl-engine (facade — depends on every module; lpl-render only when --renderer is on)
├── lpl-core
├── lpl-math         # lpl-core
├── lpl-memory       # lpl-core
├── lpl-concurrency  # lpl-core
├── lpl-container    # lpl-core, lpl-memory, lpl-concurrency
├── lpl-gpu          # lpl-core, lpl-math, lpl-memory
├── lpl-ecs          # lpl-core, lpl-math, lpl-memory, lpl-container, lpl-concurrency, lpl-gpu
├── lpl-physics      # lpl-core, lpl-math, lpl-memory, lpl-container, lpl-ecs (+lpl-gpu)
├── lpl-net          # lpl-core, lpl-math, lpl-memory, lpl-container, lpl-concurrency, lpl-ecs
├── lpl-input        # lpl-core, lpl-math
├── lpl-platform     # lpl-core
├── lpl-render       # lpl-shaders, lpl-core, lpl-math, lpl-memory  (+ Vulkan, ImGui, glm, stb, tinyobjloader)
├── lpl-image        # lpl-core
├── lpl-scene        # lpl-core, lpl-math
├── lpl-audio        # lpl-core, lpl-math
├── lpl-haptic       # lpl-core, lpl-math
├── lpl-bci          # lpl-core, lpl-math, lpl-input, lpl-container (+ eigen, liblsl, brainflow)
├── lpl-serial       # lpl-core, lpl-math, lpl-ecs, lpl-net, lpl-input
└── lpl-bench        # lpl-core

Apps:
├── lpl-server       # lpl-engine
├── lpl-client       # lpl-engine
└── lpl-benchmark    # lpl-bench + lpl-core, lpl-math, lpl-memory, lpl-container, lpl-concurrency, lpl-ecs, lpl-physics

BCI Closed-Loop Pipeline

[OpenBCI Cyton — 8 ch / 250 Hz]
    ↓  USB serial — 33-byte packets
bci/src/source/ → OpenBCIDriver → BciRingBuffer (lock-free)
    ↓
bci/src/dsp/ — FFT per channel (Hann window, 256 pts) → PSD[8][128 bins]
    ↓
┌─────────────────────────────────────────────────────┐
│  SignalMetrics::schumacher()  →  R(t) 40-70 Hz      │  muscle tension
│  RiemannianGeometry::riemannian_distance() → δ_R    │  cognitive state
│  RiemannianGeometry::mahalanobis_distance() → D_M   │  anomaly detection
└─────────────────────────────────────────────────────┘
    ↓
NeuralMetrics::from_state() → { muscle_tension, stability, concentration }
    ↓
input/ InputManager → ECS ComponentID::NEURAL → SystemScheduler → WorldPartition

Core — Composition Architecture

The engine/ module provides the top-level Core class. It is the single entry point and owns all sub-systems by composition.

┌─────────────────── Core ───────────────────────┐
│                                                 │
│  WorldPartition  _world       (chunks, ECS)     │
│  Network         _network     (transport UDP)   │
│  SystemScheduler _scheduler   (DAG PreSwap/     │
│                                PostSwap)        │
│  InputManager    _inputManager (keys/axes/neural│
│                                 par entité)     │
│  PacketQueue     _packetQueue  (queues typées)  │
│  SessionManager  _sessionManager (serveur)      │
│                                                 │
│  run(threaded)      → boucle fixe 60Hz (serveur)│
│  runVariableDt(...)→ boucle variable (client)   │
│  initClientNetwork()→ connect au serveur        │
│  registerSystem()  → ajoute un SystemDescriptor │
│  buildSchedule()   → compile le DAG             │
│                                                 │
└─────────────────────────────────────────────────┘

Frame Pipeline

Chaque frame suit le pipeline PreSwap → swapBuffers → PostSwap :

1. PreSwap Systems (mutation du write buffer)
   ├─ NetworkReceive   : socket/ring → PacketQueue
   ├─ Session          : ConnectEvent → créer entité + MSG_WELCOME   (serveur)
   ├─ InputProcessing  : InputEvent → InputManager                   (serveur)
   ├─ Welcome          : WelcomeEvent → myEntityId                   (client)
   ├─ StateReconciliation : StateUpdateEvent → monde local           (client)
   ├─ BCI / LocalInput  : source BCI + input → InputManager          (client)
   ├─ Movement         : InputManager → vélocité (partagé srv/cli)
   └─ Physics          : world.step(dt)

2. swapBuffers() — copie hot write→read, toggle writeIdx

3. PostSwap Systems (lecture du read buffer stable)
   ├─ Broadcast        : sérialise + envoie MSG_STATE                (serveur)
   ├─ Camera           : suivi de l'entité locale                    (client)
   └─ Render           : Vulkan draw                                 (client)

Zero-Copy Pipeline (NIC ↔ GPU)

┌───────────── RX PATH (inbound) ─────────────┐
│                                             │
UDP Packet (port 7777)
    ↓
Netfilter Hook (kernel/lpl_kmod.c — NF_INET_PRE_ROUTING)
    ↓
RxRingBuffer (mmap'd LplSharedMemory, atomic head/tail)
    ↓
net/ Network::network_consume_packets() → dispatch to PacketQueue (typed events)
    ↓
Systems ECS (drain queues) → mutate WorldPartition (write buffer)
    ↓
physics/ WorldPartition::step()
    ├─ GPU path: CUDA kernel (PhysicsKernel.cu)
    └─ CPU path: physicsTick (current)
    ↓
Inter-chunk migration (backward iteration + swap-and-pop, both buffers)
    ↓
swapBuffers() (copy hot write→read, atomic toggle writeIdx)
│                                              │
└───────────── TX PATH (outbound) ─────────────┘
│                                              │
SessionManager::broadcast_state() → write TxPackets to TxRingBuffer
    ↓
ioctl(LPL_IOC_KICK_TX) → wake kernel TX thread
    ↓
lpl_tx_worker (kernel thread) → kernel_sendmsg() → UDP to clients

Chunk Memory Layout

Each chunk (1000×∞×1000 units) is a self-contained Partition in ecs/:

┌─────────────────────────────────────────────────┐
│                  Partition (Chunk)              │
├─────────────────────────────────────────────────┤
│  HOT DATA (double-buffered, PinnedAllocator)    │
│  ├─ positions[2]    : Vec3[]   (write/read)     │
│  ├─ velocities[2]   : Vec3[]   (write/read)     │
│  └─ forces[2]       : Vec3[]   (write/read)     │
├─────────────────────────────────────────────────┤
│  COLD DATA (single buffer, PinnedAllocator)     │
│  ├─ ids             : uint32_t[]                │
│  ├─ rotations       : Quat[]                    │
│  ├─ masses          : float[]                   │
│  ├─ sizes           : Vec3[]                    │
│  ├─ health          : int32_t[]                 │
│  └─ neuralControls  : NeuralControl[]           │
├─────────────────────────────────────────────────┤
│  METADATA                                       │
│  ├─ _idToLocal      : adaptive sparse set (O(1))│
│  ├─ _bound          : BoundaryBox               │
│  ├─ _locker         : SpinLock                  │
│  └─ _octree         : FlatDynamicOctree         │
└─────────────────────────────────────────────────┘

Double Buffering

Double buffering enables concurrent lock-free reads (reader↔writer):

  • Writers (physics, network) write to buf[writeIdx]
  • Readers (rendering, broadcast) read from buf[writeIdx ^ 1] (stable snapshot)
  • swapBuffers() copies the write buffer to the read buffer then toggles writeIdx
  • The SpinLock only protects structural mutations (writer↔writer): addEntity, removeEntityById, physicsTick that modify vector sizes

Client-Server Architecture

┌──────────────────┐         UDP 7777          ┌────────────────────────┐
│   Client         │ ────── MSG_CONNECT ─────→ │   Server (Core)        │
│   (lpl-client)   │ ───── MSG_INPUTS ───────→ │                        │
│                  │ ←───── MSG_WELCOME ────── │  ┌─ net/               │
│  PreSwap:        │ ←───── MSG_STATE ──────── │  ├─ input/             │
│  ├ NetworkRecv   │                           │  ├─ ecs/               │
│  ├ Welcome       │         /dev/lpl0         │  └─ engine/            │
│  ├ StateRecon    │ ──── (mmap shared mem) ── │                        │
│  ├ BCI+LocalInput│                           │  PreSwap:              │
│  ├ Movement      │                           │  ├ NetworkRecv         │
│  └ Physics       │                           │  ├ Session             │
│                  │                           │  ├ InputProcessing     │
│  PostSwap:       │                           │  ├ Movement            │
│  ├ Camera        │                           │  └ Physics             │
│  └ Render (Vk)   │                           │                        │
│                  │                           │  PostSwap:             │
│                  │                           │  ├ Broadcast           │
│                  │                           │  └ ServerMonitor       │
└──────────────────┘                           └────────────────────────┘

Protocol Messages

Message Format Description
MSG_CONNECT (0x10) [1B type] Client connection → player entity creation
MSG_WELCOME (0x11) [1B type][4B entityId] Server response with assigned ID
MSG_INPUTS (0x12) [1B type][4B entityId][KeyCount+Keys][AxisCount+Axes][optional 13B neural] Unified input packet (keys, axes, optional neural)
MSG_STATE (0x13) [1B type][2B count][{4B id, 12B pos, 12B size, 4B hp}×N] World state broadcast

← Philosophy & Principles | Next: Tech Stack & Build →

Clone this wiki locally