From 629598c31d488242ffe9f83ad0a1744002bbd668 Mon Sep 17 00:00:00 2001 From: the-notable Date: Sat, 15 May 2021 20:30:05 -0700 Subject: [PATCH 01/22] example for async compute --- Cargo.toml | 7 ++ examples/tasks/async_compute.rs | 122 ++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 examples/tasks/async_compute.rs diff --git a/Cargo.toml b/Cargo.toml index 95141021a8077..91ea7259abdd0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,6 +87,8 @@ anyhow = "1.0" rand = "0.8.0" ron = "0.6.2" serde = {version = "1", features = ["derive"]} +# Needed to poll Task examples +futures-lite = "1.11.3" [[example]] name = "hello_world" @@ -400,6 +402,11 @@ path = "examples/shader/shader_custom_material.rs" name = "shader_defs" path = "examples/shader/shader_defs.rs" +# Tasks +[[example]] +name = "async_compute" +path = "examples/tasks/async_compute.rs" + # Tools [[example]] name = "bevymark" diff --git a/examples/tasks/async_compute.rs b/examples/tasks/async_compute.rs new file mode 100644 index 0000000000000..9cfb660abbe13 --- /dev/null +++ b/examples/tasks/async_compute.rs @@ -0,0 +1,122 @@ +use bevy::prelude::*; +use bevy::tasks::{AsyncComputeTaskPool, Task}; +use std::time::{Instant, Duration}; +use rand::Rng; +use std::option::Option::Some; +use futures_lite::future; + +/// This example shows how to use the ecs system and the AsyncComputeTaskPool +/// to spawn, poll, and complete tasks across systems and system ticks. + +// Number of cubes to spawn across the x, y, and z axis +const NUM_CUBES: i32 = 6; + +// Used to tag our new entity spawned with tasks +struct Marker; + +/// This system generates tasks simulating computationally intensive +/// work that potentially spawns multiple frames/ticks. A separate +/// system, handle_tasks, will poll the spawned tasks on subsequent +/// frames/ticks, and use the results to spawn cubes +fn spawn_tasks( + mut commands: Commands, + thread_pool: Res, +) { + for x in 0 ..NUM_CUBES { + for y in 0 ..NUM_CUBES { + for z in 0 ..NUM_CUBES { + + // Spawn new task on the AsyncComputeTaskPool + let task = thread_pool.spawn(async move { + + let mut rng = rand::thread_rng(); + let start_time = Instant::now(); + let duration = Duration::from_secs_f32(rng.gen_range(0.05..0.2)); + while Instant::now() - start_time < duration { + // Simulating doing hard compute work generating translation coords! + } + + // Such hard work, all done! + eprintln!("Done generating translation coords x: {} y: {} z: {}", x, y, z); + Transform::from_translation(Vec3::new(x as f32, y as f32, z as f32)) + }); + + // Spawn new entity, tag it with Marker as a component, + // and add our new task as a component + commands.spawn() + .insert(Marker) + .insert(task); + } + } + } +} + +/// This system queries for entities that have both our Marker component +/// as well as a Task component. It polls the 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 meshes: ResMut>, + mut materials: ResMut>, + mut our_entity_tasks: Query<(Entity, &mut Task), With> +) { + for (entity, mut task) in our_entity_tasks.iter_mut() { + + if let Some(transform) = future::block_on(future::poll_once(&mut *task)) { + + // Normally we would add our mesh and material assets once + // and store the handle, but that's for another example + let box_mesh = meshes.add(Mesh::from(shape::Cube { size: 0.25 })); + let box_material = materials.add(bevy::render::color::Color::rgb(1.0, 0.2, 0.3).into()); + + // Add our new PbrBundle of components to our tagged entity + commands.entity(entity).insert_bundle(PbrBundle { + mesh: box_mesh.clone(), + material: box_material.clone(), + transform, + ..Default::default() + }); + + // Task is complete, so remove task component from entity + commands.entity(entity).remove::>(); + } + } +} + +fn main() { + App::build() + .insert_resource(Msaa { samples: 4 }) + .add_plugins(DefaultPlugins) + .add_startup_system(setup_env.system()) + .add_startup_system(spawn_tasks.system()) + .add_system(handle_tasks.system()) + .run(); +} + +/// This system IS NOT part of the example, it's only +/// used to setup the light and camera for the environment +fn setup_env(mut commands: Commands) { + + // Used to center camera on spawned cubes + let offset = || { + if NUM_CUBES % 2 == 0 { + (NUM_CUBES / 2) as f32 - 0.5 + } else { + (NUM_CUBES / 2) as f32 + } + }; + + // lights + commands.spawn_bundle(PointLightBundle { + transform: Transform::from_translation(Vec3::new(4.0, 12.0, 15.0)), + ..Default::default() + }); + + // camera + commands.spawn_bundle(PerspectiveCameraBundle { + transform: Transform::from_translation(Vec3::new(offset(), offset(), 15.0)) + .looking_at(Vec3::new(offset(), offset(), 0.0), Vec3::Y), + ..Default::default() + }); +} \ No newline at end of file From 62b6505e6a64a458199555c43ee17b8b8e11d8d5 Mon Sep 17 00:00:00 2001 From: the-notable Date: Sat, 15 May 2021 20:33:50 -0700 Subject: [PATCH 02/22] removed Color qualification --- examples/tasks/async_compute.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tasks/async_compute.rs b/examples/tasks/async_compute.rs index 9cfb660abbe13..b130a24707f66 100644 --- a/examples/tasks/async_compute.rs +++ b/examples/tasks/async_compute.rs @@ -68,7 +68,7 @@ fn handle_tasks( // Normally we would add our mesh and material assets once // and store the handle, but that's for another example let box_mesh = meshes.add(Mesh::from(shape::Cube { size: 0.25 })); - let box_material = materials.add(bevy::render::color::Color::rgb(1.0, 0.2, 0.3).into()); + let box_material = materials.add(Color::rgb(1.0, 0.2, 0.3).into()); // Add our new PbrBundle of components to our tagged entity commands.entity(entity).insert_bundle(PbrBundle { From d5218edf6027d492b561c53efc456a0d85248fc9 Mon Sep 17 00:00:00 2001 From: the-notable Date: Sat, 15 May 2021 20:48:00 -0700 Subject: [PATCH 03/22] Update examples/tasks/async_compute.rs Co-authored-by: Nathan Ward <43621845+NathanSWard@users.noreply.github.com> --- examples/tasks/async_compute.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/tasks/async_compute.rs b/examples/tasks/async_compute.rs index b130a24707f66..30de4c32697bb 100644 --- a/examples/tasks/async_compute.rs +++ b/examples/tasks/async_compute.rs @@ -2,7 +2,6 @@ use bevy::prelude::*; use bevy::tasks::{AsyncComputeTaskPool, Task}; use std::time::{Instant, Duration}; use rand::Rng; -use std::option::Option::Some; use futures_lite::future; /// This example shows how to use the ecs system and the AsyncComputeTaskPool @@ -119,4 +118,4 @@ fn setup_env(mut commands: Commands) { .looking_at(Vec3::new(offset(), offset(), 0.0), Vec3::Y), ..Default::default() }); -} \ No newline at end of file +} From 271e3b53d38265ca726cb710b90264141fbe021a Mon Sep 17 00:00:00 2001 From: the-notable Date: Sat, 15 May 2021 20:48:13 -0700 Subject: [PATCH 04/22] Update examples/tasks/async_compute.rs Co-authored-by: Nathan Ward <43621845+NathanSWard@users.noreply.github.com> --- examples/tasks/async_compute.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/tasks/async_compute.rs b/examples/tasks/async_compute.rs index 30de4c32697bb..9081dcb32c424 100644 --- a/examples/tasks/async_compute.rs +++ b/examples/tasks/async_compute.rs @@ -1,5 +1,4 @@ -use bevy::prelude::*; -use bevy::tasks::{AsyncComputeTaskPool, Task}; +use bevy::{prelude::*, tasks::{AsyncComputeTaskPool, Task}; use std::time::{Instant, Duration}; use rand::Rng; use futures_lite::future; From ecf06b3ba30b805a929a962be386dcf9f97420ec Mon Sep 17 00:00:00 2001 From: the-notable Date: Sat, 15 May 2021 20:52:13 -0700 Subject: [PATCH 05/22] Update examples/tasks/async_compute.rs Co-authored-by: Nathan Ward <43621845+NathanSWard@users.noreply.github.com> --- examples/tasks/async_compute.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/tasks/async_compute.rs b/examples/tasks/async_compute.rs index 9081dcb32c424..3c637462242cb 100644 --- a/examples/tasks/async_compute.rs +++ b/examples/tasks/async_compute.rs @@ -35,7 +35,8 @@ fn spawn_tasks( } // Such hard work, all done! - eprintln!("Done generating translation coords x: {} y: {} z: {}", x, y, z); + println!("Done generating translation coords x: {} y: {} z: {}", x, y, z); + Transform::from_translation(Vec3::new(x as f32, y as f32, z as f32)) }); From 0eaa0a52a798e043cb86db218b07624379c8ee58 Mon Sep 17 00:00:00 2001 From: the-notable Date: Sat, 15 May 2021 20:52:29 -0700 Subject: [PATCH 06/22] missing closing bracket --- examples/tasks/async_compute.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tasks/async_compute.rs b/examples/tasks/async_compute.rs index 9081dcb32c424..75429170179c8 100644 --- a/examples/tasks/async_compute.rs +++ b/examples/tasks/async_compute.rs @@ -1,4 +1,4 @@ -use bevy::{prelude::*, tasks::{AsyncComputeTaskPool, Task}; +use bevy::{prelude::*, tasks::{AsyncComputeTaskPool, Task}}; use std::time::{Instant, Duration}; use rand::Rng; use futures_lite::future; From 735ec0c9ab041ceda2eb566d63ae80162c8f1781 Mon Sep 17 00:00:00 2001 From: the-notable Date: Sat, 15 May 2021 20:55:18 -0700 Subject: [PATCH 07/22] removed 'system' from comment --- examples/tasks/async_compute.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tasks/async_compute.rs b/examples/tasks/async_compute.rs index c74dda9a388cf..ce8ea30f95747 100644 --- a/examples/tasks/async_compute.rs +++ b/examples/tasks/async_compute.rs @@ -3,7 +3,7 @@ use std::time::{Instant, Duration}; use rand::Rng; use futures_lite::future; -/// This example shows how to use the ecs system and the AsyncComputeTaskPool +/// This example shows how to use the ECS and the AsyncComputeTaskPool /// to spawn, poll, and complete tasks across systems and system ticks. // Number of cubes to spawn across the x, y, and z axis From bf093eff930f1b566190180c93e7eaae36c623a2 Mon Sep 17 00:00:00 2001 From: the-notable Date: Sat, 15 May 2021 21:02:18 -0700 Subject: [PATCH 08/22] Update examples/tasks/async_compute.rs Co-authored-by: Nathan Ward <43621845+NathanSWard@users.noreply.github.com> --- examples/tasks/async_compute.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/tasks/async_compute.rs b/examples/tasks/async_compute.rs index ce8ea30f95747..5620d535c4bd3 100644 --- a/examples/tasks/async_compute.rs +++ b/examples/tasks/async_compute.rs @@ -13,7 +13,8 @@ const NUM_CUBES: i32 = 6; struct Marker; /// This system generates tasks simulating computationally intensive -/// work that potentially spawns multiple frames/ticks. A separate +/// work that potentially spans multiple frames/ticks. A separate + /// system, handle_tasks, will poll the spawned tasks on subsequent /// frames/ticks, and use the results to spawn cubes fn spawn_tasks( From 0c400d23d36358e3af4b51f2e1edefe7cccc9b94 Mon Sep 17 00:00:00 2001 From: the-notable Date: Sat, 15 May 2021 21:03:04 -0700 Subject: [PATCH 09/22] removed unnecessary space --- examples/tasks/async_compute.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/tasks/async_compute.rs b/examples/tasks/async_compute.rs index 5620d535c4bd3..c7217589f7835 100644 --- a/examples/tasks/async_compute.rs +++ b/examples/tasks/async_compute.rs @@ -14,7 +14,6 @@ struct Marker; /// This system generates tasks simulating computationally intensive /// work that potentially spans multiple frames/ticks. A separate - /// system, handle_tasks, will poll the spawned tasks on subsequent /// frames/ticks, and use the results to spawn cubes fn spawn_tasks( From 2f8dcc8636db963a9296d6f40ca1d253ab29c679 Mon Sep 17 00:00:00 2001 From: the-notable Date: Sat, 15 May 2021 21:11:55 -0700 Subject: [PATCH 10/22] Update examples/tasks/async_compute.rs Co-authored-by: Nathan Ward <43621845+NathanSWard@users.noreply.github.com> --- examples/tasks/async_compute.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/tasks/async_compute.rs b/examples/tasks/async_compute.rs index c7217589f7835..d3411ec514003 100644 --- a/examples/tasks/async_compute.rs +++ b/examples/tasks/async_compute.rs @@ -71,8 +71,8 @@ fn handle_tasks( // Add our new PbrBundle of components to our tagged entity commands.entity(entity).insert_bundle(PbrBundle { - mesh: box_mesh.clone(), - material: box_material.clone(), + mesh: box_mesh, + material: box_material, transform, ..Default::default() }); From 2846fe4c753d6ff42f1097cbca2751c069662389 Mon Sep 17 00:00:00 2001 From: the-notable Date: Sat, 15 May 2021 21:12:59 -0700 Subject: [PATCH 11/22] Update examples/tasks/async_compute.rs Co-authored-by: Nathan Ward <43621845+NathanSWard@users.noreply.github.com> --- examples/tasks/async_compute.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tasks/async_compute.rs b/examples/tasks/async_compute.rs index d3411ec514003..fd83bac21d53a 100644 --- a/examples/tasks/async_compute.rs +++ b/examples/tasks/async_compute.rs @@ -7,7 +7,7 @@ use futures_lite::future; /// to spawn, poll, and complete tasks across systems and system ticks. // Number of cubes to spawn across the x, y, and z axis -const NUM_CUBES: i32 = 6; +const NUM_CUBES: u32 = 6; // Used to tag our new entity spawned with tasks struct Marker; From e31c94702d62530bb6f1d43602197962a73821bf Mon Sep 17 00:00:00 2001 From: the-notable Date: Sat, 15 May 2021 21:16:29 -0700 Subject: [PATCH 12/22] for from for in to for each --- examples/tasks/async_compute.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/tasks/async_compute.rs b/examples/tasks/async_compute.rs index fd83bac21d53a..81e619a44f11e 100644 --- a/examples/tasks/async_compute.rs +++ b/examples/tasks/async_compute.rs @@ -60,7 +60,7 @@ fn handle_tasks( mut materials: ResMut>, mut our_entity_tasks: Query<(Entity, &mut Task), With> ) { - for (entity, mut task) in our_entity_tasks.iter_mut() { + our_entity_tasks.for_each_mut(|(entity, mut task)| { if let Some(transform) = future::block_on(future::poll_once(&mut *task)) { @@ -80,7 +80,7 @@ fn handle_tasks( // Task is complete, so remove task component from entity commands.entity(entity).remove::>(); } - } + }); } fn main() { From 6dcd5d1d2f4ebd72703373493f03641936ada450 Mon Sep 17 00:00:00 2001 From: the-notable Date: Sat, 15 May 2021 21:28:28 -0700 Subject: [PATCH 13/22] changed to expression --- examples/tasks/async_compute.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/examples/tasks/async_compute.rs b/examples/tasks/async_compute.rs index 81e619a44f11e..6a91e7486de38 100644 --- a/examples/tasks/async_compute.rs +++ b/examples/tasks/async_compute.rs @@ -98,12 +98,10 @@ fn main() { fn setup_env(mut commands: Commands) { // Used to center camera on spawned cubes - let offset = || { - if NUM_CUBES % 2 == 0 { - (NUM_CUBES / 2) as f32 - 0.5 - } else { - (NUM_CUBES / 2) as f32 - } + let offset = if NUM_CUBES % 2 == 0 { + (NUM_CUBES / 2) as f32 - 0.5 + } else { + (NUM_CUBES / 2) as f32 }; // lights @@ -114,8 +112,8 @@ fn setup_env(mut commands: Commands) { // camera commands.spawn_bundle(PerspectiveCameraBundle { - transform: Transform::from_translation(Vec3::new(offset(), offset(), 15.0)) - .looking_at(Vec3::new(offset(), offset(), 0.0), Vec3::Y), + transform: Transform::from_translation(Vec3::new(offset, offset, 15.0)) + .looking_at(Vec3::new(offset, offset, 0.0), Vec3::Y), ..Default::default() }); } From 846b63bc0b81f13a5e11af86d717eeaffba7866d Mon Sep 17 00:00:00 2001 From: the-notable Date: Sat, 15 May 2021 21:31:55 -0700 Subject: [PATCH 14/22] further explained simulating duration of compute work --- examples/tasks/async_compute.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/tasks/async_compute.rs b/examples/tasks/async_compute.rs index 6a91e7486de38..15bbac50b465f 100644 --- a/examples/tasks/async_compute.rs +++ b/examples/tasks/async_compute.rs @@ -27,11 +27,13 @@ fn spawn_tasks( // Spawn new task on the AsyncComputeTaskPool let task = thread_pool.spawn(async move { + let mut rng = rand::thread_rng(); let start_time = Instant::now(); let duration = Duration::from_secs_f32(rng.gen_range(0.05..0.2)); while Instant::now() - start_time < duration { - // Simulating doing hard compute work generating translation coords! + // Spinning for 'duration', simulating doing hard + // compute work generating translation coords! } // Such hard work, all done! From e065dff6ea4c34ac77ed7f1c456f7d5de46eb828 Mon Sep 17 00:00:00 2001 From: the-notable Date: Sat, 15 May 2021 21:46:36 -0700 Subject: [PATCH 15/22] removed entity marker struct Marker --- examples/tasks/async_compute.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/examples/tasks/async_compute.rs b/examples/tasks/async_compute.rs index 15bbac50b465f..e96787fde66db 100644 --- a/examples/tasks/async_compute.rs +++ b/examples/tasks/async_compute.rs @@ -9,9 +9,6 @@ use futures_lite::future; // Number of cubes to spawn across the x, y, and z axis const NUM_CUBES: u32 = 6; -// Used to tag our new entity spawned with tasks -struct Marker; - /// This system generates tasks simulating computationally intensive /// work that potentially spans multiple frames/ticks. A separate /// system, handle_tasks, will poll the spawned tasks on subsequent @@ -27,7 +24,6 @@ fn spawn_tasks( // Spawn new task on the AsyncComputeTaskPool let task = thread_pool.spawn(async move { - let mut rng = rand::thread_rng(); let start_time = Instant::now(); let duration = Duration::from_secs_f32(rng.gen_range(0.05..0.2)); @@ -42,25 +38,22 @@ fn spawn_tasks( Transform::from_translation(Vec3::new(x as f32, y as f32, z as f32)) }); - // Spawn new entity, tag it with Marker as a component, - // and add our new task as a component - commands.spawn() - .insert(Marker) - .insert(task); + // Spawn new entity and add our new task as a component + commands.spawn().insert(task); } } } } -/// This system queries for entities that have both our Marker component -/// as well as a Task component. It polls the 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. +/// This system queries for entities that have our Task component. It polls the +/// 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 meshes: ResMut>, mut materials: ResMut>, - mut our_entity_tasks: Query<(Entity, &mut Task), With> + mut our_entity_tasks: Query<(Entity, &mut Task)> ) { our_entity_tasks.for_each_mut(|(entity, mut task)| { From dcea7918fb7b6a9afbdc1eef0136492bf7f6ca75 Mon Sep 17 00:00:00 2001 From: the-notable Date: Sat, 15 May 2021 22:16:31 -0700 Subject: [PATCH 16/22] added example to README --- examples/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/examples/README.md b/examples/README.md index 13b791835ba65..35e8923d4c057 100644 --- a/examples/README.md +++ b/examples/README.md @@ -49,6 +49,7 @@ git checkout v0.4.0 - [Reflection](#reflection) - [Scene](#scene) - [Shaders](#shaders) + - [Tasks](#tasks) - [Tests](#tests) - [Tools](#tools) - [UI (User Interface)](#ui-user-interface) @@ -211,6 +212,13 @@ Example | File | Description `shader_custom_material` | [`shader/shader_custom_material.rs`](./shader/shader_custom_material.rs) | Illustrates creating a custom material and a shader that uses it `shader_defs` | [`shader/shader_defs.rs`](./shader/shader_defs.rs) | Demonstrates creating a custom material that uses "shaders defs" (a tool to selectively toggle parts of a shader) +## Tasks + +Example | File | Description +--- | --- | --- +`async_compute` | [`tasks/async_compute.rs`](./tasks/async_compute.rs) | How to use AsyncComputeTaskPool to complete longer running tasks + + ## Tests Example | File | Description From d4aa3ab2c4949342e97e426a428b2e5492b43d57 Mon Sep 17 00:00:00 2001 From: the-notable Date: Sun, 16 May 2021 14:10:25 -0700 Subject: [PATCH 17/22] Update examples/README.md Co-authored-by: Nathan Ward <43621845+NathanSWard@users.noreply.github.com> --- examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index 35e8923d4c057..53b00edd933e2 100644 --- a/examples/README.md +++ b/examples/README.md @@ -216,7 +216,7 @@ Example | File | Description Example | File | Description --- | --- | --- -`async_compute` | [`tasks/async_compute.rs`](./tasks/async_compute.rs) | How to use AsyncComputeTaskPool to complete longer running tasks +`async_compute` | [`tasks/async_compute.rs`](./tasks/async_compute.rs) | How to use `AsyncComputeTaskPool` to complete longer running tasks ## Tests From e2c04e8340919a74abea92b9fc9cd5ff176e9a9e Mon Sep 17 00:00:00 2001 From: the-notable Date: Sun, 16 May 2021 15:29:05 -0700 Subject: [PATCH 18/22] removed println from task --- examples/tasks/async_compute.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/tasks/async_compute.rs b/examples/tasks/async_compute.rs index e96787fde66db..8b15d51b7a7d8 100644 --- a/examples/tasks/async_compute.rs +++ b/examples/tasks/async_compute.rs @@ -33,8 +33,6 @@ fn spawn_tasks( } // Such hard work, all done! - println!("Done generating translation coords x: {} y: {} z: {}", x, y, z); - Transform::from_translation(Vec3::new(x as f32, y as f32, z as f32)) }); From bd73bee52ba4bb139dbbe2bef7ecf5f8bc696e56 Mon Sep 17 00:00:00 2001 From: the-notable Date: Sun, 16 May 2021 21:00:04 -0700 Subject: [PATCH 19/22] moved asset registration and handle storage to its own fn --- examples/tasks/async_compute.rs | 38 ++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/examples/tasks/async_compute.rs b/examples/tasks/async_compute.rs index 8b15d51b7a7d8..689e3eb49febc 100644 --- a/examples/tasks/async_compute.rs +++ b/examples/tasks/async_compute.rs @@ -9,6 +9,25 @@ use futures_lite::future; // Number of cubes to spawn across the x, y, and z axis const NUM_CUBES: u32 = 6; +struct BoxMeshHandle(Handle); +struct BoxMaterialHandle(Handle); + +/// Startup system which runs only once and generates our Box Mesh +/// and Box Material assets, adds them to their respective Asset +/// Resources, and stores their handles as resources so we can access +/// them later when we're ready to render our Boxes +fn add_assets( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +){ + let box_mesh_handle = meshes.add(Mesh::from(shape::Cube { size: 0.25 })); + commands.insert_resource(BoxMeshHandle(box_mesh_handle)); + + let box_material_handle = materials.add(Color::rgb(1.0, 0.2, 0.3).into()); + commands.insert_resource(BoxMaterialHandle(box_material_handle)); +} + /// This system generates tasks simulating computationally intensive /// work that potentially spans multiple frames/ticks. A separate /// system, handle_tasks, will poll the spawned tasks on subsequent @@ -49,23 +68,18 @@ fn spawn_tasks( /// removes the task component from the entity. fn handle_tasks( mut commands: Commands, - mut meshes: ResMut>, - mut materials: ResMut>, - mut our_entity_tasks: Query<(Entity, &mut Task)> + mut our_entity_tasks: Query<(Entity, &mut Task)>, + box_mesh_handle: Res, + box_material_handle: Res, ) { our_entity_tasks.for_each_mut(|(entity, mut task)| { if let Some(transform) = future::block_on(future::poll_once(&mut *task)) { - // Normally we would add our mesh and material assets once - // and store the handle, but that's for another example - let box_mesh = meshes.add(Mesh::from(shape::Cube { size: 0.25 })); - let box_material = materials.add(Color::rgb(1.0, 0.2, 0.3).into()); - // Add our new PbrBundle of components to our tagged entity commands.entity(entity).insert_bundle(PbrBundle { - mesh: box_mesh, - material: box_material, + mesh: box_mesh_handle.0.clone(), + material: box_material_handle.0.clone(), transform, ..Default::default() }); @@ -81,13 +95,13 @@ fn main() { .insert_resource(Msaa { samples: 4 }) .add_plugins(DefaultPlugins) .add_startup_system(setup_env.system()) + .add_startup_system(add_assets.system()) .add_startup_system(spawn_tasks.system()) .add_system(handle_tasks.system()) .run(); } -/// This system IS NOT part of the example, it's only -/// used to setup the light and camera for the environment +/// This system is only used to setup light and camera for the environment fn setup_env(mut commands: Commands) { // Used to center camera on spawned cubes From d1c758276c3b4ca68d36cd3153391ffa73b459ca Mon Sep 17 00:00:00 2001 From: the-notable Date: Tue, 18 May 2021 22:29:31 -0700 Subject: [PATCH 20/22] changed example categorization --- Cargo.toml | 10 +++++----- examples/README.md | 15 +++++++-------- examples/{tasks => async_tasks}/async_compute.rs | 0 3 files changed, 12 insertions(+), 13 deletions(-) rename examples/{tasks => async_tasks}/async_compute.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 91ea7259abdd0..201e13e7589bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -234,6 +234,11 @@ path = "examples/asset/custom_asset_io.rs" name = "hot_asset_reloading" path = "examples/asset/hot_asset_reloading.rs" +# Async Tasks +[[example]] +name = "async_compute" +path = "examples/async_tasks/async_compute.rs" + # Audio [[example]] name = "audio" @@ -402,11 +407,6 @@ path = "examples/shader/shader_custom_material.rs" name = "shader_defs" path = "examples/shader/shader_defs.rs" -# Tasks -[[example]] -name = "async_compute" -path = "examples/tasks/async_compute.rs" - # Tools [[example]] name = "bevymark" diff --git a/examples/README.md b/examples/README.md index 53b00edd933e2..11090fcf56f56 100644 --- a/examples/README.md +++ b/examples/README.md @@ -41,6 +41,7 @@ git checkout v0.4.0 - [3D Rendering](#3d-rendering) - [Application](#application) - [Assets](#assets) + - [Async Tasks](#async-tasks) - [Audio](#audio) - [Diagnostics](#diagnostics) - [ECS (Entity Component System)](#ecs-entity-component-system) @@ -49,7 +50,6 @@ git checkout v0.4.0 - [Reflection](#reflection) - [Scene](#scene) - [Shaders](#shaders) - - [Tasks](#tasks) - [Tests](#tests) - [Tools](#tools) - [UI (User Interface)](#ui-user-interface) @@ -132,6 +132,12 @@ Example | File | Description `custom_asset_io` | [`asset/custom_asset_io.rs`](./asset/custom_asset_io.rs) | Implements a custom asset io loader `hot_asset_reloading` | [`asset/hot_asset_reloading.rs`](./asset/hot_asset_reloading.rs) | Demonstrates automatic reloading of assets when modified on disk +## Async Tasks + +Example | File | Description +--- | --- | --- +`async_compute` | [`async_tasks/async_compute.rs`](async_tasks/async_compute.rs) | How to use `AsyncComputeTaskPool` to complete longer running tasks + ## Audio Example | File | Description @@ -212,13 +218,6 @@ Example | File | Description `shader_custom_material` | [`shader/shader_custom_material.rs`](./shader/shader_custom_material.rs) | Illustrates creating a custom material and a shader that uses it `shader_defs` | [`shader/shader_defs.rs`](./shader/shader_defs.rs) | Demonstrates creating a custom material that uses "shaders defs" (a tool to selectively toggle parts of a shader) -## Tasks - -Example | File | Description ---- | --- | --- -`async_compute` | [`tasks/async_compute.rs`](./tasks/async_compute.rs) | How to use `AsyncComputeTaskPool` to complete longer running tasks - - ## Tests Example | File | Description diff --git a/examples/tasks/async_compute.rs b/examples/async_tasks/async_compute.rs similarity index 100% rename from examples/tasks/async_compute.rs rename to examples/async_tasks/async_compute.rs From af9432008be18fcc2904f795e0c6d54caee1d01b Mon Sep 17 00:00:00 2001 From: the-notable Date: Wed, 19 May 2021 18:31:46 -0700 Subject: [PATCH 21/22] ran cargo run -p ci, passes --- examples/async_tasks/async_compute.rs | 247 +++++++++++++------------- 1 file changed, 121 insertions(+), 126 deletions(-) diff --git a/examples/async_tasks/async_compute.rs b/examples/async_tasks/async_compute.rs index 689e3eb49febc..9eb1445b2d806 100644 --- a/examples/async_tasks/async_compute.rs +++ b/examples/async_tasks/async_compute.rs @@ -1,126 +1,121 @@ -use bevy::{prelude::*, tasks::{AsyncComputeTaskPool, Task}}; -use std::time::{Instant, Duration}; -use rand::Rng; -use futures_lite::future; - -/// This example shows how to use the ECS and the AsyncComputeTaskPool -/// to spawn, poll, and complete tasks across systems and system ticks. - -// Number of cubes to spawn across the x, y, and z axis -const NUM_CUBES: u32 = 6; - -struct BoxMeshHandle(Handle); -struct BoxMaterialHandle(Handle); - -/// Startup system which runs only once and generates our Box Mesh -/// and Box Material assets, adds them to their respective Asset -/// Resources, and stores their handles as resources so we can access -/// them later when we're ready to render our Boxes -fn add_assets( - mut commands: Commands, - mut meshes: ResMut>, - mut materials: ResMut>, -){ - let box_mesh_handle = meshes.add(Mesh::from(shape::Cube { size: 0.25 })); - commands.insert_resource(BoxMeshHandle(box_mesh_handle)); - - let box_material_handle = materials.add(Color::rgb(1.0, 0.2, 0.3).into()); - commands.insert_resource(BoxMaterialHandle(box_material_handle)); -} - -/// This system generates tasks simulating computationally intensive -/// work that potentially spans multiple frames/ticks. A separate -/// system, handle_tasks, will poll the spawned tasks on subsequent -/// frames/ticks, and use the results to spawn cubes -fn spawn_tasks( - mut commands: Commands, - thread_pool: Res, -) { - for x in 0 ..NUM_CUBES { - for y in 0 ..NUM_CUBES { - for z in 0 ..NUM_CUBES { - - // Spawn new task on the AsyncComputeTaskPool - let task = thread_pool.spawn(async move { - - let mut rng = rand::thread_rng(); - let start_time = Instant::now(); - let duration = Duration::from_secs_f32(rng.gen_range(0.05..0.2)); - while Instant::now() - start_time < duration { - // Spinning for 'duration', simulating doing hard - // compute work generating translation coords! - } - - // Such hard work, all done! - Transform::from_translation(Vec3::new(x as f32, y as f32, z as f32)) - }); - - // Spawn new entity and add our new task as a component - commands.spawn().insert(task); - } - } - } -} - -/// This system queries for entities that have our Task component. It polls the -/// 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 our_entity_tasks: Query<(Entity, &mut Task)>, - box_mesh_handle: Res, - box_material_handle: Res, -) { - our_entity_tasks.for_each_mut(|(entity, mut task)| { - - if let Some(transform) = future::block_on(future::poll_once(&mut *task)) { - - // Add our new PbrBundle of components to our tagged entity - commands.entity(entity).insert_bundle(PbrBundle { - mesh: box_mesh_handle.0.clone(), - material: box_material_handle.0.clone(), - transform, - ..Default::default() - }); - - // Task is complete, so remove task component from entity - commands.entity(entity).remove::>(); - } - }); -} - -fn main() { - App::build() - .insert_resource(Msaa { samples: 4 }) - .add_plugins(DefaultPlugins) - .add_startup_system(setup_env.system()) - .add_startup_system(add_assets.system()) - .add_startup_system(spawn_tasks.system()) - .add_system(handle_tasks.system()) - .run(); -} - -/// This system is only used to setup light and camera for the environment -fn setup_env(mut commands: Commands) { - - // Used to center camera on spawned cubes - let offset = if NUM_CUBES % 2 == 0 { - (NUM_CUBES / 2) as f32 - 0.5 - } else { - (NUM_CUBES / 2) as f32 - }; - - // lights - commands.spawn_bundle(PointLightBundle { - transform: Transform::from_translation(Vec3::new(4.0, 12.0, 15.0)), - ..Default::default() - }); - - // camera - commands.spawn_bundle(PerspectiveCameraBundle { - transform: Transform::from_translation(Vec3::new(offset, offset, 15.0)) - .looking_at(Vec3::new(offset, offset, 0.0), Vec3::Y), - ..Default::default() - }); -} +use bevy::{ + prelude::*, + tasks::{AsyncComputeTaskPool, Task}, +}; +use futures_lite::future; +use rand::Rng; +use std::time::{Duration, Instant}; + +/// This example shows how to use the ECS and the AsyncComputeTaskPool +/// to spawn, poll, and complete tasks across systems and system ticks. + +// Number of cubes to spawn across the x, y, and z axis +const NUM_CUBES: u32 = 6; + +struct BoxMeshHandle(Handle); +struct BoxMaterialHandle(Handle); + +/// Startup system which runs only once and generates our Box Mesh +/// and Box Material assets, adds them to their respective Asset +/// Resources, and stores their handles as resources so we can access +/// them later when we're ready to render our Boxes +fn add_assets( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + let box_mesh_handle = meshes.add(Mesh::from(shape::Cube { size: 0.25 })); + commands.insert_resource(BoxMeshHandle(box_mesh_handle)); + + let box_material_handle = materials.add(Color::rgb(1.0, 0.2, 0.3).into()); + commands.insert_resource(BoxMaterialHandle(box_material_handle)); +} + +/// This system generates tasks simulating computationally intensive +/// work that potentially spans multiple frames/ticks. A separate +/// system, handle_tasks, will poll the spawned tasks on subsequent +/// frames/ticks, and use the results to spawn cubes +fn spawn_tasks(mut commands: Commands, thread_pool: Res) { + for x in 0..NUM_CUBES { + for y in 0..NUM_CUBES { + for z in 0..NUM_CUBES { + // Spawn new task on the AsyncComputeTaskPool + let task = thread_pool.spawn(async move { + let mut rng = rand::thread_rng(); + let start_time = Instant::now(); + let duration = Duration::from_secs_f32(rng.gen_range(0.05..0.2)); + while Instant::now() - start_time < duration { + // Spinning for 'duration', simulating doing hard + // compute work generating translation coords! + } + + // Such hard work, all done! + Transform::from_translation(Vec3::new(x as f32, y as f32, z as f32)) + }); + + // Spawn new entity and add our new task as a component + commands.spawn().insert(task); + } + } + } +} + +/// This system queries for entities that have our Task component. It polls the +/// 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 our_entity_tasks: Query<(Entity, &mut Task)>, + box_mesh_handle: Res, + box_material_handle: Res, +) { + our_entity_tasks.for_each_mut(|(entity, mut task)| { + if let Some(transform) = future::block_on(future::poll_once(&mut *task)) { + // Add our new PbrBundle of components to our tagged entity + commands.entity(entity).insert_bundle(PbrBundle { + mesh: box_mesh_handle.0.clone(), + material: box_material_handle.0.clone(), + transform, + ..Default::default() + }); + + // Task is complete, so remove task component from entity + commands.entity(entity).remove::>(); + } + }); +} + +fn main() { + App::build() + .insert_resource(Msaa { samples: 4 }) + .add_plugins(DefaultPlugins) + .add_startup_system(setup_env.system()) + .add_startup_system(add_assets.system()) + .add_startup_system(spawn_tasks.system()) + .add_system(handle_tasks.system()) + .run(); +} + +/// This system is only used to setup light and camera for the environment +fn setup_env(mut commands: Commands) { + // Used to center camera on spawned cubes + let offset = if NUM_CUBES % 2 == 0 { + (NUM_CUBES / 2) as f32 - 0.5 + } else { + (NUM_CUBES / 2) as f32 + }; + + // lights + commands.spawn_bundle(PointLightBundle { + transform: Transform::from_translation(Vec3::new(4.0, 12.0, 15.0)), + ..Default::default() + }); + + // camera + commands.spawn_bundle(PerspectiveCameraBundle { + transform: Transform::from_translation(Vec3::new(offset, offset, 15.0)) + .looking_at(Vec3::new(offset, offset, 0.0), Vec3::Y), + ..Default::default() + }); +} From a5e2753330c51f40107191faf060293cc0d7c1ae Mon Sep 17 00:00:00 2001 From: the-notable Date: Thu, 20 May 2021 23:23:41 -0700 Subject: [PATCH 22/22] made requested changes --- examples/async_tasks/async_compute.rs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/examples/async_tasks/async_compute.rs b/examples/async_tasks/async_compute.rs index 9eb1445b2d806..a02433cd1ac72 100644 --- a/examples/async_tasks/async_compute.rs +++ b/examples/async_tasks/async_compute.rs @@ -8,6 +8,16 @@ use std::time::{Duration, Instant}; /// This example shows how to use the ECS and the AsyncComputeTaskPool /// to spawn, poll, and complete tasks across systems and system ticks. +fn main() { + App::build() + .insert_resource(Msaa { samples: 4 }) + .add_plugins(DefaultPlugins) + .add_startup_system(setup_env.system()) + .add_startup_system(add_assets.system()) + .add_startup_system(spawn_tasks.system()) + .add_system(handle_tasks.system()) + .run(); +} // Number of cubes to spawn across the x, y, and z axis const NUM_CUBES: u32 = 6; @@ -66,11 +76,11 @@ fn spawn_tasks(mut commands: Commands, thread_pool: Res) { /// removes the task component from the entity. fn handle_tasks( mut commands: Commands, - mut our_entity_tasks: Query<(Entity, &mut Task)>, + mut transform_tasks: Query<(Entity, &mut Task)>, box_mesh_handle: Res, box_material_handle: Res, ) { - our_entity_tasks.for_each_mut(|(entity, mut task)| { + for (entity, mut task) in transform_tasks.iter_mut() { if let Some(transform) = future::block_on(future::poll_once(&mut *task)) { // Add our new PbrBundle of components to our tagged entity commands.entity(entity).insert_bundle(PbrBundle { @@ -83,18 +93,7 @@ fn handle_tasks( // Task is complete, so remove task component from entity commands.entity(entity).remove::>(); } - }); -} - -fn main() { - App::build() - .insert_resource(Msaa { samples: 4 }) - .add_plugins(DefaultPlugins) - .add_startup_system(setup_env.system()) - .add_startup_system(add_assets.system()) - .add_startup_system(spawn_tasks.system()) - .add_system(handle_tasks.system()) - .run(); + } } /// This system is only used to setup light and camera for the environment