Repository navigation
|
Hello, I am using Bevy to implement a camera control software where the device main control loop must run at 10Hz. During testing, I noticed a few things:
Looking at Bevy's source code, I believe the reason for 2. is pool executors are ticked as part of the application main loop. This is perfectly reasonable in games where main loops are expected to run between 25Hz to 240Hz. But, unfortunately for me, this does not work well with the slow loop of my use case. This explains 1. So, I am considering implementing my own task pool plugin which I would tick separately. However, I found this warning in Bevy's code: /// A function used by `bevy_app` to tick the global tasks pools on the main thread.
/// This will run a maximum of 100 local tasks per executor per call to this function.
///
/// # Warning
///
/// This function *must* be called on the main thread, or the task pools will not be updated appropriately.
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
pub fn tick_global_task_pools_on_main_thread() {Can someone elaborate of why this needed and if it applies to my use case? |
Replies: 3 comments 5 replies
|
How are you spawning your tasks? AFAIK that function is needed only for making progress on local tasks, i.e. those spawned using |
|
If you're not using DefaultPlugins, you might need to configure the task pools or add the TaskPoolPlugin. https://docs.rs/bevy/latest/bevy/prelude/struct.TaskPoolPlugin.html |
|
Before writing a custom pool, check whether your build has bevy's Without that feature pub fn spawn<T>(&self, future: ...) -> Task<T> {
LOCAL_EXECUTOR.with(|executor| {
let task = executor.spawn(future);
// Loop until all tasks are done
while executor.try_tick() {}
task
})
}
pub fn spawn_local<T>(&self, future: ...) -> Task<T> {
self.spawn(future)
}Everything lands on a thread-local executor, and after the spawning call returns it only advances when somebody ticks that executor. The only thing doing that is the function you quoted, called once per main loop iteration: for _ in 0..100 {
compute_local_executor.try_tick();
async_local_executor.try_tick();
io_local_executor.try_tick();
}So at 10 Hz your transfers get ten tick windows per second, with at most 100 polls per pool in each. That is the coupling you measured. The feature comes in through If To your actual question about the warning: with the multi-threaded pool, If you turn out to already be on the multi-threaded pool, then the next thing I'd look at is thread count rather than ticking. io: TaskPoolThreadAssignmentPolicy {
min_threads: 1,
max_threads: 4,
percent: 0.25,
...Any blocking call inside those futures occupies one of those threads for its whole duration, so a handful of blocking transfers is enough to serialise everything. That would show up as slow transfers, though it wouldn't tie them to |
Before writing a custom pool, check whether your build has bevy's
multi_threadedfeature. If it doesn't, both of your observations follow directly, and a hand-rolled pool would run into the same thing.Without that feature
bevy_taskscompilessingle_threaded_task_pool.rs, wherespawnis not what the name suggests:Everything lands on a thread-local executor, and after the spawning cal…