Git Product home page Git Product logo

bevy_framepace's Introduction

bevy_framepace⏱️

Framepacing and framelimiting for Bevy

crates.io docs.rs CI Bevy

app.2023-01-11.21-54-20.mp4

Usage

It's as simple as adding the plugin to your app:

app.add_plugins(bevy_framepace::FramepacePlugin);

By default, the plugin will automatically measure your framerate and use this for framepacing.

You can adjust the framerate limit at runtime by modifying theFramepaceSettings resource. For example, to set the framerate limit to 30fps:

settings.limiter = Limiter::from_framerate(30.0),

See demo.rs in the examples folder, or run with:

cargo run --release --example demo

How it works

The plugin works by recording how long it takes to render each frame, and sleeping the main thread until the desired frametime is reached. This ensures the next frame isn't started until the very last moment, delaying the event loop from restarting. By delaying the event loop, and thus input collection, this reduces motion-to-photon latency by moving reading input closer to rendering the frame.

The spin_sleep dependency is needed for precise sleep times. The sleep function in the standard library is not accurate enough for this application, especially on Windows.

Bevy Version Support

I intend to track the main branch of Bevy. PRs supporting this are welcome!

bevy bevy_framepace
0.13 0.15
0.12 0.14
0.11 0.13
0.10 0.12
0.9 0.7, 0.8, 0.9, 0.10, 0.11
0.8 0.5, 0.6
0.7 0.4
0.6 0.3

License

bevy_framepace is free, open source and permissively licensed! Except where noted (below and/or in individual files), all code in this repository is dual-licensed under either:

at your option. This means you can select the license you prefer! This dual-licensing approach is the de-facto standard in the Rust ecosystem and there are very good reasons to include both.

Your contributions

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

bevy_framepace's People

Contributors

aceeri avatar aevyrie avatar alexaegis avatar nhlest avatar paul-hansen avatar shanesveller avatar virxec avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bevy_framepace's Issues

Can't use the crate in headless mode

The crate needs a RenderApp to work, which is missing in headless mode.

Perhaps there is a way to remove the dependency on the RenderApp in the settings, or at least under feature?

[Bug] Panic when closing the window

bevy_framepace panics when closing the game's window. I'm on Windows 10.

[dependencies]
bevy = "0.8"
bevy_framepace = "0.5"
use bevy::prelude::*;
use bevy_framepace::FramepacePlugin;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugin(FramepacePlugin::default())
        .run();
}
PS C:\Users\Hayde\Documents\Development\bevy_test> cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.42s
     Running `target\debug\bevy_test.exe`
