From 3397750c4d392c7668d3567335db9f9cfe2b7f95 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Tue, 23 Jun 2020 00:14:15 -0400 Subject: [PATCH 1/4] [rs] Staging belt, use LocalSpawner for the framework --- wgpu/examples/boids/main.rs | 3 +- wgpu/examples/cube/main.rs | 1 + wgpu/examples/framework.rs | 55 +++++++++-- wgpu/examples/mipmap/main.rs | 1 + wgpu/examples/msaa-line/main.rs | 1 + wgpu/examples/shadow/main.rs | 1 + wgpu/examples/skybox/main.rs | 21 ++++- wgpu/examples/texture-arrays/main.rs | 1 + wgpu/examples/water/main.rs | 1 + wgpu/src/lib.rs | 6 +- wgpu/src/util/belt.rs | 132 +++++++++++++++++++++++++++ wgpu/src/util/mod.rs | 4 + 12 files changed, 207 insertions(+), 20 deletions(-) create mode 100644 wgpu/src/util/belt.rs diff --git a/wgpu/examples/boids/main.rs b/wgpu/examples/boids/main.rs index 2c38f1e456..2b4553e5fb 100644 --- a/wgpu/examples/boids/main.rs +++ b/wgpu/examples/boids/main.rs @@ -1,8 +1,6 @@ // Flocking boids example with gpu compute update pass // adapted from https://github.com/austinEng/webgpu-samples/blob/master/src/examples/computeBoids.ts -extern crate rand; - #[path = "../framework.rs"] mod framework; @@ -257,6 +255,7 @@ impl framework::Example for Example { frame: &wgpu::SwapChainTexture, device: &wgpu::Device, _queue: &wgpu::Queue, + _spawner: &impl futures::task::LocalSpawn, ) -> wgpu::CommandBuffer { // create render pass descriptor let render_pass_descriptor = wgpu::RenderPassDescriptor { diff --git a/wgpu/examples/cube/main.rs b/wgpu/examples/cube/main.rs index 26e57a38da..3c1f30e475 100644 --- a/wgpu/examples/cube/main.rs +++ b/wgpu/examples/cube/main.rs @@ -318,6 +318,7 @@ impl framework::Example for Example { frame: &wgpu::SwapChainTexture, device: &wgpu::Device, _queue: &wgpu::Queue, + _spawner: &impl futures::task::LocalSpawn, ) -> wgpu::CommandBuffer { let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); diff --git a/wgpu/examples/framework.rs b/wgpu/examples/framework.rs index 519d0acd38..2f49613e21 100644 --- a/wgpu/examples/framework.rs +++ b/wgpu/examples/framework.rs @@ -1,4 +1,6 @@ -use std::time; +use futures::task::LocalSpawn; +#[cfg(not(target_arch = "wasm32"))] +use std::time::{Duration, Instant}; use winit::{ event::{self, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -49,10 +51,15 @@ pub trait Example: 'static + Sized { frame: &wgpu::SwapChainTexture, device: &wgpu::Device, queue: &wgpu::Queue, + spawner: &impl LocalSpawn, ) -> wgpu::CommandBuffer; } -async fn run_async(event_loop: EventLoop<()>, window: Window) { +async fn run_async( + event_loop: EventLoop<()>, + window: Window, + spawner: S, +) { log::info!("Initializing the surface..."); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); @@ -111,7 +118,7 @@ async fn run_async(event_loop: EventLoop<()>, window: Window) { } #[cfg(not(target_arch = "wasm32"))] - let mut last_update_inst = time::Instant::now(); + let mut last_update_inst = Instant::now(); log::info!("Entering render loop..."); event_loop.run(move |event, _, control_flow| { @@ -121,7 +128,7 @@ async fn run_async(event_loop: EventLoop<()>, window: Window) { } else { #[cfg(not(target_arch = "wasm32"))] { - ControlFlow::WaitUntil(time::Instant::now() + time::Duration::from_millis(10)) + ControlFlow::WaitUntil(Instant::now() + Duration::from_millis(10)) } #[cfg(target_arch = "wasm32")] { @@ -132,9 +139,9 @@ async fn run_async(event_loop: EventLoop<()>, window: Window) { event::Event::MainEventsCleared => { #[cfg(not(target_arch = "wasm32"))] { - if last_update_inst.elapsed() > time::Duration::from_millis(20) { + if last_update_inst.elapsed() > Duration::from_millis(20) { window.request_redraw(); - last_update_inst = time::Instant::now(); + last_update_inst = Instant::now(); } } @@ -178,7 +185,7 @@ async fn run_async(event_loop: EventLoop<()>, window: Window) { } }; - let command_buf = example.render(&frame.output, &device, &queue); + let command_buf = example.render(&frame.output, &device, &queue, &spawner); queue.submit(Some(command_buf)); } _ => {} @@ -199,6 +206,11 @@ pub fn run(title: &str) { #[cfg(not(target_arch = "wasm32"))] { + use futures::{ + executor::{LocalPool, LocalSpawner}, + task::LocalSpawnExt, + }; + env_logger::init(); #[cfg(feature = "subscriber")] @@ -207,13 +219,31 @@ pub fn run(title: &str) { wgpu::util::initialize_default_subscriber(chrome_tracing_dir.ok()); }; - futures::executor::block_on(run_async::(event_loop, window)); + let mut local_pool = LocalPool::new(); + let spawner = local_pool.spawner(); + local_pool + .spawner() + .spawn_local(run_async::(event_loop, window, spawner)) + .unwrap(); + local_pool.run(); } #[cfg(target_arch = "wasm32")] { + use futures::{future::LocalFutureObj, task::SpawnError}; + use winit::platform::web::WindowExtWebSys; + + struct WebSpawner {} + impl LocalSpawn for WebSpawner { + fn spawn_local_obj( + &self, + future: LocalFutureObj<'static, ()>, + ) -> Result<(), SpawnError> { + Ok(wasm_bindgen_futures::spawn_local(future)) + } + } + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); console_log::init().expect("could not initialize logger"); - use winit::platform::web::WindowExtWebSys; // On wasm, append the canvas to the document body web_sys::window() .and_then(|win| win.document()) @@ -223,7 +253,12 @@ pub fn run(title: &str) { .ok() }) .expect("couldn't append canvas to document body"); - wasm_bindgen_futures::spawn_local(run_async::(event_loop, window)); + + wasm_bindgen_futures::spawn_local(run_async::( + event_loop, + window, + WebSpawner {}, + )); } } diff --git a/wgpu/examples/mipmap/main.rs b/wgpu/examples/mipmap/main.rs index da2c634907..62bf23326c 100644 --- a/wgpu/examples/mipmap/main.rs +++ b/wgpu/examples/mipmap/main.rs @@ -405,6 +405,7 @@ impl framework::Example for Example { frame: &wgpu::SwapChainTexture, device: &wgpu::Device, _queue: &wgpu::Queue, + _spawner: &impl futures::task::LocalSpawn, ) -> wgpu::CommandBuffer { let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); diff --git a/wgpu/examples/msaa-line/main.rs b/wgpu/examples/msaa-line/main.rs index 482e74a9fe..b4024ebfa8 100644 --- a/wgpu/examples/msaa-line/main.rs +++ b/wgpu/examples/msaa-line/main.rs @@ -235,6 +235,7 @@ impl framework::Example for Example { frame: &wgpu::SwapChainTexture, device: &wgpu::Device, _queue: &wgpu::Queue, + _spawner: &impl futures::task::LocalSpawn, ) -> wgpu::CommandBuffer { if self.rebuild_bundle { self.bundle = Example::create_bundle( diff --git a/wgpu/examples/shadow/main.rs b/wgpu/examples/shadow/main.rs index c243e870aa..036cba5ab6 100644 --- a/wgpu/examples/shadow/main.rs +++ b/wgpu/examples/shadow/main.rs @@ -708,6 +708,7 @@ impl framework::Example for Example { frame: &wgpu::SwapChainTexture, device: &wgpu::Device, queue: &wgpu::Queue, + _spawner: &impl futures::task::LocalSpawn, ) -> wgpu::CommandBuffer { // update uniforms for entity in self.entities.iter_mut() { diff --git a/wgpu/examples/skybox/main.rs b/wgpu/examples/skybox/main.rs index 05cb66bfba..f0766fe268 100644 --- a/wgpu/examples/skybox/main.rs +++ b/wgpu/examples/skybox/main.rs @@ -1,3 +1,5 @@ +use futures::task::{LocalSpawn, LocalSpawnExt}; + #[path = "../framework.rs"] mod framework; @@ -19,6 +21,7 @@ pub struct Skybox { bind_group: wgpu::BindGroup, uniform_buf: wgpu::Buffer, uniforms: Uniforms, + staging_belt: wgpu::util::StagingBelt, } impl Skybox { @@ -234,6 +237,7 @@ impl framework::Example for Skybox { uniform_buf, aspect, uniforms, + staging_belt: wgpu::util::StagingBelt::new(0x100, device), }, None, ) @@ -261,15 +265,22 @@ impl framework::Example for Skybox { frame: &wgpu::SwapChainTexture, device: &wgpu::Device, queue: &wgpu::Queue, + spawner: &impl LocalSpawn, ) -> wgpu::CommandBuffer { // update rotation let rotation = cgmath::Matrix4::::from_angle_x(cgmath::Deg(0.25)); self.uniforms[1] = self.uniforms[1] * rotation; - queue.write_buffer( - &self.uniform_buf, - 0, - bytemuck::cast_slice(&raw_uniforms(&self.uniforms)), - ); + let raw_uniforms = raw_uniforms(&self.uniforms); + self.staging_belt + .write_buffer( + &self.uniform_buf, + 0, + wgpu::BufferSize::new((raw_uniforms.len() * 4) as wgpu::BufferAddress).unwrap(), + device, + ) + .copy_from_slice(bytemuck::cast_slice(&raw_uniforms)); + let upload_future = self.staging_belt.flush(queue, device); + spawner.spawn_local(upload_future).unwrap(); let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); diff --git a/wgpu/examples/texture-arrays/main.rs b/wgpu/examples/texture-arrays/main.rs index ce9b1b716c..ca982bea3e 100644 --- a/wgpu/examples/texture-arrays/main.rs +++ b/wgpu/examples/texture-arrays/main.rs @@ -362,6 +362,7 @@ impl framework::Example for Example { frame: &wgpu::SwapChainTexture, device: &wgpu::Device, _queue: &wgpu::Queue, + _spawner: &impl futures::task::LocalSpawn, ) -> wgpu::CommandBuffer { let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("primary"), diff --git a/wgpu/examples/water/main.rs b/wgpu/examples/water/main.rs index b280e248b0..1de64f93a3 100644 --- a/wgpu/examples/water/main.rs +++ b/wgpu/examples/water/main.rs @@ -680,6 +680,7 @@ impl framework::Example for Example { frame: &wgpu::SwapChainTexture, device: &wgpu::Device, queue: &wgpu::Queue, + _spawner: &impl futures::task::LocalSpawn, ) -> wgpu::CommandBuffer { // Increment frame count regardless of if we draw. self.current_frame += 1; diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 87826c449c..2defa601db 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -1433,7 +1433,7 @@ impl Buffer { } } -impl BufferSlice<'_> { +impl<'a> BufferSlice<'a> { //TODO: fn slice(&self) -> Self /// Map the buffer. Buffer is ready to map once the future is resolved. @@ -1473,7 +1473,7 @@ impl BufferSlice<'_> { /// Synchronously and immediately map a buffer for reading. If the buffer is not immediately mappable /// through [`BufferDescriptor::mapped_at_creation`] or [`BufferSlice::map_async`], will panic. - pub fn get_mapped_range(&self) -> BufferView { + pub fn get_mapped_range(&self) -> BufferView<'a> { let end = self.buffer.map_context.lock().add(self.offset, self.size); let data = Context::buffer_get_mapped_range( &*self.buffer.context, @@ -1485,7 +1485,7 @@ impl BufferSlice<'_> { /// Synchronously and immediately map a buffer for writing. If the buffer is not immediately mappable /// through [`BufferDescriptor::mapped_at_creation`] or [`BufferSlice::map_async`], will panic. - pub fn get_mapped_range_mut(&self) -> BufferViewMut { + pub fn get_mapped_range_mut(&self) -> BufferViewMut<'a> { let end = self.buffer.map_context.lock().add(self.offset, self.size); let data = Context::buffer_get_mapped_range_mut( &*self.buffer.context, diff --git a/wgpu/src/util/belt.rs b/wgpu/src/util/belt.rs new file mode 100644 index 0000000000..c1a3fa30d0 --- /dev/null +++ b/wgpu/src/util/belt.rs @@ -0,0 +1,132 @@ +use crate::{ + Buffer, BufferAddress, BufferDescriptor, BufferSize, BufferUsage, BufferViewMut, + CommandEncoder, CommandEncoderDescriptor, Device, MapMode, Queue, +}; +use futures::{future::join_all, FutureExt}; +use std::{future::Future, iter, mem, sync::mpsc}; + +struct Chunk { + buffer: Buffer, + size: BufferAddress, + offset: BufferAddress, +} + +/// Staging belt is a machine that uploads data. +/// +/// Internally it uses a ring-buffer of staging buffers that are sub-allocated. +/// It has an advantage over `Queue.write_buffer` in a way that it returns a mutable slice, +/// which you can fill to avoid an extra data copy. +pub struct StagingBelt { + chunk_size: BufferAddress, + encoder: CommandEncoder, + active_chunks: Vec, + free_chunks: Vec, + sender: mpsc::Sender, + receiver: mpsc::Receiver, +} + +impl StagingBelt { + /// Create a new staging belt. + /// + /// The `chunk_size` is the unit of internal buffer allocation. + /// It's better when it's big, but ideally still 1-4 times less than + /// the total amount of data uploaded per submission. + pub fn new(chunk_size: BufferAddress, device: &Device) -> Self { + let (sender, receiver) = mpsc::channel(); + StagingBelt { + chunk_size, + encoder: device.create_command_encoder(&CommandEncoderDescriptor::default()), + active_chunks: Vec::new(), + free_chunks: Vec::new(), + sender, + receiver, + } + } + + /// Allocate the staging belt slice of `size` to be uploaded into the `target` buffer + /// at the specified offset. + /// + /// The upload will only really be scheduled at the next `StagingBelt::flush` call. + pub fn write_buffer( + &mut self, + target: &Buffer, + offset: BufferAddress, + size: BufferSize, + device: &Device, + ) -> BufferViewMut { + assert_eq!( + size.get() % crate::COPY_BUFFER_ALIGNMENT, + 0, + "Size has to be aligned to the COPY_BUFFER_ALIGNMENT" + ); + + while let Ok(mut chunk) = self.receiver.try_recv() { + chunk.offset = 0; + self.free_chunks.push(chunk); + } + let mut chunk = if let Some(index) = self + .active_chunks + .iter() + .position(|chunk| chunk.offset + size.get() <= chunk.size) + { + self.active_chunks.swap_remove(index) + } else if let Some(index) = self + .free_chunks + .iter() + .position(|chunk| size.get() <= chunk.size) + { + self.free_chunks.swap_remove(index) + } else { + let size = self.chunk_size.max(size.get()); + //log::info!("Creating chunk of size {}", size); + Chunk { + buffer: device.create_buffer(&BufferDescriptor { + label: Some("staging"), + size, + usage: BufferUsage::MAP_WRITE | BufferUsage::COPY_SRC, + mapped_at_creation: true, + }), + size, + offset: 0, + } + }; + + self.encoder + .copy_buffer_to_buffer(&chunk.buffer, chunk.offset, target, offset, size.get()); + let old_offset = chunk.offset; + chunk.offset += size.get(); + self.active_chunks.push(chunk); + self.active_chunks + .last() + .unwrap() + .buffer + .slice(old_offset..old_offset + size.get()) + .get_mapped_range_mut() + } + + /// Schedule all the pending data uploads to the `queue`. + /// + /// At this point, all the partially used staging buffers are closed until + /// the GPU is done copying the data from them. + pub fn flush(&mut self, queue: &Queue, device: &Device) -> impl Future + Send { + //log::debug!("Flushing {} chunks", self.active_chunks.len()); + for chunk in self.active_chunks.iter() { + chunk.buffer.unmap(); + } + let current = mem::replace( + &mut self.encoder, + device.create_command_encoder(&CommandEncoderDescriptor::default()), + ); + queue.submit(iter::once(current.finish())); + let sender_template = &self.sender; + join_all(self.active_chunks.drain(..).map(|chunk| { + let sender = sender_template.clone(); + chunk + .buffer + .slice(..) + .map_async(MapMode::Write) + .inspect(move |_| sender.send(chunk).unwrap()) + })) + .map(|_| ()) + } +} diff --git a/wgpu/src/util/mod.rs b/wgpu/src/util/mod.rs index cc201a0413..60dcb0515b 100644 --- a/wgpu/src/util/mod.rs +++ b/wgpu/src/util/mod.rs @@ -1,6 +1,10 @@ +mod belt; + #[cfg(all(not(target_arch = "wasm32"), feature = "subscriber"))] pub use wgc::logging::subscriber::{initialize_default_subscriber, ChromeTracingLayer}; +pub use belt::StagingBelt; + /// Wrapper aligning contents to at least 4. #[repr(align(4))] pub struct WordAligned(pub Bytes); From f042b4a7e946aeef128b5c4d5eacc803b3858296 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Tue, 23 Jun 2020 23:51:47 -0400 Subject: [PATCH 2/4] [rs] Refactor examples API, improve belt documentation and logic --- wgpu/examples/boids/main.rs | 29 ++++++-------- wgpu/examples/cube/main.rs | 13 +++--- wgpu/examples/framework.rs | 12 ++---- wgpu/examples/mipmap/main.rs | 16 ++++---- wgpu/examples/msaa-line/main.rs | 13 +++--- wgpu/examples/shadow/main.rs | 13 +++--- wgpu/examples/skybox/main.rs | 37 ++++++++--------- wgpu/examples/texture-arrays/main.rs | 25 +++++------- wgpu/examples/water/main.rs | 31 +++++++------- wgpu/src/util/belt.rs | 60 ++++++++++++++++++---------- 10 files changed, 126 insertions(+), 123 deletions(-) diff --git a/wgpu/examples/boids/main.rs b/wgpu/examples/boids/main.rs index 2b4553e5fb..a9304e18d8 100644 --- a/wgpu/examples/boids/main.rs +++ b/wgpu/examples/boids/main.rs @@ -29,7 +29,7 @@ impl framework::Example for Example { sc_desc: &wgpu::SwapChainDescriptor, device: &wgpu::Device, _queue: &wgpu::Queue, - ) -> (Self, Option) { + ) -> Self { // load (and compile) shaders and create shader modules let boids_module = device.create_shader_module(wgpu::include_spirv!("boids.comp.spv")); @@ -219,18 +219,15 @@ impl framework::Example for Example { // returns Example struct and No encoder commands - ( - Example { - particle_bind_groups, - particle_buffers, - vertices_buffer, - compute_pipeline, - render_pipeline, - work_group_count, - frame_num: 0, - }, - None, - ) + Example { + particle_bind_groups, + particle_buffers, + vertices_buffer, + compute_pipeline, + render_pipeline, + work_group_count, + frame_num: 0, + } } /// update is called for any WindowEvent not handled by the framework @@ -254,9 +251,9 @@ impl framework::Example for Example { &mut self, frame: &wgpu::SwapChainTexture, device: &wgpu::Device, - _queue: &wgpu::Queue, + queue: &wgpu::Queue, _spawner: &impl futures::task::LocalSpawn, - ) -> wgpu::CommandBuffer { + ) { // create render pass descriptor let render_pass_descriptor = wgpu::RenderPassDescriptor { color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { @@ -297,7 +294,7 @@ impl framework::Example for Example { self.frame_num += 1; // done - command_encoder.finish() + queue.submit(Some(command_encoder.finish())); } } diff --git a/wgpu/examples/cube/main.rs b/wgpu/examples/cube/main.rs index 3c1f30e475..f599c69357 100644 --- a/wgpu/examples/cube/main.rs +++ b/wgpu/examples/cube/main.rs @@ -116,7 +116,7 @@ impl framework::Example for Example { sc_desc: &wgpu::SwapChainDescriptor, device: &wgpu::Device, queue: &wgpu::Queue, - ) -> (Self, Option) { + ) -> Self { use std::mem; // Create the vertex and index buffers @@ -287,15 +287,14 @@ impl framework::Example for Example { }); // Done - let this = Example { + Example { vertex_buf, index_buf, index_count: index_data.len(), bind_group, uniform_buf, pipeline, - }; - (this, None) + } } fn update(&mut self, _event: winit::event::WindowEvent) { @@ -317,9 +316,9 @@ impl framework::Example for Example { &mut self, frame: &wgpu::SwapChainTexture, device: &wgpu::Device, - _queue: &wgpu::Queue, + queue: &wgpu::Queue, _spawner: &impl futures::task::LocalSpawn, - ) -> wgpu::CommandBuffer { + ) { let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); { @@ -349,7 +348,7 @@ impl framework::Example for Example { rpass.draw_indexed(0..self.index_count as u32, 0, 0..1); } - encoder.finish() + queue.submit(Some(encoder.finish())); } } diff --git a/wgpu/examples/framework.rs b/wgpu/examples/framework.rs index 2f49613e21..8e8a41e18d 100644 --- a/wgpu/examples/framework.rs +++ b/wgpu/examples/framework.rs @@ -38,7 +38,7 @@ pub trait Example: 'static + Sized { sc_desc: &wgpu::SwapChainDescriptor, device: &wgpu::Device, queue: &wgpu::Queue, - ) -> (Self, Option); + ) -> Self; fn resize( &mut self, sc_desc: &wgpu::SwapChainDescriptor, @@ -52,7 +52,7 @@ pub trait Example: 'static + Sized { device: &wgpu::Device, queue: &wgpu::Queue, spawner: &impl LocalSpawn, - ) -> wgpu::CommandBuffer; + ); } async fn run_async( @@ -112,10 +112,7 @@ async fn run_async( let mut swap_chain = device.create_swap_chain(&surface, &sc_desc); log::info!("Initializing the example..."); - let (mut example, init_command_buf) = E::init(&sc_desc, &device, &queue); - if init_command_buf.is_some() { - queue.submit(init_command_buf); - } + let mut example = E::init(&sc_desc, &device, &queue); #[cfg(not(target_arch = "wasm32"))] let mut last_update_inst = Instant::now(); @@ -185,8 +182,7 @@ async fn run_async( } }; - let command_buf = example.render(&frame.output, &device, &queue, &spawner); - queue.submit(Some(command_buf)); + example.render(&frame.output, &device, &queue, &spawner); } _ => {} } diff --git a/wgpu/examples/mipmap/main.rs b/wgpu/examples/mipmap/main.rs index 62bf23326c..ebf75cfa73 100644 --- a/wgpu/examples/mipmap/main.rs +++ b/wgpu/examples/mipmap/main.rs @@ -204,8 +204,8 @@ impl framework::Example for Example { fn init( sc_desc: &wgpu::SwapChainDescriptor, device: &wgpu::Device, - _queue: &wgpu::Queue, - ) -> (Self, Option) { + queue: &wgpu::Queue, + ) -> Self { use std::mem; let mut init_encoder = @@ -375,14 +375,14 @@ impl framework::Example for Example { // Done Self::generate_mipmaps(&mut init_encoder, &device, &texture, mip_level_count); + queue.submit(Some(init_encoder.finish())); - let this = Example { + Example { vertex_buf, bind_group, uniform_buf, draw_pipeline, - }; - (this, Some(init_encoder.finish())) + } } fn update(&mut self, _event: winit::event::WindowEvent) { @@ -404,9 +404,9 @@ impl framework::Example for Example { &mut self, frame: &wgpu::SwapChainTexture, device: &wgpu::Device, - _queue: &wgpu::Queue, + queue: &wgpu::Queue, _spawner: &impl futures::task::LocalSpawn, - ) -> wgpu::CommandBuffer { + ) { let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); { @@ -433,7 +433,7 @@ impl framework::Example for Example { rpass.draw(0..4, 0..1); } - encoder.finish() + queue.submit(Some(encoder.finish())); } } diff --git a/wgpu/examples/msaa-line/main.rs b/wgpu/examples/msaa-line/main.rs index b4024ebfa8..f42c9f92f0 100644 --- a/wgpu/examples/msaa-line/main.rs +++ b/wgpu/examples/msaa-line/main.rs @@ -132,7 +132,7 @@ impl framework::Example for Example { sc_desc: &wgpu::SwapChainDescriptor, device: &wgpu::Device, _queue: &wgpu::Queue, - ) -> (Self, Option) { + ) -> Self { log::info!("Press left/right arrow keys to change sample_count."); let sample_count = 4; @@ -179,7 +179,7 @@ impl framework::Example for Example { vertex_count, ); - let this = Example { + Example { bundle, vs_module, fs_module, @@ -190,8 +190,7 @@ impl framework::Example for Example { sample_count, rebuild_bundle: false, sc_desc: sc_desc.clone(), - }; - (this, None) + } } fn update(&mut self, event: winit::event::WindowEvent) { @@ -234,9 +233,9 @@ impl framework::Example for Example { &mut self, frame: &wgpu::SwapChainTexture, device: &wgpu::Device, - _queue: &wgpu::Queue, + queue: &wgpu::Queue, _spawner: &impl futures::task::LocalSpawn, - ) -> wgpu::CommandBuffer { + ) { if self.rebuild_bundle { self.bundle = Example::create_bundle( device, @@ -282,7 +281,7 @@ impl framework::Example for Example { .execute_bundles(iter::once(&self.bundle)); } - encoder.finish() + queue.submit(iter::once(encoder.finish())); } } diff --git a/wgpu/examples/shadow/main.rs b/wgpu/examples/shadow/main.rs index 036cba5ab6..8da8837b49 100644 --- a/wgpu/examples/shadow/main.rs +++ b/wgpu/examples/shadow/main.rs @@ -1,4 +1,4 @@ -use std::{mem, ops::Range, rc::Rc}; +use std::{iter, mem, ops::Range, rc::Rc}; #[path = "../framework.rs"] mod framework; @@ -207,7 +207,7 @@ impl framework::Example for Example { sc_desc: &wgpu::SwapChainDescriptor, device: &wgpu::Device, _queue: &wgpu::Queue, - ) -> (Self, Option) { + ) -> Self { // Create the vertex and index buffers let vertex_size = mem::size_of::(); let (cube_vertex_data, cube_index_data) = create_cube(); @@ -656,7 +656,7 @@ impl framework::Example for Example { label: None, }); - let this = Example { + Example { entities, lights, lights_are_dirty: true, @@ -664,8 +664,7 @@ impl framework::Example for Example { forward_pass, forward_depth: depth_texture.create_default_view(), light_uniform_buf, - }; - (this, None) + } } fn update(&mut self, _event: winit::event::WindowEvent) { @@ -709,7 +708,7 @@ impl framework::Example for Example { device: &wgpu::Device, queue: &wgpu::Queue, _spawner: &impl futures::task::LocalSpawn, - ) -> wgpu::CommandBuffer { + ) { // update uniforms for entity in self.entities.iter_mut() { if entity.rotation_speed != 0.0 { @@ -811,7 +810,7 @@ impl framework::Example for Example { } } - encoder.finish() + queue.submit(iter::once(encoder.finish())); } } diff --git a/wgpu/examples/skybox/main.rs b/wgpu/examples/skybox/main.rs index f0766fe268..4a9b7836c4 100644 --- a/wgpu/examples/skybox/main.rs +++ b/wgpu/examples/skybox/main.rs @@ -1,8 +1,8 @@ -use futures::task::{LocalSpawn, LocalSpawnExt}; - #[path = "../framework.rs"] mod framework; +use futures::task::{LocalSpawn, LocalSpawnExt}; + const SKYBOX_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unorm; type Uniform = cgmath::Matrix4; @@ -42,7 +42,7 @@ impl framework::Example for Skybox { sc_desc: &wgpu::SwapChainDescriptor, device: &wgpu::Device, queue: &wgpu::Queue, - ) -> (Self, Option) { + ) -> Self { let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { bindings: &[ wgpu::BindGroupLayoutEntry::new( @@ -230,17 +230,15 @@ impl framework::Example for Skybox { ], label: None, }); - ( - Self { - pipeline, - bind_group, - uniform_buf, - aspect, - uniforms, - staging_belt: wgpu::util::StagingBelt::new(0x100, device), - }, - None, - ) + + Skybox { + pipeline, + bind_group, + uniform_buf, + aspect, + uniforms, + staging_belt: wgpu::util::StagingBelt::new(0x100, device), + } } fn update(&mut self, _event: winit::event::WindowEvent) { @@ -266,7 +264,7 @@ impl framework::Example for Skybox { device: &wgpu::Device, queue: &wgpu::Queue, spawner: &impl LocalSpawn, - ) -> wgpu::CommandBuffer { + ) { // update rotation let rotation = cgmath::Matrix4::::from_angle_x(cgmath::Deg(0.25)); self.uniforms[1] = self.uniforms[1] * rotation; @@ -279,8 +277,7 @@ impl framework::Example for Skybox { device, ) .copy_from_slice(bytemuck::cast_slice(&raw_uniforms)); - let upload_future = self.staging_belt.flush(queue, device); - spawner.spawn_local(upload_future).unwrap(); + let transfer_comb = self.staging_belt.flush(device); let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); @@ -306,7 +303,11 @@ impl framework::Example for Skybox { rpass.set_bind_group(0, &self.bind_group, &[]); rpass.draw(0..3 as u32, 0..1); } - encoder.finish() + + queue.submit(vec![transfer_comb, encoder.finish()]); + + let belt_future = self.staging_belt.recall(); + spawner.spawn_local(belt_future).unwrap(); } } diff --git a/wgpu/examples/texture-arrays/main.rs b/wgpu/examples/texture-arrays/main.rs index ca982bea3e..931bdba12e 100644 --- a/wgpu/examples/texture-arrays/main.rs +++ b/wgpu/examples/texture-arrays/main.rs @@ -95,7 +95,7 @@ impl framework::Example for Example { sc_desc: &wgpu::SwapChainDescriptor, device: &wgpu::Device, queue: &wgpu::Queue, - ) -> (Self, Option) { + ) -> Self { let mut uniform_workaround = false; let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv")); let fs_bytes: Vec = match device.capabilities() { @@ -335,16 +335,13 @@ impl framework::Example for Example { alpha_to_coverage_enabled: false, }); - ( - Self { - vertex_buffer, - index_buffer, - bind_group, - pipeline, - uniform_workaround_data, - }, - None, - ) + Self { + vertex_buffer, + index_buffer, + bind_group, + pipeline, + uniform_workaround_data, + } } fn resize( &mut self, @@ -361,9 +358,9 @@ impl framework::Example for Example { &mut self, frame: &wgpu::SwapChainTexture, device: &wgpu::Device, - _queue: &wgpu::Queue, + queue: &wgpu::Queue, _spawner: &impl futures::task::LocalSpawn, - ) -> wgpu::CommandBuffer { + ) { let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("primary"), }); @@ -394,7 +391,7 @@ impl framework::Example for Example { drop(rpass); - encoder.finish() + queue.submit(Some(encoder.finish())); } } diff --git a/wgpu/examples/water/main.rs b/wgpu/examples/water/main.rs index 1de64f93a3..050fe0ba11 100644 --- a/wgpu/examples/water/main.rs +++ b/wgpu/examples/water/main.rs @@ -4,7 +4,7 @@ mod framework; mod point_gen; use cgmath::Point3; -use std::mem; +use std::{iter, mem}; /// /// Radius of the terrain. @@ -269,7 +269,7 @@ impl framework::Example for Example { sc_desc: &wgpu::SwapChainDescriptor, device: &wgpu::Device, queue: &wgpu::Queue, - ) -> (Self, Option) { + ) -> Self { // Size of one water vertex let water_vertex_size = mem::size_of::(); @@ -609,7 +609,7 @@ impl framework::Example for Example { }); // Done - let this = Example { + Example { water_vertex_buf, water_vertex_count: water_vertices.len(), water_bind_group_layout, @@ -632,8 +632,7 @@ impl framework::Example for Example { current_frame: 0, active: Some(0), - }; - (this, None) + } } fn update(&mut self, _event: winit::event::WindowEvent) { @@ -681,7 +680,7 @@ impl framework::Example for Example { device: &wgpu::Device, queue: &wgpu::Queue, _spawner: &impl futures::task::LocalSpawn, - ) -> wgpu::CommandBuffer { + ) { // Increment frame count regardless of if we draw. self.current_frame += 1; let back_color = wgpu::Color { @@ -699,21 +698,21 @@ impl framework::Example for Example { bytemuck::cast_slice(&[water_sin, water_cos]), ); + // Only render valid frames. See resize method. + if let Some(active) = self.active { + if active >= self.current_frame { + return; + } + } else { + return; + } + // The encoder provides a way to turn our instructions here, into // a command buffer the GPU can understand. let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Main Command Encoder"), }); - // Only render valid frames. See resize method. - if let Some(active) = self.active { - if active >= self.current_frame { - return encoder.finish(); - } - } else { - return encoder.finish(); - } - // First pass: render the reflection. { let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { @@ -792,7 +791,7 @@ impl framework::Example for Example { rpass.draw(0..self.water_vertex_count as u32, 0..1); } - encoder.finish() + queue.submit(iter::once(encoder.finish())); } } diff --git a/wgpu/src/util/belt.rs b/wgpu/src/util/belt.rs index c1a3fa30d0..6d780785cd 100644 --- a/wgpu/src/util/belt.rs +++ b/wgpu/src/util/belt.rs @@ -1,9 +1,9 @@ use crate::{ - Buffer, BufferAddress, BufferDescriptor, BufferSize, BufferUsage, BufferViewMut, - CommandEncoder, CommandEncoderDescriptor, Device, MapMode, Queue, + Buffer, BufferAddress, BufferDescriptor, BufferSize, BufferUsage, BufferViewMut, CommandBuffer, + CommandEncoder, CommandEncoderDescriptor, Device, MapMode, }; use futures::{future::join_all, FutureExt}; -use std::{future::Future, iter, mem, sync::mpsc}; +use std::{future::Future, mem, sync::mpsc}; struct Chunk { buffer: Buffer, @@ -19,7 +19,11 @@ struct Chunk { pub struct StagingBelt { chunk_size: BufferAddress, encoder: CommandEncoder, + /// Chunks that we are actively using for pending transfers at this moment. active_chunks: Vec, + /// Chunks that have scheduled transfers already. + closed_chunks: Vec, + /// Chunks that are back from the GPU and ready to be used. free_chunks: Vec, sender: mpsc::Sender, receiver: mpsc::Receiver, @@ -37,6 +41,7 @@ impl StagingBelt { chunk_size, encoder: device.create_command_encoder(&CommandEncoderDescriptor::default()), active_chunks: Vec::new(), + closed_chunks: Vec::new(), free_chunks: Vec::new(), sender, receiver, @@ -54,16 +59,6 @@ impl StagingBelt { size: BufferSize, device: &Device, ) -> BufferViewMut { - assert_eq!( - size.get() % crate::COPY_BUFFER_ALIGNMENT, - 0, - "Size has to be aligned to the COPY_BUFFER_ALIGNMENT" - ); - - while let Ok(mut chunk) = self.receiver.try_recv() { - chunk.offset = 0; - self.free_chunks.push(chunk); - } let mut chunk = if let Some(index) = self .active_chunks .iter() @@ -78,7 +73,8 @@ impl StagingBelt { self.free_chunks.swap_remove(index) } else { let size = self.chunk_size.max(size.get()); - //log::info!("Creating chunk of size {}", size); + #[cfg(not(target_arch = "wasm32"))] + wgc::span!(_guard, INFO, "Creating chunk of size {}", size); Chunk { buffer: device.create_buffer(&BufferDescriptor { label: Some("staging"), @@ -95,6 +91,11 @@ impl StagingBelt { .copy_buffer_to_buffer(&chunk.buffer, chunk.offset, target, offset, size.get()); let old_offset = chunk.offset; chunk.offset += size.get(); + let remainder = chunk.offset % crate::COPY_BUFFER_ALIGNMENT; + if remainder != 0 { + chunk.offset += crate::COPY_BUFFER_ALIGNMENT - remainder; + } + self.active_chunks.push(chunk); self.active_chunks .last() @@ -104,22 +105,37 @@ impl StagingBelt { .get_mapped_range_mut() } - /// Schedule all the pending data uploads to the `queue`. + /// Produce a command buffer with all the accumulated transfers. /// /// At this point, all the partially used staging buffers are closed until /// the GPU is done copying the data from them. - pub fn flush(&mut self, queue: &Queue, device: &Device) -> impl Future + Send { - //log::debug!("Flushing {} chunks", self.active_chunks.len()); - for chunk in self.active_chunks.iter() { + pub fn flush(&mut self, device: &Device) -> CommandBuffer { + #[cfg(not(target_arch = "wasm32"))] + wgc::span!(_guard, DEBUG, "Flushing chunks"); + + for chunk in self.active_chunks.drain(..) { chunk.buffer.unmap(); + self.closed_chunks.push(chunk); } - let current = mem::replace( + + mem::replace( &mut self.encoder, device.create_command_encoder(&CommandEncoderDescriptor::default()), - ); - queue.submit(iter::once(current.finish())); + ) + .finish() + } + + /// Recall all of the closed buffers back for re-usal. + /// + /// This has to be called after the command buffer produced by `flush` is submitted! + pub fn recall(&mut self) -> impl Future + Send { + while let Ok(mut chunk) = self.receiver.try_recv() { + chunk.offset = 0; + self.free_chunks.push(chunk); + } + let sender_template = &self.sender; - join_all(self.active_chunks.drain(..).map(|chunk| { + join_all(self.closed_chunks.drain(..).map(|chunk| { let sender = sender_template.clone(); chunk .buffer From 6fb12c053729ee185635e59c0c01c05a50c4f7dc Mon Sep 17 00:00:00 2001 From: kyren Date: Wed, 24 Jun 2020 10:39:53 -0400 Subject: [PATCH 3/4] [rs] Get event loop + futures working on native, untested on web --- wgpu/examples/framework.rs | 198 +++++++++++++++++-------------------- 1 file changed, 92 insertions(+), 106 deletions(-) diff --git a/wgpu/examples/framework.rs b/wgpu/examples/framework.rs index 8e8a41e18d..6a052d7075 100644 --- a/wgpu/examples/framework.rs +++ b/wgpu/examples/framework.rs @@ -1,10 +1,9 @@ -use futures::task::LocalSpawn; +use futures::{task::{LocalSpawn}, executor::{block_on, LocalPool}}; #[cfg(not(target_arch = "wasm32"))] use std::time::{Duration, Instant}; use winit::{ event::{self, WindowEvent}, event_loop::{ControlFlow, EventLoop}, - window::Window, }; #[cfg_attr(rustfmt, rustfmt_skip)] @@ -55,47 +54,101 @@ pub trait Example: 'static + Sized { ); } -async fn run_async( - event_loop: EventLoop<()>, - window: Window, - spawner: S, -) { - log::info!("Initializing the surface..."); +pub fn run(title: &str) { + let event_loop = EventLoop::new(); + let mut builder = winit::window::WindowBuilder::new(); + builder = builder.with_title(title); + #[cfg(windows_OFF)] // TODO + { + use winit::platform::windows::WindowBuilderExtWindows; + builder = builder.with_no_redirection_bitmap(true); + } + let window = builder.build(&event_loop).unwrap(); - let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); - let (size, surface) = unsafe { - let size = window.inner_size(); - let surface = instance.create_surface(&window); - (size, surface) + #[cfg(not(target_arch = "wasm32"))] + let (mut pool, spawner) = { + env_logger::init(); + + #[cfg(feature = "subscriber")] + { + let chrome_tracing_dir = std::env::var("WGPU_CHROME_TRACING"); + wgpu::util::initialize_default_subscriber(chrome_tracing_dir.ok()); + }; + + let local_pool = LocalPool::new(); + let spawner = local_pool.spawner(); + (local_pool, spawner) + }; + #[cfg(target_arch = "wasm32")] + let spawner = { + use futures::{future::LocalFutureObj, task::SpawnError}; + use winit::platform::web::WindowExtWebSys; + + struct WebSpawner {} + impl LocalSpawn for WebSpawner { + fn spawn_local_obj( + &self, + future: LocalFutureObj<'static, ()>, + ) -> Result<(), SpawnError> { + Ok(wasm_bindgen_futures::spawn_local(future)) + } + } + + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + console_log::init().expect("could not initialize logger"); + // On wasm, append the canvas to the document body + web_sys::window() + .and_then(|win| win.document()) + .and_then(|doc| doc.body()) + .and_then(|body| { + body.append_child(&web_sys::Element::from(window.canvas())) + .ok() + }) + .expect("couldn't append canvas to document body"); + + WebSpawner {} }; - let (needed_extensions, unsafe_extensions) = E::needed_extensions(); + log::info!("Initializing the surface..."); - let adapter = instance - .request_adapter( - &wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::Default, - compatible_surface: Some(&surface), - }, - unsafe_extensions, - ) - .await - .unwrap(); + let (size, surface, instance, adapter, device, queue) = block_on(async { + let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); + let (size, surface) = unsafe { + let size = window.inner_size(); + let surface = instance.create_surface(&window); + (size, surface) + }; - let adapter_extensions = adapter.extensions(); + let (needed_extensions, unsafe_extensions) = E::needed_extensions(); - let trace_dir = std::env::var("WGPU_TRACE"); - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - extensions: adapter_extensions & needed_extensions, - limits: wgpu::Limits::default(), - shader_validation: true, - }, - trace_dir.ok().as_ref().map(std::path::Path::new), - ) - .await - .unwrap(); + let adapter = instance + .request_adapter( + &wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::Default, + compatible_surface: Some(&surface), + }, + unsafe_extensions, + ) + .await + .unwrap(); + + let adapter_extensions = adapter.extensions(); + + let trace_dir = std::env::var("WGPU_TRACE"); + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + extensions: adapter_extensions & needed_extensions, + limits: wgpu::Limits::default(), + shader_validation: true, + }, + trace_dir.ok().as_ref().map(std::path::Path::new), + ) + .await + .unwrap(); + + (size, surface, instance, adapter, device, queue) + }); let mut sc_desc = wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, @@ -140,6 +193,8 @@ async fn run_async( window.request_redraw(); last_update_inst = Instant::now(); } + + pool.run_until_stalled(); } #[cfg(target_arch = "wasm32")] @@ -189,75 +244,6 @@ async fn run_async( }); } -pub fn run(title: &str) { - let event_loop = EventLoop::new(); - let mut builder = winit::window::WindowBuilder::new(); - builder = builder.with_title(title); - #[cfg(windows_OFF)] // TODO - { - use winit::platform::windows::WindowBuilderExtWindows; - builder = builder.with_no_redirection_bitmap(true); - } - let window = builder.build(&event_loop).unwrap(); - - #[cfg(not(target_arch = "wasm32"))] - { - use futures::{ - executor::{LocalPool, LocalSpawner}, - task::LocalSpawnExt, - }; - - env_logger::init(); - - #[cfg(feature = "subscriber")] - { - let chrome_tracing_dir = std::env::var("WGPU_CHROME_TRACING"); - wgpu::util::initialize_default_subscriber(chrome_tracing_dir.ok()); - }; - - let mut local_pool = LocalPool::new(); - let spawner = local_pool.spawner(); - local_pool - .spawner() - .spawn_local(run_async::(event_loop, window, spawner)) - .unwrap(); - local_pool.run(); - } - #[cfg(target_arch = "wasm32")] - { - use futures::{future::LocalFutureObj, task::SpawnError}; - use winit::platform::web::WindowExtWebSys; - - struct WebSpawner {} - impl LocalSpawn for WebSpawner { - fn spawn_local_obj( - &self, - future: LocalFutureObj<'static, ()>, - ) -> Result<(), SpawnError> { - Ok(wasm_bindgen_futures::spawn_local(future)) - } - } - - std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - console_log::init().expect("could not initialize logger"); - // On wasm, append the canvas to the document body - web_sys::window() - .and_then(|win| win.document()) - .and_then(|doc| doc.body()) - .and_then(|body| { - body.append_child(&web_sys::Element::from(window.canvas())) - .ok() - }) - .expect("couldn't append canvas to document body"); - - wasm_bindgen_futures::spawn_local(run_async::( - event_loop, - window, - WebSpawner {}, - )); - } -} - // This allows treating the framework as a standalone example, // thus avoiding listing the example names in `Cargo.toml`. #[allow(dead_code)] From aae1f216e0704ef92956de8d875546de0acd08e2 Mon Sep 17 00:00:00 2001 From: kyren Date: Thu, 25 Jun 2020 13:00:23 -0400 Subject: [PATCH 4/4] [rs] A different attempt at the example framework --- wgpu/examples/framework.rs | 135 +++++++++++++++++++++++++------------ 1 file changed, 91 insertions(+), 44 deletions(-) diff --git a/wgpu/examples/framework.rs b/wgpu/examples/framework.rs index 6a052d7075..e665e4386c 100644 --- a/wgpu/examples/framework.rs +++ b/wgpu/examples/framework.rs @@ -1,4 +1,4 @@ -use futures::{task::{LocalSpawn}, executor::{block_on, LocalPool}}; +use futures::task::LocalSpawn; #[cfg(not(target_arch = "wasm32"))] use std::time::{Duration, Instant}; use winit::{ @@ -54,7 +54,18 @@ pub trait Example: 'static + Sized { ); } -pub fn run(title: &str) { +struct Setup { + window: winit::window::Window, + event_loop: EventLoop<()>, + instance: wgpu::Instance, + size: winit::dpi::PhysicalSize, + surface: wgpu::Surface, + adapter: wgpu::Adapter, + device: wgpu::Device, + queue: wgpu::Queue, +} + +async fn setup(title: &str) -> Setup { let event_loop = EventLoop::new(); let mut builder = winit::window::WindowBuilder::new(); builder = builder.with_title(title); @@ -65,6 +76,67 @@ pub fn run(title: &str) { } let window = builder.build(&event_loop).unwrap(); + log::info!("Initializing the surface..."); + + let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); + let (size, surface) = unsafe { + let size = window.inner_size(); + let surface = instance.create_surface(&window); + (size, surface) + }; + + let (needed_extensions, unsafe_extensions) = E::needed_extensions(); + + let adapter = instance + .request_adapter( + &wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::Default, + compatible_surface: Some(&surface), + }, + unsafe_extensions, + ) + .await + .unwrap(); + + let adapter_extensions = adapter.extensions(); + + let trace_dir = std::env::var("WGPU_TRACE"); + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + extensions: adapter_extensions & needed_extensions, + limits: wgpu::Limits::default(), + shader_validation: true, + }, + trace_dir.ok().as_ref().map(std::path::Path::new), + ) + .await + .unwrap(); + + Setup { + window, + event_loop, + instance, + size, + surface, + adapter, + device, + queue, + } +} + +fn start( + Setup { + window, + event_loop, + instance, + size, + surface, + adapter, + device, + queue, + }: Setup, +) { #[cfg(not(target_arch = "wasm32"))] let (mut pool, spawner) = { env_logger::init(); @@ -75,10 +147,11 @@ pub fn run(title: &str) { wgpu::util::initialize_default_subscriber(chrome_tracing_dir.ok()); }; - let local_pool = LocalPool::new(); + let local_pool = futures::executor::LocalPool::new(); let spawner = local_pool.spawner(); (local_pool, spawner) }; + #[cfg(target_arch = "wasm32")] let spawner = { use futures::{future::LocalFutureObj, task::SpawnError}; @@ -109,47 +182,6 @@ pub fn run(title: &str) { WebSpawner {} }; - log::info!("Initializing the surface..."); - - let (size, surface, instance, adapter, device, queue) = block_on(async { - let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); - let (size, surface) = unsafe { - let size = window.inner_size(); - let surface = instance.create_surface(&window); - (size, surface) - }; - - let (needed_extensions, unsafe_extensions) = E::needed_extensions(); - - let adapter = instance - .request_adapter( - &wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::Default, - compatible_surface: Some(&surface), - }, - unsafe_extensions, - ) - .await - .unwrap(); - - let adapter_extensions = adapter.extensions(); - - let trace_dir = std::env::var("WGPU_TRACE"); - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - extensions: adapter_extensions & needed_extensions, - limits: wgpu::Limits::default(), - shader_validation: true, - }, - trace_dir.ok().as_ref().map(std::path::Path::new), - ) - .await - .unwrap(); - - (size, surface, instance, adapter, device, queue) - }); - let mut sc_desc = wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, // TODO: Allow srgb unconditionally @@ -244,6 +276,21 @@ pub fn run(title: &str) { }); } +#[cfg(not(target_arch = "wasm32"))] +pub fn run(title: &str) { + let setup = futures::executor::block_on(setup::(title)); + start::(setup); +} + +#[cfg(target_arch = "wasm32")] +pub fn run(title: &str) { + let title = title.to_owned(); + wasm_bindgen_futures::spawn_local(async move { + let setup = setup::(&title).await; + start::(setup); + }); +} + // This allows treating the framework as a standalone example, // thus avoiding listing the example names in `Cargo.toml`. #[allow(dead_code)]