From 385a2b189528d0c6c706f7d25969414b61450774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Sat, 5 Feb 2022 01:52:47 +0000 Subject: [PATCH] add examples on how to have a data source running in another thread / in a task pool thread (#2915) Add two examples on how to communicate with a task that is running either in another thread or in a thread from `AsyncComputeTaskPool`. Loosely based on https://github.com/bevyengine/bevy/discussions/1150 --- Cargo.toml | 5 ++ examples/README.md | 1 + .../external_source_external_thread.rs | 89 +++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 examples/async_tasks/external_source_external_thread.rs diff --git a/Cargo.toml b/Cargo.toml index 95df03bd94..22e4005cb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,6 +105,7 @@ serde = { version = "1", features = ["derive"] } bytemuck = "1.7" # Needed to poll Task examples futures-lite = "1.11.3" +crossbeam-channel = "0.5.0" [[example]] name = "hello_world" @@ -287,6 +288,10 @@ path = "examples/asset/hot_asset_reloading.rs" name = "async_compute" path = "examples/async_tasks/async_compute.rs" +[[example]] +name = "external_source_external_thread" +path = "examples/async_tasks/external_source_external_thread.rs" + # Audio [[example]] name = "audio" diff --git a/examples/README.md b/examples/README.md index 8444192369..6bfd2569ee 100644 --- a/examples/README.md +++ b/examples/README.md @@ -145,6 +145,7 @@ Example | File | Description Example | File | Description --- | --- | --- `async_compute` | [`async_tasks/async_compute.rs`](async_tasks/async_compute.rs) | How to use `AsyncComputeTaskPool` to complete longer running tasks +`external_source_external_thread` | [`async_tasks/external_source_external_thread.rs`](async_tasks/external_source_external_thread.rs) | How to use an external thread to run an infinite task and communicate with a channel ## Audio diff --git a/examples/async_tasks/external_source_external_thread.rs b/examples/async_tasks/external_source_external_thread.rs new file mode 100644 index 0000000000..7c3cd01cf3 --- /dev/null +++ b/examples/async_tasks/external_source_external_thread.rs @@ -0,0 +1,89 @@ +use bevy::prelude::*; +// Using crossbeam_channel instead of std as std `Receiver` is `!Sync` +use crossbeam_channel::{bounded, Receiver}; +use rand::Rng; +use std::time::{Duration, Instant}; + +fn main() { + App::new() + .add_event::() + .add_plugins(DefaultPlugins) + .add_startup_system(setup) + .add_system(read_stream) + .add_system(spawn_text) + .add_system(move_text) + .run(); +} + +struct StreamReceiver(Receiver); +struct StreamEvent(u32); + +struct LoadedFont(Handle); + +fn setup(mut commands: Commands, asset_server: Res) { + commands.spawn_bundle(OrthographicCameraBundle::new_2d()); + + let (tx, rx) = bounded::(10); + std::thread::spawn(move || loop { + // Everything here happens in another thread + // This is where you could connect to an external data source + let mut rng = rand::thread_rng(); + let start_time = Instant::now(); + let duration = Duration::from_secs_f32(rng.gen_range(0.0..0.2)); + while Instant::now() - start_time < duration { + // Spinning for 'duration', simulating doing hard work! + } + + tx.send(rng.gen_range(0..2000)).unwrap(); + }); + + commands.insert_resource(StreamReceiver(rx)); + commands.insert_resource(LoadedFont(asset_server.load("fonts/FiraSans-Bold.ttf"))); +} + +// This system reads from the receiver and sends events to Bevy +fn read_stream(receiver: ResMut, mut events: EventWriter) { + for from_stream in receiver.0.try_iter() { + events.send(StreamEvent(from_stream)) + } +} + +fn spawn_text( + mut commands: Commands, + mut reader: EventReader, + loaded_font: Res, +) { + let text_style = TextStyle { + font: loaded_font.0.clone(), + font_size: 20.0, + color: Color::WHITE, + }; + let text_alignment = TextAlignment { + vertical: VerticalAlign::Center, + horizontal: HorizontalAlign::Center, + }; + for (per_frame, event) in reader.iter().enumerate() { + commands.spawn_bundle(Text2dBundle { + text: Text::with_section(format!("{}", event.0), text_style.clone(), text_alignment), + transform: Transform::from_xyz( + per_frame as f32 * 100.0 + rand::thread_rng().gen_range(-40.0..40.0), + 300.0, + 0.0, + ), + ..Default::default() + }); + } +} + +fn move_text( + mut commands: Commands, + mut texts: Query<(Entity, &mut Transform), With>, + time: Res