bevy icon indicating copy to clipboard operation
bevy copied to clipboard

Frame rate permanently drops overtime

Open ahillss opened this issue 2 years ago • 12 comments

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}");
}

ahillss avatar Jan 10 '24 12:01 ahillss

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.

pablo-lua avatar Jan 10 '24 18:01 pablo-lua

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(),  
)))```

ahillss avatar Jan 11 '24 01:01 ahillss

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)

pablo-lua avatar Jan 11 '24 02:01 pablo-lua

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;
}


ahillss avatar Jan 11 '24 06:01 ahillss

The problem doesn't seem to occur with only MinimalPlugins (after running for a day).

ahillss avatar Jan 12 '24 23:01 ahillss

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.

pablo-lua avatar Jan 12 '24 23:01 pablo-lua

Couldn't reproduce this on Linux (Wayland), so maybe it's something specific to Windows?

Friz64 avatar Jan 15 '24 12:01 Friz64

Or maybe it is something specific of this machine. Could you try maybe a cargo clean and retry to see if framerate drops?

pablo-lua avatar Jan 15 '24 12:01 pablo-lua

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.

ahillss avatar Jan 18 '24 22:01 ahillss

One possible test: Two winit Windows If the problem is in fact winit, this can shorten the time

pablo-lua avatar Jan 18 '24 22:01 pablo-lua

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.

ahillss avatar Jan 21 '24 02:01 ahillss

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.

ahillss avatar Jan 22 '24 13:01 ahillss

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.

ahillss avatar Jan 29 '24 05:01 ahillss

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.

pablo-lua avatar Jan 31 '24 22:01 pablo-lua

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.

pablo-lua avatar Feb 03 '24 22:02 pablo-lua

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).

ahillss avatar Feb 05 '24 06:02 ahillss

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.

ahillss avatar Feb 19 '24 14:02 ahillss