Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Performance Budgets

This document defines frame time allocations for AstraWeave games targeting 60 FPS.

Target Specification

MetricValueNotes
Target FPS60Standard for action games
Frame Budget16.67 ms1000ms ÷ 60
Headroom Target20%3.33 ms reserved for spikes
Available Budget13.34 ms80% of frame

Budget Allocation

SystemBudget (ms)PercentageNotes
Game Logic2.012%ECS, AI, gameplay
Physics2.515%Simulation, collision
Rendering6.036%Draw calls, GPU sync
Audio0.53%Spatial audio, mixing
Networking1.06%Sync, prediction
UI0.53%HUD, menus
I/O0.53%Streaming, saves
Headroom3.6722%Spike absorption
Total16.67100%

Visual Breakdown

Frame Budget: 16.67ms
├── Game Logic ████░░░░░░░░░░░░░░░░ 2.0ms (12%)
├── Physics    █████░░░░░░░░░░░░░░░ 2.5ms (15%)
├── Rendering  ████████████░░░░░░░░ 6.0ms (36%)
├── Audio      █░░░░░░░░░░░░░░░░░░░ 0.5ms (3%)
├── Networking ██░░░░░░░░░░░░░░░░░░ 1.0ms (6%)
├── UI         █░░░░░░░░░░░░░░░░░░░ 0.5ms (3%)
├── I/O        █░░░░░░░░░░░░░░░░░░░ 0.5ms (3%)
└── Headroom   ███████░░░░░░░░░░░░░ 3.67ms (22%)

Game Logic Budget (2.0 ms)

Sub-Allocation

ComponentBudget (µs)Notes
ECS core (1k entities)850.51% of frame
AI updates (500 agents)471GOAP + arbiter
Gameplay systems500Combat, quests, dialogue
Script execution300Rhai scripts
Event processing200Input, triggers
Total1,55622% headroom within budget

Scaling Guidelines

Entity CountECS BudgetAI AgentsFeasibility
1,00085 µs500✅ Comfortable
5,000529 µs1,000✅ Within budget
10,0001 ms2,000⚠️ Near limit
20,000+>2 ms❌ Consider 30 FPS

Physics Budget (2.5 ms)

Sub-Allocation

ComponentBudget (µs)Notes
Rigid body simulation500100 bodies
Character controllers200Player + NPCs
Collision detection800Spatial hash accelerated
Raycasts300AI, weapons, cameras
Triggers/sensors200Zone detection
Total2,00020% headroom

Collision Budget

With spatial hashing (99.96% check reduction):

ObjectsNaiveWith HashSavings
1004,950 checks2 checks99.96%
1,000499,500 checks~20 checks99.996%
10,00049.9M checks~200 checks99.9996%

Rendering Budget (6.0 ms)

Sub-Allocation

StageBudget (ms)Notes
Culling0.5Frustum, occlusion
Shadow passes1.0CSM, spot shadows
G-Buffer1.5Deferred geometry
Lighting1.0Clustered, IBL
Post-processing1.0Bloom, SSAO, TAA
UI overlay0.5HUD, debug
GPU sync0.5Fence waiting
Total6.0

Draw Call Budget

CategoryCallsNotes
Opaque geometry500With instancing
Transparent100Sorted, no batching
Shadows200Per cascade
UI50Batched
Total850Target maximum

Audio Budget (0.5 ms)

Sub-Allocation

ComponentBudget (µs)Notes
Voice mixing2004-bus mixer
Spatial positioning1503D audio
DSP effects100Reverb, filters
Stream decode50Music, ambient
Total500

Monitoring Budget Compliance

Runtime Checks

#![allow(unused)]
fn main() {
use std::time::Instant;

struct FrameBudget {
    logic_budget: Duration,
    physics_budget: Duration,
    render_budget: Duration,
}

impl FrameBudget {
    fn check(&self, phase: &str, elapsed: Duration, budget: Duration) {
        if elapsed > budget {
            warn!(
                "{} exceeded budget: {:?} > {:?} ({}%)",
                phase,
                elapsed,
                budget,
                (elapsed.as_micros() * 100 / budget.as_micros())
            );
        }
    }
}
}

Tracy Zones

#![allow(unused)]
fn main() {
#[cfg(feature = "profiling")]
{
    astraweave_profiling::zone!("GameLogic");
    // ... game logic ...
}
}

30 FPS Budget (Alternative)

For complex simulations or lower-end hardware:

MetricValue
Frame Budget33.33 ms
Headroom6.67 ms (20%)
Available26.66 ms

When to use 30 FPS:

  • Open worlds with >20k entities
  • Complex physics simulations
  • Lower-end hardware targets
  • Cinematic experiences

Budget Violations

Severity Levels

ViolationDurationResponse
Minor1-2 framesLog, continue
Moderate3-10 framesReduce quality
Severe>10 framesEmergency LOD

Automatic Quality Scaling

#![allow(unused)]
fn main() {
// Reduce quality on budget overrun
if frame_time > Duration::from_millis(18) {
    quality.reduce_shadow_resolution();
}
if frame_time > Duration::from_millis(20) {
    quality.reduce_draw_distance();
}
if frame_time > Duration::from_millis(25) {
    quality.disable_post_processing();
}
}

See Also