Append commands (#10400)
# Objective - I've been experimenting with different patterns to try and make async tasks more convenient. One of the better ones I've found is to return a command queue to allow for deferred &mut World access. It can be convenient to check for task completion in a normal system, but it is hard to do something with the command queue after getting it back. This pr adds a `append` to Commands. This allows appending the returned command queue onto the system's commands. ## Solution - I edited the async compute example to use the new `append`, but not sure if I should keep the example changed as this might be too opinionated. ## Future Work - It would be very easy to pull the pattern used in the example out into a plugin or a external crate, so users wouldn't have to add the checking system. --- ## Changelog - add `append` to `Commands` and `CommandQueue`
This commit is contained in:
parent
3578eec351
commit
208ecb53dc
@ -136,6 +136,11 @@ impl CommandQueue {
|
|||||||
cursor = unsafe { cursor.add(size) };
|
cursor = unsafe { cursor.add(size) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Take all commands from `other` and append them to `self`, leaving `other` empty
|
||||||
|
pub fn append(&mut self, other: &mut CommandQueue) {
|
||||||
|
self.bytes.append(&mut other.bytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -146,6 +146,11 @@ impl<'w, 's> Commands<'w, 's> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Take all commands from `other` and append them to `self`, leaving `other` empty
|
||||||
|
pub fn append(&mut self, other: &mut CommandQueue) {
|
||||||
|
self.queue.append(other);
|
||||||
|
}
|
||||||
|
|
||||||
/// Pushes a [`Command`] to the queue for creating a new empty [`Entity`],
|
/// Pushes a [`Command`] to the queue for creating a new empty [`Entity`],
|
||||||
/// and returns its corresponding [`EntityCommands`].
|
/// and returns its corresponding [`EntityCommands`].
|
||||||
///
|
///
|
||||||
@ -1321,4 +1326,23 @@ mod tests {
|
|||||||
assert!(!world.contains_resource::<W<i32>>());
|
assert!(!world.contains_resource::<W<i32>>());
|
||||||
assert!(world.contains_resource::<W<f64>>());
|
assert!(world.contains_resource::<W<f64>>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn append() {
|
||||||
|
let mut world = World::default();
|
||||||
|
let mut queue_1 = CommandQueue::default();
|
||||||
|
{
|
||||||
|
let mut commands = Commands::new(&mut queue_1, &world);
|
||||||
|
commands.insert_resource(W(123i32));
|
||||||
|
}
|
||||||
|
let mut queue_2 = CommandQueue::default();
|
||||||
|
{
|
||||||
|
let mut commands = Commands::new(&mut queue_2, &world);
|
||||||
|
commands.insert_resource(W(456.0f64));
|
||||||
|
}
|
||||||
|
queue_1.append(&mut queue_2);
|
||||||
|
queue_1.apply(&mut world);
|
||||||
|
assert!(world.contains_resource::<W<i32>>());
|
||||||
|
assert!(world.contains_resource::<W<f64>>());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
//! to spawn, poll, and complete tasks across systems and system ticks.
|
//! to spawn, poll, and complete tasks across systems and system ticks.
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
|
ecs::system::{CommandQueue, SystemState},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
tasks::{block_on, futures_lite::future, AsyncComputeTaskPool, Task},
|
tasks::{block_on, futures_lite::future, AsyncComputeTaskPool, Task},
|
||||||
};
|
};
|
||||||
@ -42,7 +43,7 @@ fn add_assets(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct ComputeTransform(Task<Transform>);
|
struct ComputeTransform(Task<CommandQueue>);
|
||||||
|
|
||||||
/// This system generates tasks simulating computationally intensive
|
/// This system generates tasks simulating computationally intensive
|
||||||
/// work that potentially spans multiple frames/ticks. A separate
|
/// work that potentially spans multiple frames/ticks. A separate
|
||||||
@ -56,6 +57,7 @@ fn spawn_tasks(mut commands: Commands) {
|
|||||||
// Spawn new task on the AsyncComputeTaskPool; the task will be
|
// Spawn new task on the AsyncComputeTaskPool; the task will be
|
||||||
// executed in the background, and the Task future returned by
|
// executed in the background, and the Task future returned by
|
||||||
// spawn() can be used to poll for the result
|
// spawn() can be used to poll for the result
|
||||||
|
let entity = commands.spawn_empty().id();
|
||||||
let task = thread_pool.spawn(async move {
|
let task = thread_pool.spawn(async move {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
@ -66,11 +68,41 @@ fn spawn_tasks(mut commands: Commands) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Such hard work, all done!
|
// Such hard work, all done!
|
||||||
Transform::from_xyz(x as f32, y as f32, z as f32)
|
let transform = Transform::from_xyz(x as f32, y as f32, z as f32);
|
||||||
|
let mut command_queue = CommandQueue::default();
|
||||||
|
|
||||||
|
// we use a raw command queue to pass a FnOne(&mut World) back to be
|
||||||
|
// applied in a deferred manner.
|
||||||
|
command_queue.push(move |world: &mut World| {
|
||||||
|
let (box_mesh_handle, box_material_handle) = {
|
||||||
|
let mut system_state = SystemState::<(
|
||||||
|
Res<BoxMeshHandle>,
|
||||||
|
Res<BoxMaterialHandle>,
|
||||||
|
)>::new(world);
|
||||||
|
let (box_mesh_handle, box_material_handle) =
|
||||||
|
system_state.get_mut(world);
|
||||||
|
|
||||||
|
(box_mesh_handle.clone(), box_material_handle.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
world
|
||||||
|
.entity_mut(entity)
|
||||||
|
// Add our new PbrBundle of components to our tagged entity
|
||||||
|
.insert(PbrBundle {
|
||||||
|
mesh: box_mesh_handle,
|
||||||
|
material: box_material_handle,
|
||||||
|
transform,
|
||||||
|
..default()
|
||||||
|
})
|
||||||
|
// Task is complete, so remove task component from entity
|
||||||
|
.remove::<ComputeTransform>();
|
||||||
|
});
|
||||||
|
|
||||||
|
command_queue
|
||||||
});
|
});
|
||||||
|
|
||||||
// Spawn new entity and add our new task as a component
|
// Spawn new entity and add our new task as a component
|
||||||
commands.spawn(ComputeTransform(task));
|
commands.entity(entity).insert(ComputeTransform(task));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,24 +112,11 @@ fn spawn_tasks(mut commands: Commands) {
|
|||||||
/// tasks to see if they're complete. If the task is complete it takes the result, adds a
|
/// tasks to see if they're complete. If the task is complete it takes the result, adds a
|
||||||
/// new [`PbrBundle`] of components to the entity using the result from the task's work, and
|
/// new [`PbrBundle`] of components to the entity using the result from the task's work, and
|
||||||
/// removes the task component from the entity.
|
/// removes the task component from the entity.
|
||||||
fn handle_tasks(
|
fn handle_tasks(mut commands: Commands, mut transform_tasks: Query<&mut ComputeTransform>) {
|
||||||
mut commands: Commands,
|
for mut task in &mut transform_tasks {
|
||||||
mut transform_tasks: Query<(Entity, &mut ComputeTransform)>,
|
if let Some(mut commands_queue) = block_on(future::poll_once(&mut task.0)) {
|
||||||
box_mesh_handle: Res<BoxMeshHandle>,
|
// append the returned command queue to have it execute later
|
||||||
box_material_handle: Res<BoxMaterialHandle>,
|
commands.append(&mut commands_queue);
|
||||||
) {
|
|
||||||
for (entity, mut task) in &mut transform_tasks {
|
|
||||||
if let Some(transform) = block_on(future::poll_once(&mut task.0)) {
|
|
||||||
// Add our new PbrBundle of components to our tagged entity
|
|
||||||
commands.entity(entity).insert(PbrBundle {
|
|
||||||
mesh: box_mesh_handle.clone(),
|
|
||||||
material: box_material_handle.clone(),
|
|
||||||
transform,
|
|
||||||
..default()
|
|
||||||
});
|
|
||||||
|
|
||||||
// Task is complete, so remove task component from entity
|
|
||||||
commands.entity(entity).remove::<ComputeTransform>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user