Frame rate permanently drops overtime
Bevy version
0.12.1
[Optional] Relevant system information
- Windows 10
- cargo 1.76.0-nightly (2c03e0e2d 2023-11-16)
- tested on two machines : i5, 16gb mem, gtx 970 and i5, 8gb mem, gtx 670
What you did
Created a test program displaying the framerate, and left it running for 8+ hours.
What went wrong
Was expecting framerate to stay at 60 fps. But framerate would decrease over time, down to about 15fps and never recover.
Additional information
Framerate decreases faster when:
- more going on (eg lots of entities/components, rendering)
- when running another program that has high cpu/gpu usage at the same time as the bevy app
With a barebones bevy app, it can take up to a day or possibly longer for the framerate to decrease.
In my bevy app, the menus are noticeably laggy to interact with after the framerate has dropped to 50-45 fps.
Test code
Cargo.toml :
[package]
name = "hello"
version = "0.1.0"
edition = "2021"
[dependencies]
bevy = "0.12.1"
rust-toolchain.toml :
[toolchain]
channel = "nightly"
main.rs :
use bevy::diagnostic::{FrameTimeDiagnosticsPlugin, DiagnosticsStore};
use bevy::prelude::*;
fn main() {
let mut app = App::new();
app
.add_plugins(( bevy::prelude::DefaultPlugins, FrameTimeDiagnosticsPlugin::default(), ))
.add_systems(Update, ( show_fps_in_title, )) ;
app.run();
}
fn show_fps_in_title( mut windows: Query<&mut Window>, diagnostics: Res<DiagnosticsStore>, ) {
let Ok(mut window) = windows.get_single_mut() else { return; };
let (fps,avg,smoothed) = diagnostics.get(FrameTimeDiagnosticsPlugin::FPS)
.map(|x|(x.value().unwrap_or_default(),x.average().unwrap_or_default(),x.smoothed().unwrap_or_default()))
.unwrap_or_default();
window.title = format!("{fps:.0} {avg:.0} {smoothed:.0}");
}
Something that can help here: Does let the App running with MinimalPlugins make the FPS lower too? Maybe we can narrow the major problem a bit Though can get hard to see the FPS without some Plugins.
Yeah it's a bit hard to test fps without a window or rendering. Alternately you could test the time it takes to loop.
Here's all the default plugins, with as many commented out as possible. I'm testing it now to see if the problem still occurs.
.add_plugins(((
// bevy::log::LogPlugin::default(),
bevy::core::TaskPoolPlugin::default(),
// bevy::core::TypeRegistrationPlugin,
bevy::core::FrameCountPlugin,
bevy::time::TimePlugin,
// bevy::transform::TransformPlugin,
// bevy::hierarchy::HierarchyPlugin,
// bevy::diagnostic::DiagnosticsPlugin,
bevy::input::InputPlugin,
bevy::window::WindowPlugin::default(),
bevy::a11y::AccessibilityPlugin,
bevy::asset::AssetPlugin::default(),
// bevy::scene::ScenePlugin,
bevy::winit::WinitPlugin::default(),
bevy::render::RenderPlugin::default(),
bevy::render::texture::ImagePlugin::default(),
),(
// bevy::render::pipelined_rendering::PipelinedRenderingPlugin,
// bevy::core_pipeline::CorePipelinePlugin,
// bevy::sprite::SpritePlugin,
// bevy::text::TextPlugin,
// bevy::ui::UiPlugin,
// bevy::pbr::PbrPlugin::default(),
// bevy::gltf::GltfPlugin::default(),
// bevy::audio::AudioPlugin::default(),
// bevy::gilrs::GilrsPlugin,
// bevy::animation::AnimationPlugin,
// bevy::gizmos::GizmoPlugin,
FrameTimeDiagnosticsPlugin::default(),
)))```
Well, to be fair, we could remove the winit and the window by printing in the console the framerate, we could filter frames using the FrameCount and do the check-up only at 10k frames and see if the FPS goes down any further. (Something like that)
fn checkup(frame_count: Res<FrameCounter>, diagnostic: Res<DiagnosticStore>) {
if frame_count.0 % 10000 == 0 {
//Only will check on every 10k in order to lower possible problems that the println can have
}
}
If even in this case, the fps goes down, maybe something is going with the World or the App themselves. If nothing happens, maybe winit itself could be the problem (or some other thing)
The previous test with the window/rendering and removed plugins still has the issue.
I am testing it with the MinimalPlugins now.
It loops 60 times a second and prints the framerate every 10 seconds to console.
use std::time::Duration;
use bevy::time::common_conditions;
use bevy::app::ScheduleRunnerPlugin;
use bevy::diagnostic::{FrameTimeDiagnosticsPlugin, DiagnosticsStore};
use bevy::prelude::*;
fn main() {
let mut app = App::new();
app
.add_plugins((
MinimalPlugins.set(ScheduleRunnerPlugin::run_loop(Duration::from_secs_f64(1.0 / 60.0,))),
FrameTimeDiagnosticsPlugin::default(),
))
.add_systems(Update, ( print_fps.run_if( common_conditions::on_timer(Duration::from_secs(10))), )) ;
app.run();
}
fn print_fps( diagnostics: Res<DiagnosticsStore>, mut c:Local<u64> ) {
let (fps,avg,smoothed) = diagnostics.get(FrameTimeDiagnosticsPlugin::FPS)
.map(|x|(x.value().unwrap_or_default(),x.average().unwrap_or_default(),x.smoothed().unwrap_or_default()))
.unwrap_or_default();
println!("{}: {fps:.0} {avg:.0} {smoothed:.0}",*c);
(*c)+=1;
}
The problem doesn't seem to occur with only MinimalPlugins (after running for a day).
We can probably rule out the World, the App and everything that is within the MinimalPlugins. My guess is that the bevy_winit is at fault here, but not sure why.
Couldn't reproduce this on Linux (Wayland), so maybe it's something specific to Windows?
Or maybe it is something specific of this machine. Could you try maybe a cargo clean and retry to see if framerate drops?
I've had the problem occur on two different win 10 machines, and tried running cargo clean.
It can take quite a while to occur, I'll try come up with a better test to make it occur sooner.
One possible test: Two winit Windows If the problem is in fact winit, this can shorten the time
We can probably rule out the World, the App and everything that is within the MinimalPlugins. My guess is that the bevy_winit is at fault here, but not sure why.
One possible test: Two winit Windows
If the problem is in fact winit, this can shorten the time
I'll give that a try.
It's going to take a bit of time, for some reason the tests have been taking much longer (days instead of hours) for the problem occur.
I noticed something while putting together the multiple windows test, if one window visible and the rest are minimised, the visible window's frame rate drops until the others are made visible again.
The problem seems to occur more quickly as well.
I ran the multiple windows without rendering, for a couple of days, I didn't notice any framerate loss. But it can be hard to tell since the framerate without rendering enabled is all over the place.
For the multiple windows with rendering enabled, the issue did occur much more quickly than a single window. I believe when the windows are minimised the issue will occur much sooner (Within 5 hours with the windows minimised).
Not sure where to go from there.
Maybe someone can try confirm the problem with the multiple windows with rendering enabled test? Minimise the 4 windows and leaving it for a couple of hours and then maximise them and check the fps.
I think we have pinpointed that the problem is in fact the Window, I'll have tests on this field as soon as possible and return here.
I ran the test, and it absolutely looks like the problem is happening here. After about 20 minutes, the average fps went from 30 to 20. Another thing worth mentioning is that the memory usage went from 30 to 100 after these minutes, it seems that there is some kind of leak happening. Might be worth checking on this in 0.13, as winit was updated, but maybe some kind of unsafe function is running when isn't safe at all, and the problem can either be in bevy_window or bevy_winit.
I hadn't notice the memory leakage.
I tested it with window_render, it seems to happen when a window is minimised, and the leak rate increases for each additional minimised window. I also tested with a single window app, which also leaked.
I tested with the window_norender test, and didn't find any memory leaks.
So the memory leak has to be related to bevy_render?
Also the frame rate loss issue still occurs even when the windows aren't minimised (and the memory isn't leaking).
I updated the examples for 0.13.
The frame rate loss issue still persists, but the memory seems to leak much slower now.
I also noticed after the frame rate has deteriorated (and the windows not minimised), the memory usage seems to jump around between 40-60 megs. I wonder if something is repeatedly being created and destroyed.