-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
move time update to after everything #4728
Conversation
Wow, way smoother! |
crates/bevy_app/src/app.rs
Outdated
@@ -56,6 +56,8 @@ pub struct App { | |||
pub runner: Box<dyn Fn(App)>, | |||
/// A container of [`Stage`]s set to be run in a linear order. | |||
pub schedule: Schedule, | |||
/// stage run after main schedule and sub app schedules | |||
pub stage_very_last: SystemStage, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe final
, rather than very_last
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm down for that. very_last
was my 🚲 🏠 name.
crates/bevy_app/src/app.rs
Outdated
@@ -326,6 +330,15 @@ impl App { | |||
self.add_system_to_stage(CoreStage::Update, system) | |||
} | |||
|
|||
/// adds a system to the very last stage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// adds a system to the very last stage | |
/// Adds a system to the very last stage in the [`Schedule`] | |
/// | |
/// [`Time`] is updated during [`CoreSystem::TIme`] in this stage: try to make sure any system that you add runs before it in the schedule to ensure the smoothest possible frame timing. |
Can you note that this also closes #4691 in the PR description? |
This is more of a fix for when vsync is on than when vsync is off like in that issue. |
crates/bevy_app/src/app.rs
Outdated
@@ -115,6 +118,7 @@ impl App { | |||
for sub_app in self.sub_apps.values_mut() { | |||
(sub_app.runner)(&mut self.world, &mut sub_app.app); | |||
} | |||
self.stage_very_last.run(&mut self.world); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should consider bumping time management as a concern into the "app runner"? It seems like what we really want is "very first" (as in pre-input event processing for a given update), not "very last".
Conceptually "starting the stopwatch at the beginning of a frame" feels easier to reason about and ensures we don't have multiple frames with zero elapsed time.
Although I'm not convinced winit will make that switch easy on us. Processing input after deciding to present seems desirable, but they abstract that out from us.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we want "very first". Ideally we'd be setting the time as close to when the frame is presented as possible. Which means during the render stage. For example, there might be a future where people are inserting systems after the frame is presented like bevy_framepace
is doing. (This positioning doesn't actual work for frame pacing in pipelined rendering. It actually does want to be before input processing. But it's late and I'm having trouble coming up with something that would actually be inserted after frame presentation.) This hypothetical system could cause the same timing issues that we're currently seeing with input processing.
I did consider adding the time update to after app.update in the event handler, but any other runners would need to know to add the call there as well.
When we do have pipelined rendering I suspect that we'll want to run the time update system right after the frame presentation in the render stage and then move the time into the app world during extract. This won't currently work very well because that would cause the time to be for the N-2 frame. But in pipelined rendering it would be for the N-1 frame, where N is the current frame. Ideally ideally we'd be getting the frame time from hardware, but that requires api support that isn't fully supported on all hardware/os's/wgpu yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose we can move towards something more like the future with pipelines rendering by adding another extract stage at the end of the rendering schedule. This stage would logically be merged with the other one once we have pipelined rendering.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When we do have pipelined rendering I suspect that we'll want to run the time update system right after the frame presentation in the render stage and then move the time into the app world during extract.
This would also be my suggestion based on my experimenting with bevy_framepace
. However, instead of using extract we could try using a globally shared Arc<RwLock>
, or use a channel.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, instead of using extract we could try using a globally shared Arc, or use a channel.
A channel sounds like a good idea. I'll try that.
closing in favor of #4744 |
# Objective - The time update is currently done in the wrong part of the schedule. For a single frame the current order of things is update input, update time (First stage), other stages, render stage (frame presentation). So when we update the time it includes the input processing of the current frame and the frame presentation of the previous frame. This is a problem when vsync is on. When input processing takes a longer amount of time for a frame, the vsync wait time gets shorter. So when these are not paired correctly we can potentially have a long input processing time added to the normal vsync wait time in the previous frame. This leads to inaccurate frame time reporting and more variance of the time than actually exists. For more details of why this is an issue see the linked issue below. - Helps with #4669 - Supercedes #4728 and #4735. This PR should be less controversial than those because it doesn't add to the API surface. ## Solution - The most accurate frame time would come from hardware. We currently don't have access to that for multiple reasons, so the next best thing we can do is measure the frame time as close to frame presentation as possible. This PR gets the Instant::now() for the time immediately after frame presentation in the render system and then sends that time to the app world through a channel. - implements suggestion from @aevyrie from here #4728 (comment) ## Statistics ![image](https://user-images.githubusercontent.com/2180432/168410265-f249f66e-ea9d-45d1-b3d8-7207a7bc536c.png) --- ## Changelog - Make frame time reporting more accurate. ## Migration Guide `time.delta()` now reports zero for 2 frames on startup instead of 1 frame.
# Objective - The time update is currently done in the wrong part of the schedule. For a single frame the current order of things is update input, update time (First stage), other stages, render stage (frame presentation). So when we update the time it includes the input processing of the current frame and the frame presentation of the previous frame. This is a problem when vsync is on. When input processing takes a longer amount of time for a frame, the vsync wait time gets shorter. So when these are not paired correctly we can potentially have a long input processing time added to the normal vsync wait time in the previous frame. This leads to inaccurate frame time reporting and more variance of the time than actually exists. For more details of why this is an issue see the linked issue below. - Helps with bevyengine#4669 - Supercedes bevyengine#4728 and bevyengine#4735. This PR should be less controversial than those because it doesn't add to the API surface. ## Solution - The most accurate frame time would come from hardware. We currently don't have access to that for multiple reasons, so the next best thing we can do is measure the frame time as close to frame presentation as possible. This PR gets the Instant::now() for the time immediately after frame presentation in the render system and then sends that time to the app world through a channel. - implements suggestion from @aevyrie from here bevyengine#4728 (comment) ## Statistics ![image](https://user-images.githubusercontent.com/2180432/168410265-f249f66e-ea9d-45d1-b3d8-7207a7bc536c.png) --- ## Changelog - Make frame time reporting more accurate. ## Migration Guide `time.delta()` now reports zero for 2 frames on startup instead of 1 frame.
# Objective - The time update is currently done in the wrong part of the schedule. For a single frame the current order of things is update input, update time (First stage), other stages, render stage (frame presentation). So when we update the time it includes the input processing of the current frame and the frame presentation of the previous frame. This is a problem when vsync is on. When input processing takes a longer amount of time for a frame, the vsync wait time gets shorter. So when these are not paired correctly we can potentially have a long input processing time added to the normal vsync wait time in the previous frame. This leads to inaccurate frame time reporting and more variance of the time than actually exists. For more details of why this is an issue see the linked issue below. - Helps with bevyengine#4669 - Supercedes bevyengine#4728 and bevyengine#4735. This PR should be less controversial than those because it doesn't add to the API surface. ## Solution - The most accurate frame time would come from hardware. We currently don't have access to that for multiple reasons, so the next best thing we can do is measure the frame time as close to frame presentation as possible. This PR gets the Instant::now() for the time immediately after frame presentation in the render system and then sends that time to the app world through a channel. - implements suggestion from @aevyrie from here bevyengine#4728 (comment) ## Statistics ![image](https://user-images.githubusercontent.com/2180432/168410265-f249f66e-ea9d-45d1-b3d8-7207a7bc536c.png) --- ## Changelog - Make frame time reporting more accurate. ## Migration Guide `time.delta()` now reports zero for 2 frames on startup instead of 1 frame.
# Objective - The time update is currently done in the wrong part of the schedule. For a single frame the current order of things is update input, update time (First stage), other stages, render stage (frame presentation). So when we update the time it includes the input processing of the current frame and the frame presentation of the previous frame. This is a problem when vsync is on. When input processing takes a longer amount of time for a frame, the vsync wait time gets shorter. So when these are not paired correctly we can potentially have a long input processing time added to the normal vsync wait time in the previous frame. This leads to inaccurate frame time reporting and more variance of the time than actually exists. For more details of why this is an issue see the linked issue below. - Helps with bevyengine#4669 - Supercedes bevyengine#4728 and bevyengine#4735. This PR should be less controversial than those because it doesn't add to the API surface. ## Solution - The most accurate frame time would come from hardware. We currently don't have access to that for multiple reasons, so the next best thing we can do is measure the frame time as close to frame presentation as possible. This PR gets the Instant::now() for the time immediately after frame presentation in the render system and then sends that time to the app world through a channel. - implements suggestion from @aevyrie from here bevyengine#4728 (comment) ## Statistics ![image](https://user-images.githubusercontent.com/2180432/168410265-f249f66e-ea9d-45d1-b3d8-7207a7bc536c.png) --- ## Changelog - Make frame time reporting more accurate. ## Migration Guide `time.delta()` now reports zero for 2 frames on startup instead of 1 frame.
Objective
Solution
Changelog
Migration Guide
Why this might not be the right solution