2022-08-01T23:20:59.485277Z  INFO bevy_render::renderer: AdapterInfo { name: "Radeon RX 570 Series", vendor: 4098, device: 26591, device_type: DiscreteGpu, backend: Vulkan }
2022-08-01T23:20:59.615713Z  INFO bevy_framepace: [Update] Detected refresh rate is: 60 fps
2022-08-01T23:20:59.676518Z  WARN bevy_framepace: [Frame Drop] 73.53ms (+56578.85μs)
2022-08-01T23:20:59.755949Z  WARN bevy_framepace: [Frame Drop] 27.63ms (+10683.25μs)
2022-08-01T23:21:00.918444Z  WARN bevy_framepace: [Frame Drop] 90.74ms (+73786.45μs)
2022-08-01T23:21:00.923103Z  INFO bevy_winit: Skipped event for closed window: WindowId(00000000-0000-0000-0000-000000000000)
2022-08-01T23:21:00.923727Z  INFO bevy_winit: Skipped event for closed window: WindowId(00000000-0000-0000-0000-000000000000)
2022-08-01T23:21:00.934491Z  INFO bevy_winit: Skipped event for closed window: WindowId(00000000-0000-0000-0000-000000000000)
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', C:\Users\Hayde\.cargo\registry\src\github.com-1ecc6299db9ec823\bevy_framepace-0.5.0\src\lib.rs:101:51
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\bevy_test.exe` (exit code: 101)

demo example is failing

Screen Shot 2022-03-21 at 17 22 12

Incase it matters, i'm on an m1 mac

INFO bevy_render::renderer: AdapterInfo { name: "Apple M1 Pro", vendor: 0, device: 0, device_type: DiscreteGpu, backend: Metal }

PID controller for sleep error

Attached is a patch that adds a PID controller. I found that the derivative factor adds stutter, but using proportional and integral factors improves the 'sleep error' (difference between target sleep time and actual sleep time) dramatically. The PID design is fundamentally different from the current controller, which is setting the sleep time equal to the previous error, whereas the PID controller tries to reduce that error to zero (which makes more sense to me).

  • The PID controller fixes the 3-5 FPS frame loss that the current design suffers from.
  • The PID controller does NOT appear compatible with vsync when 'delay waiting for a frame to present' is not incorporated. I noticed worse latency when vsync is enabled. It seems to be random luck that the current design works well with vsync.

I hacked the bevy render function to record frame presentation delays and incorporated those into the framepace app. Doing so resulted in extremely low latencies with vsync enabled (even better than the current code, afaict). It would require a pretty critical PR to update the core bevy render function in order to get timing info on the frame presentation step.

add_PID_controller.patch

Unnessary allocation in `Limiter` `Display`

Every branch allocates a String just to pass it to write!(). write!() supports all the normal formatting that println!() uses, so you can just use write!() in every branch.
It doesn't matter too much though, as its only really used in the demo app.

Unnecessary features in Cargo.toml

Cargo.toml lists render as a required feature which causes bevy to be built with a lot of unnecessary features like bevy_pbr and beby_gltf. This is unnecessary and can be simplified to only bevy_render.

App time variance

Framepacing adds a delay between the previous frame's presentation and the beginning of the current frame's update cycle. It calculates that delay based on the duration of the previous frame's update cycle.

If the current frame's update cycle takes longer than the previous frame, then the current frame's duration (from previous frame's presentation to current frame's presentation), will be longer than the target duration. This is a problem when vsync is enabled, because the current frame will miss the frame buffer refresh that comes after the previous frame's presentation step (the previous frame was 'just in time' for the buffer swap, but the current frame is 'just too late'). As a result, the previous frame will be presented twice. Depending on the vsync buffering strategy and how many frames are buffered, the current frame may be stuck on the frame presentation step all the way until the next buffer refresh.

Even very low variance in update cycle times can therefore lead to frequent frame drops on machines with unbuffered vsync. On machines with buffered vsync I think you will instead see occasional stutter based on desync between frame presentation and the time stamps of the beginning of update cycles.

Frame loss for unbuffered vsync can be solved by adding a 'buffer' to the framepace sleep based on the variance of app times (e.g. 2 standard deviations).

Since vsync is not always on, you'd need an additional framerate limiter just prior to the frame presentation step, which can absorb any remaining time to reach the target frametime. @aevyrie has observed that on machines with buffered vsync you get better results by targeting a slower framerate than the monitor refresh period -> a framerate limiter would allow compile-time configuration to achieve the best strategy for any given machine.

wasm support

I noticed this plugin doesn't work when compiled to wasm. This is because std::instant isn't supported on wasm aswell as the spin_sleep library. I was wondering if its possible to have a different method of frame pacing when compiling for web that doesn't use the spin_sleep library. so far replacing std::instant with the instant crate gets us part way there.

Bevy v0.10

I would like to use this plugin in Bevy v0.10.

Logic in get_display_refresh_rate is extremely convoluted

It feels like the match statement could be a much simpler if matches!(settings.frame_rate_limit, FrameRateLimitParam::Auto).

No significant logic is occurring elsewhere. Should be tackled after you merge FrameRateLimit and FrameRateLimitParam into a single type.

Don't use plugins as resources

Inserting the entire plugin as a resource is confusing and unidiomatic.

Instead:

  1. Merge FrameRateLimit and FrameRateLimitParam into a single type.
  2. Newtype WarnOnFrameDrop.
  3. Add FrameRateLimit and WarnOnFrameDrop as resources.

Store frame time, not frame rate

When used, we're taking the inverse every frame (and converting it to an f32).

Instead, store an f32 frame time in the FrameRateLimit resource (see #10) and use that directly to save on wasted work and reduce complexity.

Provider helper methods to allow users to specify the settings with either frame rates or frame times.

Refresh rate detection doesn't work when using multiple screens with different refresh rate

The logic here only reruns when the plugin settings or WinitWindows change, but I don't think that will happen just by dragging a window to another screen:

if !(settings.is_changed() || winit.is_changed()) {

This then causes bevy_framepace to always use the initial screen's refresh rate, even when the window is moved over to another screen with a different refresh rate later, effectively rendering the plugin ineffective on that screen.

If calling detect_frametime every frame is too much work, perhaps the library could be made to work without having an accurate refresh rate value, by measuring the time bevy spends waiting for VSync? In my own experiments, I've managed to measure this by timing the executing time of the prepare_windows system.

Thread parking

I coundn't help but notice this crate uses spinlocks while thread parking exists. Why not use thread parking instead?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.