diff --git a/Cargo.lock b/Cargo.lock index c82e3eb..dae23be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -328,6 +328,7 @@ dependencies = [ "spirv-std", "spirv-std-macros", "spirv_backend_lib", + "spirv_backend_runner", "structopt", "threadpool", "typetag", @@ -2037,6 +2038,10 @@ dependencies = [ "spirv-std", ] +[[package]] +name = "spirv_backend_runner" +version = "0.1.0" + [[package]] name = "strsim" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index 5f72703..7d6c481 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ spirv-builder = { git = "https://github.com/EmbarkStudios/rust-gpu", features = spirv-std = { git = "https://github.com/EmbarkStudios/rust-gpu" } spirv-std-macros = { git = "https://github.com/EmbarkStudios/rust-gpu" } spirv_backend_lib = { path = "src/sim/spirv/spirv_backend_lib" } +spirv_backend_runner = { path = "src/sim/spirv/spirv_backend_runner" } [dev-dependencies] diff --git a/src/sim/spirv/mod.rs b/src/sim/spirv/mod.rs index 01b605f..fcd4c34 100644 --- a/src/sim/spirv/mod.rs +++ b/src/sim/spirv/mod.rs @@ -1,15 +1,13 @@ use futures::FutureExt as _; -use log::trace; use ndarray::{self, Array3}; use serde::{Deserialize, Serialize}; -use spirv_builder::{Capability, MetadataPrintout, SpirvBuilder}; use std::borrow::Cow; use std::num::NonZeroU64; use std::path::PathBuf; use std::sync::Arc; use wgpu; use wgpu::util::DeviceExt as _; -use log::{info, warn}; +use log::{info, trace, warn}; use crate::geom::{Coord, Index, Meters, Vec3}; use crate::real::Real as _; @@ -51,6 +49,7 @@ impl WgpuData { pub fn new(volume: u64) -> Self where M::Ffi: 'static { + info!("WgpuData::new({})", volume); use std::mem::size_of; let max_elem_size = size_of::().max(size_of::>()); let max_buf_size = volume * max_elem_size as u64 + 0x1000; @@ -489,37 +488,9 @@ unsafe fn from_bytes(slice: &[u8]) -> &[T] { std::slice::from_raw_parts(slice.as_ptr() as *const T, new_len) } -/// Compiles the compute shader to spirv +/// Loads the shader fn get_shader() -> wgpu::ShaderModuleDescriptorSpirV<'static> { - // based on rust-gpu/examples/runners/wgpu/src/lib.rs:maybe_watch - // Hack: spirv_builder builds into a custom directory if running under cargo, to not - // deadlock, and the default target directory if not. However, packages like `proc-macro2` - // have different configurations when being built here vs. when building - // rustc_codegen_spirv normally, so we *want* to build into a separate target directory, to - // not have to rebuild half the crate graph every time we run. So, pretend we're running - // under cargo by setting these environment variables. - // std::env::set_var("OUT_DIR", env!("OUT_DIR")); - // std::env::set_var("PROFILE", env!("PROFILE")); - // Capabilities documented here: https://www.khronos.org/registry/SPIR-V/specs/unified1/SPIRV.html#Capability - let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let crate_path = [manifest_dir, "src", "sim", "spirv", "spirv_backend"] - .iter() - .copied() - .collect::(); - let builder = SpirvBuilder::new(crate_path, "spirv-unknown-vulkan1.1") - //let builder = SpirvBuilder::new(crate_path, "spirv-unknown-spv1.3") - .print_metadata(MetadataPrintout::None) - .capability(Capability::Int8) - // .capability(Capability::PhysicalStorageBufferAddresses) - // .capability(Capability::Addresses) - // .capability(Capability::GenericPointer) - ; - - let initial_result = builder.build().unwrap(); - - let module_path = initial_result.module.unwrap_single(); - let data = std::fs::read(module_path.clone()).unwrap_or_else(|e| { panic!("module path: {:?}: {:?}", module_path, e) }); - + let data = spirv_backend_runner::spirv_module(); let spirv = Cow::Owned(wgpu::util::make_spirv_raw(&data).into_owned()); wgpu::ShaderModuleDescriptorSpirV { @@ -531,14 +502,16 @@ fn get_shader() -> wgpu::ShaderModuleDescriptorSpirV<'static> { async fn open_device(max_buf_size: u64) -> (wgpu::Device, wgpu::Queue) { // based on rust-gpu/examples/runners/wgpu/src/compute.rs:start_internal let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY); + info!("open_device: got instance"); let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::default(), + power_preference: wgpu::PowerPreference::HighPerformance, force_fallback_adapter: false, compatible_surface: None, }) .await .expect("Failed to find an appropriate adapter"); + info!("open_device: got adapter"); // XXX not all adapters will support non-default limits, and it could // cause perf degradations even on the ones that do. May want to consider @@ -562,6 +535,7 @@ async fn open_device(max_buf_size: u64) -> (wgpu::Device, wgpu::Queue) { ) .await .expect("Failed to create device"); + info!("open_device: got device"); (device, queue) } diff --git a/src/sim/spirv/spirv_backend_builder/Cargo.toml b/src/sim/spirv/spirv_backend_builder/Cargo.toml new file mode 100644 index 0000000..fa57751 --- /dev/null +++ b/src/sim/spirv/spirv_backend_builder/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "spirv_backend_builder" +version = "0.1.0" +authors = ["Colin "] +edition = "2021" + +[dependencies] +log = "0.4" +spirv-builder = { git = "https://github.com/EmbarkStudios/rust-gpu", features = ["use-compiled-tools"] } diff --git a/src/sim/spirv/spirv_backend_builder/src/main.rs b/src/sim/spirv/spirv_backend_builder/src/main.rs new file mode 100644 index 0000000..227157a --- /dev/null +++ b/src/sim/spirv/spirv_backend_builder/src/main.rs @@ -0,0 +1,30 @@ +use spirv_builder::{Capability, MetadataPrintout, SpirvBuilder}; +use log::info; +use std::env; +use std::error::Error; +use std::path::PathBuf; + +fn main() -> Result<(), Box> { + // based on EmbarkStudios/rust-gpu/examples/runners/wgpu/src/lib.rs:maybe_watch + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let crate_path = [manifest_dir, "../", "spirv_backend"] + .iter() + .copied() + .collect::(); + info!("get_shader: constructing builder"); + let builder = SpirvBuilder::new(crate_path, "spirv-unknown-vulkan1.1") + //let builder = SpirvBuilder::new(crate_path, "spirv-unknown-spv1.3") + .print_metadata(MetadataPrintout::None) + .capability(Capability::Int8) + // .capability(Capability::PhysicalStorageBufferAddresses) + // .capability(Capability::Addresses) + // .capability(Capability::GenericPointer) + ; + + info!("get_shader: building"); + let initial_result = builder.build()?; + + let module_path = initial_result.module.unwrap_single(); + info!("get_shader: built: {:?}", module_path); + Ok(()) +} diff --git a/src/sim/spirv/spirv_backend_runner/Cargo.toml b/src/sim/spirv/spirv_backend_runner/Cargo.toml new file mode 100644 index 0000000..343b635 --- /dev/null +++ b/src/sim/spirv/spirv_backend_runner/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "spirv_backend_runner" +version = "0.1.0" +authors = ["Colin "] +edition = "2021" + +[lib] +crate-type = ["lib"] diff --git a/src/sim/spirv/spirv_backend_runner/build.rs b/src/sim/spirv/spirv_backend_runner/build.rs new file mode 100644 index 0000000..cfd6f83 --- /dev/null +++ b/src/sim/spirv/spirv_backend_runner/build.rs @@ -0,0 +1,69 @@ +use std::env; +use std::error::Error; +use std::path::PathBuf; + +fn main() -> Result<(), Box> { + // heavily inspired from EmbarkStudios/rust-gpu/examples/runners/wgpu/build.rs + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-env-changed=CARGO_CFG_TARGET_OS"); + println!("cargo:rerun-if-env-changed=CARGO_CFG_TARGET_ARCH"); + // While OUT_DIR is set for both build.rs and compiling the crate, PROFILE is only set in + // build.rs. So, export it to crate compilation as well. + let profile = env::var("PROFILE").unwrap(); + println!("cargo:rustc-env=PROFILE={}", profile); + let mut dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + // recover the directory from which this was invoked + assert!( + dir.ends_with("out") + && dir.pop() + && dir.pop() + && dir.ends_with("build") + && dir.pop() + && dir.ends_with(profile) + && dir.pop() + ); + + // recover the toplevel directory + assert!( + dir.ends_with("target") + && dir.pop() + ); + if dir.ends_with("spirv_backend_runner") { + // being run from this dir + assert!( + dir.pop() + && dir.ends_with("spirv") + && dir.pop() + && dir.ends_with("sim") + && dir.pop() + && dir.ends_with("src") + && dir.pop() + ); + } // else already at the top-level dir + + let target_dir = dir.join("target/spirv_backend_builder"); + let manifest_path = dir.join("src/sim/spirv/spirv_backend_builder/Cargo.toml"); + println!("target_dir: {:?}", target_dir); + println!("manifest_path: {:?}", manifest_path); + + let status = std::process::Command::new("cargo") + .args([ + "run", + "--release", + "--manifest-path", + ]) + .arg(manifest_path) + .arg("--target-dir") + .arg(target_dir) + .stderr(std::process::Stdio::inherit()) + .stdout(std::process::Stdio::inherit()) + .status()?; + if !status.success() { + if let Some(code) = status.code() { + std::process::exit(code); + } else { + std::process::exit(1); + } + } + Ok(()) +} diff --git a/src/sim/spirv/spirv_backend_runner/src/lib.rs b/src/sim/spirv/spirv_backend_runner/src/lib.rs new file mode 100644 index 0000000..2889b6f --- /dev/null +++ b/src/sim/spirv/spirv_backend_runner/src/lib.rs @@ -0,0 +1,8 @@ +/// returns the compiled spirv module +/// for use with e.g. `wgpu::util::make_spirv_raw`. +pub fn spirv_module() -> &'static [u8] { + // we specifically include the bytes into this binary, for ease of packaging (v.s. attempting + // to locate the compiled result at runtime). + include_bytes!("../../../../../target/spirv-builder/spirv-unknown-vulkan1.1/release/deps/spirv_backend.spv.dir/module") +} +