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) };
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
|
@ -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`],
|
||||
/// and returns its corresponding [`EntityCommands`].
|
||||
///
|
||||
@ -1321,4 +1326,23 @@ mod tests {
|
||||
assert!(!world.contains_resource::<W<i32>>());
|
||||
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.
|
||||
|
||||
use bevy::{
|
||||
ecs::system::{CommandQueue, SystemState},
|
||||
prelude::*,
|
||||
tasks::{block_on, futures_lite::future, AsyncComputeTaskPool, Task},
|
||||
};
|
||||
@ -42,7 +43,7 @@ fn add_assets(
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct ComputeTransform(Task<Transform>);
|
||||
struct ComputeTransform(Task<CommandQueue>);
|
||||
|
||||
/// This system generates tasks simulating computationally intensive
|
||||
/// 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
|
||||
// executed in the background, and the Task future returned by
|
||||
// spawn() can be used to poll for the result
|
||||
let entity = commands.spawn_empty().id();
|
||||
let task = thread_pool.spawn(async move {
|
||||
let mut rng = rand::thread_rng();
|
||||
let start_time = Instant::now();
|
||||
@ -66,11 +68,41 @@ fn spawn_tasks(mut commands: Commands) {
|
||||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
/// new [`PbrBundle`] of components to the entity using the result from the task's work, and
|
||||
/// removes the task component from the entity.
|
||||
fn handle_tasks(
|
||||
mut commands: Commands,
|
||||
mut transform_tasks: Query<(Entity, &mut ComputeTransform)>,
|
||||
box_mesh_handle: Res<BoxMeshHandle>,
|
||||
box_material_handle: Res<BoxMaterialHandle>,
|
||||
) {
|
||||
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>();
|
||||
fn handle_tasks(mut commands: Commands, mut transform_tasks: Query<&mut ComputeTransform>) {
|
||||
for mut task in &mut transform_tasks {
|
||||
if let Some(mut commands_queue) = block_on(future::poll_once(&mut task.0)) {
|
||||
// append the returned command queue to have it execute later
|
||||
commands.append(&mut commands_queue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user