From 2d75e0473fb97a16c16a3d229aefa357d91e8428 Mon Sep 17 00:00:00 2001 From: Viktor Zoutman Date: Mon, 16 Nov 2020 12:21:33 +0100 Subject: [PATCH] Merged wgpu runners (#241) * Added clap to switch between shaders * Merged wgpu runners * tested something related to async which i forgot to udno * clippy * xamprocky suggestion --- Cargo.lock | 25 +- Cargo.toml | 1 - examples/runners/wgpu-compute/Cargo.toml | 23 -- examples/runners/wgpu-compute/build.rs | 8 - examples/runners/wgpu/build.rs | 1 + .../src/main.rs => wgpu/src/compute.rs} | 23 +- examples/runners/wgpu/src/graphics.rs | 211 ++++++++++++++++ examples/runners/wgpu/src/lib.rs | 227 ++---------------- 8 files changed, 240 insertions(+), 279 deletions(-) delete mode 100644 examples/runners/wgpu-compute/Cargo.toml delete mode 100644 examples/runners/wgpu-compute/build.rs rename examples/runners/{wgpu-compute/src/main.rs => wgpu/src/compute.rs} (82%) create mode 100644 examples/runners/wgpu/src/graphics.rs diff --git a/Cargo.lock b/Cargo.lock index 77482521bc..56e3deb77e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -649,17 +649,6 @@ dependencies = [ "winit", ] -[[package]] -name = "example-runner-wgpu-compute" -version = "0.1.0" -dependencies = [ - "futures", - "rspirv 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spirv-builder", - "wasm-bindgen-futures", - "wgpu", -] - [[package]] name = "filetime" version = "0.2.13" @@ -1960,18 +1949,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "rspirv" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b11de7481ced3d22182a2be7670323028b38a7cc3457e470f91d144947fba4" -dependencies = [ - "derive_more", - "fxhash", - "num-traits", - "spirv_headers 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "rspirv" version = "0.7.0" @@ -1990,7 +1967,7 @@ dependencies = [ "bimap", "pipe", "pretty_assertions", - "rspirv 0.7.0 (git+https://github.com/gfx-rs/rspirv.git?rev=f11f8797bd4df2d1d22cf10767b39a5119c57551)", + "rspirv", "spirv-tools", "tar", "tempfile", diff --git a/Cargo.toml b/Cargo.toml index b3a460ee96..cc904e6ec7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,6 @@ members = [ "examples/runners/cpu", "examples/runners/ash", "examples/runners/wgpu", - "examples/runners/wgpu-compute", "examples/shaders/sky-shader", "examples/shaders/simplest-shader", "examples/shaders/compute-shader", diff --git a/examples/runners/wgpu-compute/Cargo.toml b/examples/runners/wgpu-compute/Cargo.toml deleted file mode 100644 index a4b03017d3..0000000000 --- a/examples/runners/wgpu-compute/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "example-runner-wgpu-compute" -version = "0.1.0" -authors = ["Embark "] -edition = "2018" -license = "MIT OR Apache-2.0" - -# See rustc_codegen_spirv/Cargo.toml for details on these features -[features] -default = ["use-compiled-tools"] -use-installed-tools = ["spirv-builder/use-installed-tools"] -use-compiled-tools = ["spirv-builder/use-compiled-tools"] - -[dependencies] -wgpu = "0.6.0" -futures = { version = "0.3", default-features = false, features = ["std", "executor"] } -rspirv = "0.7.0" - -[build-dependencies] -spirv-builder = { path = "../../../crates/spirv-builder" } - -[target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen-futures = "0.4.18" diff --git a/examples/runners/wgpu-compute/build.rs b/examples/runners/wgpu-compute/build.rs deleted file mode 100644 index dd10208fde..0000000000 --- a/examples/runners/wgpu-compute/build.rs +++ /dev/null @@ -1,8 +0,0 @@ -use spirv_builder::SpirvBuilder; -use std::error::Error; - -fn main() -> Result<(), Box> { - // This will set the env var `compute_shader.spv` to a spir-v file that can be include!()'d - SpirvBuilder::new("../../shaders/compute-shader").build()?; - Ok(()) -} diff --git a/examples/runners/wgpu/build.rs b/examples/runners/wgpu/build.rs index 55f95cc3cc..87322e3d6a 100644 --- a/examples/runners/wgpu/build.rs +++ b/examples/runners/wgpu/build.rs @@ -11,5 +11,6 @@ fn build_shader(path_to_create: &str) -> Result<(), Box> { fn main() -> Result<(), Box> { build_shader("../../shaders/sky-shader")?; build_shader("../../shaders/simplest-shader")?; + build_shader("../../shaders/compute-shader")?; Ok(()) } diff --git a/examples/runners/wgpu-compute/src/main.rs b/examples/runners/wgpu/src/compute.rs similarity index 82% rename from examples/runners/wgpu-compute/src/main.rs rename to examples/runners/wgpu/src/compute.rs index 7e80b3d1e1..3cee856e9c 100644 --- a/examples/runners/wgpu-compute/src/main.rs +++ b/examples/runners/wgpu/src/compute.rs @@ -1,6 +1,4 @@ -fn shader_module() -> wgpu::ShaderModuleSource<'static> { - wgpu::include_spirv!(env!("compute_shader.spv")) -} +use super::{shader_module, Options}; fn create_device_queue() -> (wgpu::Device, wgpu::Queue) { async fn create_device_queue_async() -> (wgpu::Device, wgpu::Queue) { @@ -25,21 +23,20 @@ fn create_device_queue() -> (wgpu::Device, wgpu::Queue) { .await .expect("Failed to create device") } - #[cfg(not(target_arch = "wasm32"))] - { - return futures::executor::block_on(create_device_queue_async()); - }; - #[cfg(target_arch = "wasm32")] - { - return wasm_bindgen_futures::spawn_local(create_device_queue_async()); - }; + cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + wasm_bindgen_futures::spawn_local(create_device_queue_async()) + } else { + futures::executor::block_on(create_device_queue_async()) + } + } } -fn main() { +pub fn start(options: &Options) { let (device, queue) = create_device_queue(); // Load the shaders from disk - let module = device.create_shader_module(shader_module()); + let module = device.create_shader_module(shader_module(options.shader)); let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: None, diff --git a/examples/runners/wgpu/src/graphics.rs b/examples/runners/wgpu/src/graphics.rs new file mode 100644 index 0000000000..fcf04c8873 --- /dev/null +++ b/examples/runners/wgpu/src/graphics.rs @@ -0,0 +1,211 @@ +use super::{shader_module, Options}; +use winit::{ + event::{Event, KeyboardInput, VirtualKeyCode, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::Window, +}; + +async fn run( + options: &Options, + event_loop: EventLoop<()>, + window: Window, + swapchain_format: wgpu::TextureFormat, +) { + let size = window.inner_size(); + let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); + + // Wait for Resumed event on Android; the surface is only needed early to + // find an adapter that can render to this surface. + let mut surface = if cfg!(target_os = "android") { + None + } else { + Some(unsafe { instance.create_surface(&window) }) + }; + + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::default(), + // Request an adapter which can render to our surface + compatible_surface: surface.as_ref(), + }) + .await + .expect("Failed to find an appropriate adapter"); + + // Create the logical device and command queue + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + features: wgpu::Features::empty(), + limits: wgpu::Limits::default(), + shader_validation: true, + }, + None, + ) + .await + .expect("Failed to create device"); + + // Load the shaders from disk + let module = device.create_shader_module(shader_module(options.shader)); + + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[], + push_constant_ranges: &[], + }); + + let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: Some(&pipeline_layout), + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &module, + entry_point: "main_vs", + }, + fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + module: &module, + entry_point: "main_fs", + }), + // Use the default rasterizer state: no culling, no depth bias + rasterization_state: None, + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[swapchain_format.into()], + depth_stencil_state: None, + vertex_state: wgpu::VertexStateDescriptor { + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[], + }, + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + }); + + let mut sc_desc = wgpu::SwapChainDescriptor { + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + format: swapchain_format, + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::Mailbox, + }; + + let mut swap_chain = surface + .as_ref() + .map(|surface| device.create_swap_chain(&surface, &sc_desc)); + + event_loop.run(move |event, _, control_flow| { + // Have the closure take ownership of the resources. + // `event_loop.run` never returns, therefore we must do this to ensure + // the resources are properly cleaned up. + let _ = (&instance, &adapter, &module, &pipeline_layout); + + *control_flow = ControlFlow::Wait; + match event { + Event::Resumed => { + let s = unsafe { instance.create_surface(&window) }; + swap_chain = Some(device.create_swap_chain(&s, &sc_desc)); + surface = Some(s); + } + Event::Suspended => { + surface = None; + swap_chain = None; + } + Event::WindowEvent { + event: WindowEvent::Resized(size), + .. + } => { + // Recreate the swap chain with the new size + sc_desc.width = size.width; + sc_desc.height = size.height; + if let Some(surface) = &surface { + swap_chain = Some(device.create_swap_chain(surface, &sc_desc)); + } + } + Event::RedrawRequested(_) => { + if let Some(swap_chain) = &mut swap_chain { + let frame = swap_chain + .get_current_frame() + .expect("Failed to acquire next swap chain texture") + .output; + let mut encoder = device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), + store: true, + }, + }], + depth_stencil_attachment: None, + }); + rpass.set_pipeline(&render_pipeline); + rpass.draw(0..3, 0..1); + } + + queue.submit(Some(encoder.finish())); + } + } + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => *control_flow = ControlFlow::Exit, + Event::WindowEvent { + event: + WindowEvent::KeyboardInput { + input: + KeyboardInput { + virtual_keycode: Some(VirtualKeyCode::Escape), + .. + }, + .. + }, + .. + } => *control_flow = ControlFlow::Exit, + _ => {} + } + }); +} + +pub fn start(options: &Options) { + let event_loop = EventLoop::new(); + let window = winit::window::WindowBuilder::new() + .with_title("Rust GPU - wgpu") + .with_inner_size(winit::dpi::LogicalSize::new(1280.0, 720.0)) + .build(&event_loop) + .unwrap(); + + cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + 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()) + .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"); + // Temporarily avoid srgb formats for the swapchain on the web + wasm_bindgen_futures::spawn_local(run( + event_loop, + window, + wgpu::TextureFormat::Bgra8Unorm, + )); + } else { + wgpu_subscriber::initialize_default_subscriber(None); + futures::executor::block_on(run( + options, + event_loop, + window, + if cfg!(target_os = "android") { + wgpu::TextureFormat::Rgba8UnormSrgb + } else { + wgpu::TextureFormat::Bgra8UnormSrgb + }, + )); + } + } +} diff --git a/examples/runners/wgpu/src/lib.rs b/examples/runners/wgpu/src/lib.rs index f0e9ae33f2..c86876bfa5 100644 --- a/examples/runners/wgpu/src/lib.rs +++ b/examples/runners/wgpu/src/lib.rs @@ -1,234 +1,41 @@ use clap::Clap; use strum::{Display, EnumString}; -use winit::{ - event::{Event, KeyboardInput, VirtualKeyCode, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::Window, -}; -#[derive(EnumString, Display)] -enum RustGPUShader { +mod compute; +mod graphics; + +#[derive(EnumString, Display, PartialEq, Copy, Clone)] +pub enum RustGPUShader { Simplest, Sky, + Compute, } fn shader_module(shader: RustGPUShader) -> wgpu::ShaderModuleSource<'static> { match shader { RustGPUShader::Simplest => wgpu::include_spirv!(env!("simplest_shader.spv")), RustGPUShader::Sky => wgpu::include_spirv!(env!("sky_shader.spv")), + RustGPUShader::Compute => wgpu::include_spirv!(env!("compute_shader.spv")), } } -#[derive(Clap)] -struct Options { - #[clap(short, long, default_value = "Sky")] - shader: RustGPUShader, +fn is_compute_shader(shader: RustGPUShader) -> bool { + shader == RustGPUShader::Compute } -async fn run( - options: Options, - event_loop: EventLoop<()>, - window: Window, - swapchain_format: wgpu::TextureFormat, -) { - let size = window.inner_size(); - let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); - - // Wait for Resumed event on Android; the surface is only needed early to - // find an adapter that can render to this surface. - let mut surface = if cfg!(target_os = "android") { - None - } else { - Some(unsafe { instance.create_surface(&window) }) - }; - - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::default(), - // Request an adapter which can render to our surface - compatible_surface: surface.as_ref(), - }) - .await - .expect("Failed to find an appropriate adapter"); - - // Create the logical device and command queue - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - features: wgpu::Features::empty(), - limits: wgpu::Limits::default(), - shader_validation: true, - }, - None, - ) - .await - .expect("Failed to create device"); - - // Load the shaders from disk - let module = device.create_shader_module(shader_module(options.shader)); - - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: None, - bind_group_layouts: &[], - push_constant_ranges: &[], - }); - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: None, - layout: Some(&pipeline_layout), - vertex_stage: wgpu::ProgrammableStageDescriptor { - module: &module, - entry_point: "main_vs", - }, - fragment_stage: Some(wgpu::ProgrammableStageDescriptor { - module: &module, - entry_point: "main_fs", - }), - // Use the default rasterizer state: no culling, no depth bias - rasterization_state: None, - primitive_topology: wgpu::PrimitiveTopology::TriangleList, - color_states: &[swapchain_format.into()], - depth_stencil_state: None, - vertex_state: wgpu::VertexStateDescriptor { - index_format: wgpu::IndexFormat::Uint16, - vertex_buffers: &[], - }, - sample_count: 1, - sample_mask: !0, - alpha_to_coverage_enabled: false, - }); - - let mut sc_desc = wgpu::SwapChainDescriptor { - usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, - format: swapchain_format, - width: size.width, - height: size.height, - present_mode: wgpu::PresentMode::Mailbox, - }; - - let mut swap_chain = surface - .as_ref() - .map(|surface| device.create_swap_chain(&surface, &sc_desc)); - - event_loop.run(move |event, _, control_flow| { - // Have the closure take ownership of the resources. - // `event_loop.run` never returns, therefore we must do this to ensure - // the resources are properly cleaned up. - let _ = (&instance, &adapter, &module, &pipeline_layout); - - *control_flow = ControlFlow::Wait; - match event { - Event::Resumed => { - let s = unsafe { instance.create_surface(&window) }; - swap_chain = Some(device.create_swap_chain(&s, &sc_desc)); - surface = Some(s); - } - Event::Suspended => { - surface = None; - swap_chain = None; - } - Event::WindowEvent { - event: WindowEvent::Resized(size), - .. - } => { - // Recreate the swap chain with the new size - sc_desc.width = size.width; - sc_desc.height = size.height; - if let Some(surface) = &surface { - swap_chain = Some(device.create_swap_chain(surface, &sc_desc)); - } - } - Event::RedrawRequested(_) => { - if let Some(swap_chain) = &mut swap_chain { - let frame = swap_chain - .get_current_frame() - .expect("Failed to acquire next swap chain texture") - .output; - let mut encoder = device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - { - let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { - attachment: &frame.view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), - store: true, - }, - }], - depth_stencil_attachment: None, - }); - rpass.set_pipeline(&render_pipeline); - rpass.draw(0..3, 0..1); - } - - queue.submit(Some(encoder.finish())); - } - } - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - Event::WindowEvent { - event: - WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(VirtualKeyCode::Escape), - .. - }, - .. - }, - .. - } => *control_flow = ControlFlow::Exit, - _ => {} - } - }); +#[derive(Clap)] +pub struct Options { + #[clap(short, long, default_value = "Sky")] + shader: RustGPUShader, } #[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))] pub fn main() { let options: Options = Options::parse(); - let event_loop = EventLoop::new(); - let window = winit::window::WindowBuilder::new() - .with_title("Rust GPU - wgpu") - .with_inner_size(winit::dpi::LogicalSize::new(1280.0, 720.0)) - .build(&event_loop) - .unwrap(); - - cfg_if::cfg_if! { - if #[cfg(target_arch = "wasm32")] { - 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()) - .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"); - // Temporarily avoid srgb formats for the swapchain on the web - wasm_bindgen_futures::spawn_local(run( - event_loop, - window, - wgpu::TextureFormat::Bgra8Unorm, - )); - } else { - wgpu_subscriber::initialize_default_subscriber(None); - futures::executor::block_on(run( - options, - event_loop, - window, - if cfg!(target_os = "android") { - wgpu::TextureFormat::Rgba8UnormSrgb - } else { - wgpu::TextureFormat::Bgra8UnormSrgb - }, - )); - } + if is_compute_shader(options.shader) { + compute::start(&options) + } else { + graphics::start(&options); } }