Update wgpu (and ash, to avoid duplicate deps).

This commit is contained in:
Eduard-Mihai Burtescu 2022-10-04 10:08:48 +03:00 committed by Eduard-Mihai Burtescu
parent b67ad40fef
commit a9472a0743
6 changed files with 659 additions and 477 deletions

852
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -14,17 +14,14 @@ use-installed-tools = ["spirv-builder/use-installed-tools"]
use-compiled-tools = ["spirv-builder/use-compiled-tools"]
[dependencies]
ash = "0.35"
ash-window = "0.9"
winit = "0.26"
ash = "0.37"
ash-window = "0.12"
raw-window-handle = "0.5.1"
winit = "0.28.3"
structopt = "0.3.20"
cfg-if = "1.0.0"
shared = { path = "../../shaders/shared" }
spirv-builder = { workspace = true, default-features = false }
# TODO: Remove this once no longer needed, only needed to make cargo-deny happy for some reason.
# https://rustsec.org/advisories/RUSTSEC-2021-0119
nix = "0.20.2"
[target.'cfg(target_os = "macos")'.dependencies]
ash-molten = { version = "0.12.0", features = ["pre-built"] }
ash-molten = { version = "0.13.1", features = ["pre-built"] }

View File

@ -76,6 +76,7 @@ use ash::{
vk,
};
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
use winit::{
event::{Event, VirtualKeyCode, WindowEvent},
event_loop::{ControlFlow, EventLoop},
@ -271,11 +272,10 @@ impl RenderBase {
.map(|raw_name| raw_name.as_ptr())
.collect();
let mut extension_names_raw = ash_window::enumerate_required_extensions(&window)
.unwrap()
.iter()
.map(|ext| ext.as_ptr())
.collect::<Vec<_>>();
let mut extension_names_raw =
ash_window::enumerate_required_extensions(window.raw_display_handle())
.unwrap()
.to_vec();
if options.debug_layer {
extension_names_raw.push(ext::DebugUtils::name().as_ptr());
}
@ -299,8 +299,16 @@ impl RenderBase {
}
};
let surface =
unsafe { ash_window::create_surface(&entry, &instance, &window, None).unwrap() };
let surface = unsafe {
ash_window::create_surface(
&entry,
&instance,
window.raw_display_handle(),
window.raw_window_handle(),
None,
)
.unwrap()
};
let (debug_utils_loader, debug_call_back) = if options.debug_layer {
let debug_utils_loader = ext::DebugUtils::new(&entry, &instance);

View File

@ -22,7 +22,7 @@ shared = { path = "../../shaders/shared" }
futures = { version = "0.3", default-features = false, features = ["std", "executor"] }
# Vulkan SDK or MoltenVK needs to be installed for `vulkan-portability` to work on macOS
wgpu = { git = "https://github.com/gfx-rs/wgpu", features = ["spirv", "vulkan-portability"] }
winit = { version = "0.26" }
winit = "0.28.3"
structopt = "0.3"
strum = { version = "0.23.0", default_features = false, features = ["std", "derive"] }
bytemuck = "1.6.3"
@ -31,13 +31,13 @@ bytemuck = "1.6.3"
spirv-builder = { workspace = true, features = ["watch"] }
[target.'cfg(target_os = "android")'.dependencies]
ndk-glue = "0.4"
ndk-glue = "0.7.0"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
env_logger = "0.9.0"
[target.'cfg(target_arch = "wasm32")'.dependencies]
web-sys = "=0.3.55"
web-sys = "0.3.60"
console_error_panic_hook = "0.1.6"
console_log = "0.2.0"
wasm-bindgen-futures = "0.4.18"

View File

@ -1,7 +1,6 @@
use wgpu::util::DeviceExt;
use super::Options;
use futures::future::join;
use std::{convert::TryInto, future::Future, time::Duration};
fn block_on<T>(future: impl Future<Output = T>) -> T {
@ -24,13 +23,12 @@ pub async fn start_internal(
_options: &Options,
shader_binary: wgpu::ShaderModuleDescriptor<'static>,
) {
let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY);
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
force_fallback_adapter: false,
compatible_surface: None,
})
let backends = wgpu::util::backend_bits_from_env().unwrap_or(wgpu::Backends::PRIMARY);
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends,
dx12_shader_compiler: wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default(),
});
let adapter = wgpu::util::initialize_adapter_from_env_or_default(&instance, backends, None)
.await
.expect("Failed to find an appropriate adapter");
@ -51,7 +49,7 @@ pub async fn start_internal(
let timestamp_period = queue.get_timestamp_period();
// Load the shaders from disk
let module = device.create_shader_module(&shader_binary);
let module = device.create_shader_module(shader_binary);
let top = 2u32.pow(20);
let src_range = 1..top;
@ -135,7 +133,7 @@ pub async fn start_internal(
cpass.set_bind_group(0, &bind_group, &[]);
cpass.set_pipeline(&compute_pipeline);
cpass.write_timestamp(&queries, 0);
cpass.dispatch(src_range.len() as u32 / 64, 1, 1);
cpass.dispatch_workgroups(src_range.len() as u32 / 64, 1, 1);
cpass.write_timestamp(&queries, 1);
}
@ -151,41 +149,41 @@ pub async fn start_internal(
queue.submit(Some(encoder.finish()));
let buffer_slice = readback_buffer.slice(..);
let timestamp_slice = timestamp_buffer.slice(..);
let timestamp_future = timestamp_slice.map_async(wgpu::MapMode::Read);
let buffer_future = buffer_slice.map_async(wgpu::MapMode::Read);
timestamp_slice.map_async(wgpu::MapMode::Read, |r| r.unwrap());
buffer_slice.map_async(wgpu::MapMode::Read, |r| r.unwrap());
// NOTE(eddyb) `poll` should return only after the above callbacks fire
// (see also https://github.com/gfx-rs/wgpu/pull/2698 for more details).
device.poll(wgpu::Maintain::Wait);
if let (Ok(()), Ok(())) = join(buffer_future, timestamp_future).await {
let data = buffer_slice.get_mapped_range();
let timing_data = timestamp_slice.get_mapped_range();
let result = data
.chunks_exact(4)
.map(|b| u32::from_ne_bytes(b.try_into().unwrap()))
.collect::<Vec<_>>();
let timings = timing_data
.chunks_exact(8)
.map(|b| u64::from_ne_bytes(b.try_into().unwrap()))
.collect::<Vec<_>>();
drop(data);
readback_buffer.unmap();
drop(timing_data);
timestamp_buffer.unmap();
let mut max = 0;
for (src, out) in src_range.zip(result.iter().copied()) {
if out == u32::MAX {
println!("{src}: overflowed");
break;
} else if out > max {
max = out;
// Should produce <https://oeis.org/A006877>
println!("{src}: {out}");
}
let data = buffer_slice.get_mapped_range();
let timing_data = timestamp_slice.get_mapped_range();
let result = data
.chunks_exact(4)
.map(|b| u32::from_ne_bytes(b.try_into().unwrap()))
.collect::<Vec<_>>();
let timings = timing_data
.chunks_exact(8)
.map(|b| u64::from_ne_bytes(b.try_into().unwrap()))
.collect::<Vec<_>>();
drop(data);
readback_buffer.unmap();
drop(timing_data);
timestamp_buffer.unmap();
let mut max = 0;
for (src, out) in src_range.zip(result.iter().copied()) {
if out == u32::MAX {
println!("{src}: overflowed");
break;
} else if out > max {
max = out;
// Should produce <https://oeis.org/A006877>
println!("{src}: {out}");
}
println!(
"Took: {:?}",
Duration::from_nanos(
((timings[1] - timings[0]) as f64 * f64::from(timestamp_period)) as u64
)
);
}
println!(
"Took: {:?}",
Duration::from_nanos(
((timings[1] - timings[0]) as f64 * f64::from(timestamp_period)) as u64
)
);
}

View File

@ -4,7 +4,7 @@ use super::Options;
use shared::ShaderConstants;
use winit::{
event::{ElementState, Event, KeyboardInput, MouseButton, VirtualKeyCode, WindowEvent},
event_loop::{ControlFlow, EventLoop},
event_loop::{ControlFlow, EventLoop, EventLoopBuilder},
window::Window,
};
@ -37,25 +37,37 @@ async fn run(
window: Window,
shader_binary: wgpu::ShaderModuleDescriptor<'static>,
) {
let instance = wgpu::Instance::new(wgpu::Backends::VULKAN | wgpu::Backends::METAL);
let backends = wgpu::util::backend_bits_from_env()
.unwrap_or(wgpu::Backends::VULKAN | wgpu::Backends::METAL);
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends,
dx12_shader_compiler: wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default(),
});
// 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
// HACK(eddyb) marker error type for lazily-created surfaces (e.g. on Android).
struct SurfaceCreationPending {
preferred_format: wgpu::TextureFormat,
}
// Wait for Resumed event on Android; the surface is only otherwise needed
// early to find an adapter that can render to this surface.
let initial_surface = if cfg!(target_os = "android") {
Err(SurfaceCreationPending {
preferred_format: wgpu::TextureFormat::Rgba8UnormSrgb,
})
} else {
Some(unsafe { instance.create_surface(&window) })
Ok(unsafe { instance.create_surface(&window) }
.expect("Failed to create surface from window"))
};
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
force_fallback_adapter: false,
// Request an adapter which can render to our surface
compatible_surface: surface.as_ref(),
})
.await
.expect("Failed to find an appropriate adapter");
let adapter = wgpu::util::initialize_adapter_from_env_or_default(
&instance,
backends,
// Request an adapter which can render to our surface
initial_surface.as_ref().ok(),
)
.await
.expect("Failed to find an appropriate adapter");
let features = wgpu::Features::PUSH_CONSTANTS;
let limits = wgpu::Limits {
@ -76,6 +88,30 @@ async fn run(
.await
.expect("Failed to create device");
let auto_configure_surface =
|adapter: &_, device: &_, surface: wgpu::Surface, size: winit::dpi::PhysicalSize<_>| {
let mut surface_config = surface
.get_default_config(adapter, size.width, size.height)
.unwrap_or_else(|| {
panic!(
"Missing formats/present modes in surface capabilities: {:#?}",
surface.get_capabilities(adapter)
)
});
// FIXME(eddyb) should this be toggled by a CLI arg?
// NOTE(eddyb) VSync was disabled in the past, but without VSync,
// especially for simpler shaders, you can easily hit thousands
// of frames per second, stressing GPUs for no reason.
surface_config.present_mode = wgpu::PresentMode::AutoVsync;
surface.configure(device, &surface_config);
(surface, surface_config)
};
let mut surface_with_config = initial_surface
.map(|surface| auto_configure_surface(&adapter, &device, surface, window.inner_size()));
// Load the shaders from disk
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
@ -87,28 +123,15 @@ async fn run(
}],
});
let preferred_format = if let Some(surface) = &surface {
surface.get_preferred_format(&adapter).unwrap()
} else {
// if Surface is none, we're guaranteed to be on android
wgpu::TextureFormat::Rgba8UnormSrgb
};
let mut render_pipeline =
create_pipeline(&device, &pipeline_layout, preferred_format, shader_binary);
let size = window.inner_size();
let mut surface_config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: preferred_format,
width: size.width,
height: size.height,
present_mode: wgpu::PresentMode::Mailbox,
};
if let Some(surface) = &mut surface {
surface.configure(&device, &surface_config);
}
let mut render_pipeline = create_pipeline(
&device,
&pipeline_layout,
surface_with_config.as_ref().map_or_else(
|pending| pending.preferred_format,
|(_, surface_config)| surface_config.format,
),
shader_binary,
);
let start = std::time::Instant::now();
@ -132,13 +155,21 @@ async fn run(
window.request_redraw();
}
Event::Resumed => {
let s = unsafe { instance.create_surface(&window) };
surface_config.format = s.get_preferred_format(&adapter).unwrap();
s.configure(&device, &surface_config);
surface = Some(s);
let new_surface = unsafe { instance.create_surface(&window) }
.expect("Failed to create surface from window (after resume)");
surface_with_config = Ok(auto_configure_surface(
&adapter,
&device,
new_surface,
window.inner_size(),
));
}
Event::Suspended => {
surface = None;
if let Ok((_, surface_config)) = &surface_with_config {
surface_with_config = Err(SurfaceCreationPending {
preferred_format: surface_config.format,
});
}
}
Event::WindowEvent {
event: WindowEvent::Resized(size),
@ -146,22 +177,29 @@ async fn run(
} => {
if size.width != 0 && size.height != 0 {
// Recreate the swap chain with the new size
surface_config.width = size.width;
surface_config.height = size.height;
if let Some(surface) = &surface {
surface.configure(&device, &surface_config);
if let Ok((surface, surface_config)) = &mut surface_with_config {
surface_config.width = size.width;
surface_config.height = size.height;
surface.configure(&device, surface_config);
}
}
}
Event::RedrawRequested(_) => {
if let Some(surface) = &mut surface {
// FIXME(eddyb) only the mouse shader *really* needs this, could
// avoid doing wasteful rendering by special-casing each shader?
// (with VSync enabled this can't be *too* bad, thankfully)
// FIXME(eddyb) is this the best way to do continuous redraws in
// `winit`? (or should we stop using `ControlFlow::Wait`? etc.)
window.request_redraw();
if let Ok((surface, surface_config)) = &mut surface_with_config {
let output = match surface.get_current_texture() {
Ok(surface) => surface,
Err(err) => {
eprintln!("get_current_texture error: {err:?}");
match err {
wgpu::SurfaceError::Lost => {
surface.configure(&device, &surface_config);
surface.configure(&device, surface_config);
}
wgpu::SurfaceError::OutOfMemory => {
*control_flow = ControlFlow::Exit;
@ -179,14 +217,14 @@ async fn run(
{
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[wgpu::RenderPassColorAttachment {
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &output_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
store: true,
},
}],
})],
depth_stencil_attachment: None,
});
@ -270,8 +308,15 @@ async fn run(
}
}
Event::UserEvent(new_module) => {
*render_pipeline =
create_pipeline(&device, &pipeline_layout, surface_config.format, new_module);
*render_pipeline = create_pipeline(
&device,
&pipeline_layout,
surface_with_config.as_ref().map_or_else(
|pending| pending.preferred_format,
|(_, surface_config)| surface_config.format,
),
new_module,
);
window.request_redraw();
*control_flow = ControlFlow::Poll;
}
@ -286,7 +331,7 @@ fn create_pipeline(
surface_format: wgpu::TextureFormat,
shader_binary: wgpu::ShaderModuleDescriptor<'_>,
) -> wgpu::RenderPipeline {
let module = device.create_shader_module(&shader_binary);
let module = device.create_shader_module(shader_binary);
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
layout: Some(pipeline_layout),
@ -313,11 +358,11 @@ fn create_pipeline(
fragment: Some(wgpu::FragmentState {
module: &module,
entry_point: shaders::main_fs,
targets: &[wgpu::ColorTargetState {
targets: &[Some(wgpu::ColorTargetState {
format: surface_format,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
}],
})],
}),
multiview: None,
})
@ -326,7 +371,7 @@ fn create_pipeline(
#[allow(clippy::match_wild_err_arm)]
pub fn start(options: &Options) {
// Build the shader before we pop open a window, since it might take a while.
let event_loop = EventLoop::with_user_event();
let event_loop = EventLoopBuilder::with_user_event().build();
let proxy = event_loop.create_proxy();
let initial_shader = maybe_watch(
options.shader,