4 releases
| 0.1.3 | Sep 25, 2025 |
|---|---|
| 0.1.2 | Sep 25, 2025 |
| 0.1.1 | Sep 25, 2025 |
| 0.1.0 | Sep 24, 2025 |
#866 in Game dev
56 downloads per month
97KB
1.5K
SLoC
bevy_perf_hud


A configurable performance heads-up display (HUD) plugin for Bevy applications. Visualize frame pacing, entity counts, and resource usage in real time, with extensibility for your own metrics.
Table of Contents
- Features
- Installation
- Quick Start
- Advanced Configuration
- Built-in Metrics
- Custom Metrics
- Examples
- Performance Impact
- Troubleshooting
- Getting Help
- Supported Versions
- License
- Acknowledgements
Features
- Flexible HUD layout with multi-curve graphs and resource bars.
- Built-in providers for FPS, frame time, entity count, and system/process CPU & memory usage.
- Fine-grained control over smoothing, quantization, autoscaling, and appearance.
- Extensible
PerfMetricProvidertrait for custom metrics that appear alongside built-ins.
Installation
Minimum Supported Rust Version (MSRV): 1.76.0
Add the crate to your Cargo.toml:
[dependencies]
bevy = { version = "0.16", default-features = false, features = [
"bevy_winit",
"bevy_ui",
"bevy_render",
"bevy_diagnostic",
"sysinfo_plugin",
] }
bevy_perf_hud = "0.1"
Feature Flags
| Feature | Description | Default |
|---|---|---|
default |
Enables all standard functionality | ✓ |
Requirements
- Bevy Features: The HUD requires
bevy_ui,bevy_diagnostic, andbevy_renderfeatures - System Metrics: Add
sysinfo_pluginfeature for CPU/memory monitoring - Platform Support: Windows, macOS, Linux (system metrics may have limited functionality on some platforms)
Tip: If you use
DefaultPlugins, the required features are already enabled. Withoutsysinfo_plugin, system/process CPU & memory providers will be silently skipped.
Quick Start
use bevy::prelude::*;
use bevy_perf_hud::BevyPerfHudPlugin;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(BevyPerfHudPlugin)
.run();
}
By default the HUD appears near the top-right corner. To reposition or customize the layout, insert a PerfHudSettings
resource before adding the plugin:
use bevy::prelude::*;
use bevy_perf_hud::{BevyPerfHudPlugin, PerfHudSettings};
fn main() {
App::new()
.insert_resource(PerfHudSettings {
origin: Vec2::new(32.0, 32.0),
..default()
})
.add_plugins(DefaultPlugins)
.add_plugins(BevyPerfHudPlugin)
.run();
}
Advanced Configuration
PerfHudSettings exposes additional knobs for tailoring the HUD:
graph: adjust canvas size, curve smoothing, quantization, and decide which metrics appear in the time-series chart.bars: control whether resource bars render, set per-metric min/max bounds, and decide when to show numeric values.enabled/origin: toggle the HUD globally and anchor it anywhere on screen.
Example: expand the graph, smooth the FPS curve, and shrink the system CPU bar range.
use bevy::prelude::*;
use bevy_perf_hud::{BevyPerfHudPlugin, PerfHudSettings};
fn main() {
App::new()
.insert_resource({
let mut settings = PerfHudSettings::default();
settings.origin = Vec2::new(24.0, 24.0);
settings.graph.size = Vec2::new(360.0, 120.0);
if let Some(fps_curve) = settings
.graph
.curves
.iter_mut()
.find(|curve| curve.metric.id == "fps")
{
fps_curve.smoothing = Some(0.15);
}
// Modify the entity count bar to use auto-scaling
if let Some(entity_bar) = settings
.bars
.bars
.iter_mut()
.find(|bar| bar.metric.id == "entity_count")
{
entity_bar.scale_mode = bevy_perf_hud::BarScaleMode::Auto {
smoothing: 0.8, // Smooth range transitions
min_span: 100.0, // Minimum range of 100 entities
margin_frac: 0.2, // 20% margin for growth headroom
};
entity_bar.show_value = Some(true);
}
settings
})
.add_plugins(DefaultPlugins)
.add_plugins(BevyPerfHudPlugin)
.run();
}
Bar Scaling Modes
Performance bars can use different scaling modes to adapt their range dynamically:
Fixed Mode (Default)
Uses static min_value and max_value - traditional behavior with predictable, stable ranges.
use bevy_perf_hud::{BarConfig, BarScaleMode, MetricDefinition};
BarConfig {
metric: MetricDefinition { /* ... */ },
min_value: 0.0,
max_value: 100.0,
scale_mode: BarScaleMode::Fixed, // Uses min_value/max_value directly
// ...
}
Auto Mode
Automatically adjusts range based on historical data with smooth transitions:
BarConfig {
metric: MetricDefinition { /* ... */ },
min_value: 0.0, // Fallback if no data
max_value: 100.0, // Fallback if no data
scale_mode: BarScaleMode::Auto {
smoothing: 0.8, // Range change smoothing (0.0=instant, 1.0=never)
min_span: 10.0, // Minimum range span to prevent division by zero
margin_frac: 0.15, // Margin fraction above/below data (0.0-0.5)
},
min_limit: Some(0.0), // Hard minimum bound (optional)
max_limit: Some(500.0), // Hard maximum bound (optional)
// ...
}
Percentile Mode
Uses percentiles of recent samples - ideal for spiky data like latency:
BarConfig {
metric: MetricDefinition { /* ... */ },
min_value: 0.0, // Fallback if insufficient samples
max_value: 100.0, // Fallback if insufficient samples
scale_mode: BarScaleMode::Percentile {
lower: 5.0, // P5 percentile for minimum (ignores bottom 5%)
upper: 95.0, // P95 percentile for maximum (ignores top 5%)
sample_count: 60, // Number of recent samples to analyze
},
min_limit: Some(0.0), // Hard bounds prevent extreme outliers
max_limit: Some(1000.0),
// ...
}
Use Cases:
- Fixed: CPU/memory percentages, FPS with known limits
- Auto: Variable metrics like entity counts, memory usage in MB
- Percentile: Network latency, frame spikes, any metric with outliers
Built-in Metrics
| Metric ID | Description |
|---|---|
fps |
Frames per second (floored to an integer). |
frame_time_ms |
Smoothed frame time in milliseconds. |
entity_count |
Active entity count in the World. |
system/cpu_usage |
Overall system CPU usage percentage. |
system/mem_usage |
Overall system memory usage percentage. |
process/cpu_usage |
CPU usage of the running process. |
process/mem_usage |
Memory footprint of the running process (MiB). |
Custom Metrics
Implement the PerfMetricProvider trait and register it with the PerfHudAppExt helper:
Basic Example
use bevy::prelude::*;
use bevy_perf_hud::{PerfHudAppExt, PerfMetricProvider, MetricSampleContext};
#[derive(Default)]
struct NetworkLagProvider(f32);
impl PerfMetricProvider for NetworkLagProvider {
fn metric_id(&self) -> &str { "net/lag_ms" }
fn sample(&mut self, _ctx: MetricSampleContext) -> Option<f32> {
// Simulate network latency measurement
self.0 = (self.0 + 1.0) % 120.0;
Some(self.0)
}
}
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(bevy_perf_hud::BevyPerfHudPlugin)
.add_perf_metric_provider(NetworkLagProvider::default())
.run();
}
Advanced Example
Here's a more realistic example that tracks multiple game metrics:
use bevy::prelude::*;
use bevy_perf_hud::{PerfHudAppExt, PerfMetricProvider, MetricSampleContext, PerfHudSettings};
use std::collections::VecDeque;
// Track active player connections
#[derive(Resource, Default)]
struct GameStats {
active_players: u32,
packets_per_second: VecDeque<u32>,
last_update: f64,
}
#[derive(Default)]
struct PlayerCountProvider;
impl PerfMetricProvider for PlayerCountProvider {
fn metric_id(&self) -> &str { "game/players" }
fn sample(&mut self, ctx: MetricSampleContext) -> Option<f32> {
ctx.world.get_resource::<GameStats>()
.map(|stats| stats.active_players as f32)
}
}
#[derive(Default)]
struct NetworkThroughputProvider;
impl PerfMetricProvider for NetworkThroughputProvider {
fn metric_id(&self) -> &str { "net/packets_sec" }
fn sample(&mut self, ctx: MetricSampleContext) -> Option<f32> {
ctx.world.get_resource::<GameStats>()
.and_then(|stats| stats.packets_per_second.back().copied())
.map(|pps| pps as f32)
}
}
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.insert_resource(GameStats::default())
.insert_resource({
let mut settings = PerfHudSettings::default();
// Add our custom metrics to the HUD display
settings.graph.curves.push(bevy_perf_hud::GraphCurveSettings {
metric: bevy_perf_hud::MetricSettings {
id: "game/players".to_string(),
..default()
},
color: Color::srgb(0.2, 0.8, 0.2),
..default()
});
settings.bars.bars.push(bevy_perf_hud::BarSettings {
metric: bevy_perf_hud::MetricSettings {
id: "net/packets_sec".to_string(),
..default()
},
max_value: 1000.0,
..default()
});
settings
})
.add_plugins(bevy_perf_hud::BevyPerfHudPlugin)
.add_perf_metric_provider(PlayerCountProvider)
.add_perf_metric_provider(NetworkThroughputProvider)
.add_systems(Update, update_game_stats)
.run();
}
// System to update our custom metrics data
fn update_game_stats(
mut stats: ResMut<GameStats>,
time: Res<Time>,
// Your actual game systems would provide real data
) {
let now = time.elapsed_secs_f64();
if now - stats.last_update > 1.0 {
// Simulate some realistic game data
stats.active_players = (20.0 + 10.0 * (now * 0.1).sin()) as u32;
let pps = (500.0 + 200.0 * (now * 0.05).cos()) as u32;
stats.packets_per_second.push_back(pps);
if stats.packets_per_second.len() > 60 {
stats.packets_per_second.pop_front();
}
stats.last_update = now;
}
}
Custom Metric Guidelines
- Unique IDs: Use descriptive, hierarchical names like
"game/players"or"net/latency_ms" - Performance: Keep
sample()implementations fast - they're called every frame - Optional Values: Return
Nonewhen data isn't available rather than placeholder values - Units: Include units in the metric ID for clarity (
_ms,_mb,_percent)
Examples
The repository ships with several runnable examples:
examples/simple.rs: 3D scene with keyboard shortcuts (Space spawns cubes, F1 toggles HUD modes).examples/custom_metric.rs: Demonstrates registering an additional metric provider with auto-scaling.examples/bar_scaling_modes.rs: Shows all three bar scaling modes (Fixed, Auto, Percentile) in action.
Run them with:
cargo run --example simple
cargo run --example custom_metric
cargo run --example bar_scaling_modes
Performance Impact
The performance HUD is designed to have minimal impact on your application:
- CPU Usage: ~0.1-0.5% overhead on typical applications
- Memory Usage: ~2-4MB for storing historical data and UI components
- Render Cost: UI rendering typically adds <0.1ms to frame time
Optimization Tips:
- Reduce
history_samplesin graph settings for lower memory usage - Disable unused metrics by removing them from curves/bars configuration
- Use larger
update_intervalfor custom metrics that are expensive to sample - Consider disabling the HUD in release builds using feature flags
Troubleshooting
Common Issues
HUD not appearing:
- Ensure
bevy_uifeature is enabled in your Bevy dependency - Check that you're using
DefaultPluginsor have added the required UI plugins manually - Verify the HUD isn't positioned outside your window bounds
Missing system metrics (CPU/Memory):
- Add the
sysinfo_pluginfeature to your Bevy dependency - Without this feature, system/process metrics will be silently skipped
Poor performance with many entities:
- The
entity_countmetric can impact performance with 100k+ entities - Consider removing it from the HUD configuration for very large worlds
Custom metrics not updating:
- Ensure your
PerfMetricProvider::sample()method returnsSome(value) - Check that the provider is properly registered with
add_perf_metric_provider() - Verify the metric ID is unique and doesn't conflict with built-in metrics
Performance Debugging
If the HUD itself is causing performance issues:
// Temporarily disable to isolate performance problems
.insert_resource(PerfHudSettings {
enabled: false,
..default ()
})
Getting Help
- Issues: Report bugs or request features on GitHub Issues
- Discussions: Ask questions on GitHub Discussions
- Discord: Join our Discord server for real-time help
- Documentation: Detailed API docs are available on docs.rs
When reporting issues, please include:
- Your Bevy version
- Operating system and version
- Minimal code example that reproduces the problem
- Console output or error messages
Supported Versions
| bevy | bevy_perf_hud |
|---|---|
| 0.16 | 0.1.3 |
License
Dual-licensed under either the MIT License or Apache License 2.0.
Acknowledgements
- Bevy Engine for providing the ECS/game-engine foundation.
bevy_diagnosticandSystemInformationDiagnosticsPluginfor the metrics that power the HUD.
Looking for the Chinese documentation? See README_CN.md.
Dependencies
~80–125MB
~2M SLoC