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
This commit is contained in:
Viktor Zoutman 2020-11-16 12:21:33 +01:00 committed by GitHub
parent 28e71d9932
commit 2d75e0473f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 240 additions and 279 deletions

25
Cargo.lock generated
View File

@ -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",

View File

@ -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",

View File

@ -1,23 +0,0 @@
[package]
name = "example-runner-wgpu-compute"
version = "0.1.0"
authors = ["Embark <opensource@embark-studios.com>"]
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"

View File

@ -1,8 +0,0 @@
use spirv_builder::SpirvBuilder;
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
// 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(())
}

View File

@ -11,5 +11,6 @@ fn build_shader(path_to_create: &str) -> Result<(), Box<dyn Error>> {
fn main() -> Result<(), Box<dyn Error>> {
build_shader("../../shaders/sky-shader")?;
build_shader("../../shaders/simplest-shader")?;
build_shader("../../shaders/compute-shader")?;
Ok(())
}

View File

@ -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,

View File

@ -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
},
));
}
}
}

View File

@ -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);
}
}