Skip to main content

Display Timing System

Monolex uses an adaptive timing system that measures actual WebKit rendering performance and adjusts the entire backend accordingly. This provides optimal efficiency under varying system loads.

Overview

┌─────────────────────────────────────────────────────────────────┐
│  ADAPTIVE TIMING ARCHITECTURE                                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Hardware Display (120Hz/60Hz/30Hz)                             │
│       │                                                         │
│       ▼                                                         │
│  WebKit measureWebKitFps()                                      │
│       │ Measures actual rAF callback rate                       │
│       │                                                         │
│       ▼                                                         │
│  invoke("set_measured_fps", 30)                                 │
│       │                                                         │
│       ▼                                                         │
│  Rust DisplayTiming                                             │
│       │ effective_hz.store(30)                                  │
│       │                                                         │
│       ▼                                                         │
│  All frame-based timing adapts:                                 │
│       • tick_interval = frames(3) = 100ms                       │
│       • sync_deadline = frames(1) = 33ms                        │
│       • resize_settle = frames(6) = 200ms                       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Why Adaptive Timing?

WebKit’s requestAnimationFrame rate varies based on system conditions:
ConditionTypical FPSCause
Normal60 fpsStandard operation
System load30 fpsCPU contention
Background tab1 fpsBrowser throttling
ProMotion120 fpsHigh refresh display
Without adaptive timing, the backend would continue operating at 60Hz even when WebKit can only render at 30fps, wasting CPU cycles.

Two Control Layers

Monolex uses two complementary mechanisms to optimize rendering:
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                                                                         │
│   OUTER CONTROL (Hz)                    │    INNER CONTROL (ACK)                        │
│   ══════════════════════════════════════│════════════════════════════════════════════   │
│                                         │                                               │
│   Location: Rust backend                │    Location: TypeScript frontend              │
│                                         │                                               │
│   Mechanism:                            │    Mechanism:                                 │
│     tick_interval = frames(3)           │      waiting_ack flag                         │
│     60Hz → 50ms                         │      true → block emit                        │
│     30Hz → 100ms                        │      false → allow emit                       │
│                                         │                                               │
│   What it controls:                     │    What it controls:                          │
│     • tick() frequency                  │      • emit() permission                      │
│     • pull() frequency                  │      • Prevents TS overload                   │
│     • hash computation frequency        │                                               │
│     • Overall render attempt rate       │                                               │
│                                         │                                               │
│   Cost saved:                           │    Cost saved:                                │
│     • Rust CPU cycles                   │      • TS CPU cycles                          │
│     • tokio scheduler overhead          │      • decode() calls                         │
│                                         │      • inject() calls                         │
│                                         │      • WebGL render calls                     │
│                                         │                                               │
│   When it helps:                        │    When it helps:                             │
│     System under load                   │      Frontend busy processing                 │
│     → WebKit RAF slows down             │      → ACK delayed                            │
│     → Hz detection measures 30fps       │      → waiting_ack stays true                 │
│     → tick_interval becomes 100ms       │      → emit blocked until ACK                 │
│     → Rust does less work               │      → Frontend not overwhelmed               │
│                                         │                                               │
│   Proactive:                            │    Reactive:                                  │
│     "Slow down before hitting wall"     │      "Stop when wall is hit"                  │
│                                         │                                               │
└─────────────────────────────────────────────────────────────────────────────────────────┘

Hz Detection (Outer Control)

Controls how often the backend attempts to render:
  • Measures WebKit FPS using requestAnimationFrame
  • Updates effective_hz atomically in Rust
  • All frame-based durations scale automatically
┌─────────────────────────────────────────────────────────────────┐
│  FRAME-BASED TIMING CALCULATION                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   "Wait for 3 frames"                                           │
│        │                                                        │
│        ├── @ 60Hz: 3 × 16.7ms = 50ms                           │
│        │                                                        │
│        └── @ 30Hz: 3 × 33.3ms = 100ms                          │
│                                                                 │
│   Result: Same "3 frames" adapts to actual display speed        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

ACK Gating (Inner Control)

Controls whether each emit is allowed:
  • Frontend sends ACK after processing each frame
  • Backend blocks next emit until ACK received
  • Prevents frontend overload
┌─────────────────────────────────────────────────────────────────┐
│  ACK GATING LOGIC                                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Is frontend still processing?                                 │
│        │                                                        │
│        ├── YES → Wait (don't send new frame)                   │
│        │                                                        │
│        └── NO → Send update, then wait for confirmation        │
│                                                                 │
│   This prevents overwhelming the display                        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Cost Savings

When Hz drops from 60 to 30:
Component60Hz30HzReduction
tick() calls/sec201050%
pull() calls/sec201050%
emit() calls/sec~10~10Same
Rust CPU usage100%~50%50%
The key insight: both mechanisms produce the same emit rate (~10/sec at 30fps), but Hz detection reduces backend work while ACK gating prevents frontend overload.

Measurement Process

┌─────────────────────────────────────────────────────────────────┐
│  FPS MEASUREMENT PROCESS                                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Step 1: Count 60 screen refreshes                             │
│           │                                                     │
│           ▼                                                     │
│   Step 2: Measure how long it took                              │
│           │                                                     │
│           ▼                                                     │
│   Step 3: Calculate: 60 ÷ time = FPS                            │
│           │                                                     │
│           ▼                                                     │
│   Result: Actual display performance (30, 45, 60, 120 fps)      │
│                                                                 │
│   Example:                                                      │
│   ─────────                                                     │
│   60 frames in 1000ms → 60 fps (normal)                         │
│   60 frames in 2000ms → 30 fps (system under load)              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Adaptive Intervals

  • Normal (≥30 fps): Measure every 3 seconds
  • Low (< 30 fps): Measure every 1 second

Self-Regulating System

The timing system creates a natural feedback loop:
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                                                                         │
│   SELF-REGULATING FEEDBACK LOOP                                                         │
│                                                                                         │
│   System Load ↑                                                                         │
│        │                                                                                │
│        ▼                                                                                │
│   WebKit FPS ↓ (60 → 30)                                                                │
│        │                                                                                │
│        ▼                                                                                │
│   DisplayTiming measures drop                                                           │
│        │                                                                                │
│        ▼                                                                                │
│   tick_interval increases (50ms → 100ms)                                                │
│        │                                                                                │
│        ▼                                                                                │
│   Rust does less work                                                                   │
│        │                                                                                │
│        ▼                                                                                │
│   System Load ↓                                                                         │
│        │                                                                                │
│        ▼                                                                                │
│   (cycle continues)                                                                     │
│                                                                                         │
└─────────────────────────────────────────────────────────────────────────────────────────┘
This self-regulation prevents the terminal from contributing to system overload.

How It Works

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                                                                         │
│   DISPLAY TIMING SYSTEM                                                                 │
│                                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                                 │  │
│   │   FRONTEND (Measures Display)                                                   │  │
│   │   ────────────────────────────                                                  │  │
│   │                                                                                 │  │
│   │   ┌────────────┐    ┌────────────┐    ┌────────────┐                           │  │
│   │   │ Count 60   │───▶│ Calculate  │───▶│ Report to  │                           │  │
│   │   │ frames     │    │ FPS        │    │ Backend    │                           │  │
│   │   └────────────┘    └────────────┘    └─────┬──────┘                           │  │
│   │                                             │                                   │  │
│   │   Repeats every 1-3 seconds                 │                                   │  │
│   │                                             │                                   │  │
│   └─────────────────────────────────────────────┼───────────────────────────────────┘  │
│                                                 │                                      │
│                                                 ▼                                      │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                                 │  │
│   │   BACKEND (Adapts Timing)                                                       │  │
│   │   ────────────────────────                                                      │  │
│   │                                                                                 │  │
│   │   ┌────────────┐    ┌────────────┐    ┌────────────┐                           │  │
│   │   │ Receive    │───▶│ Cap at     │───▶│ Update all │                           │  │
│   │   │ FPS value  │    │ 20-120 Hz  │    │ timers     │                           │  │
│   │   └────────────┘    └────────────┘    └────────────┘                           │  │
│   │                                                                                 │  │
│   │   Safety: Never below 20fps, never above display max                            │  │
│   │                                                                                 │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                         │
└─────────────────────────────────────────────────────────────────────────────────────────┘

Why Both Are Needed

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                                                                         │
│  Hz Detection alone:                                                                    │
│  ───────────────────                                                                    │
│  • Reduces tick frequency based on MEASURED display rate                                │
│  • But if measurement is wrong, could overwhelm frontend                                │
│  • Needs ACK as safety net                                                              │
│                                                                                         │
│  ACK alone:                                                                             │
│  ──────────                                                                             │
│  • Prevents frontend overload (backpressure)                                            │
│  • But Rust still does unnecessary work (wasted ticks)                                  │
│  • Needs Hz Detection to reduce base rate                                               │
│                                                                                         │
│  Together:                                                                              │
│  ─────────                                                                              │
│  • Hz Detection = "Don't try too often" (proactive)                                     │
│  • ACK = "Don't send if not ready" (reactive)                                           │
│  • Optimal efficiency with safety fallback                                              │
│                                                                                         │
│  ═══════════════════════════════════════════════════════════════════════════════════    │
│                                                                                         │
│  Hz Detection: "Know beforehand, run slower" (proactive rate adaptation)                │
│  ACK Only:     "Run fast, wait when blocked" (reactive backpressure)                    │
│                                                                                         │
└─────────────────────────────────────────────────────────────────────────────────────────┘

Key Points

Hz Detection

Controls Rust backend tick rate. Proactive optimization that reduces work before overload.

ACK Gating

Protects frontend from overload. Reactive backpressure that blocks when busy.

Lock-Free

Uses AtomicU32 for thread-safe, lock-free Hz sharing across async tasks.

Self-Regulating

Creates natural feedback loop that prevents terminal from contributing to system load.