From 84494bf8ff7355eb6cd633856fc07e481ae6c8f8 Mon Sep 17 00:00:00 2001 From: Henno Date: Mon, 12 Apr 2021 16:43:01 -0400 Subject: [PATCH] Add compile_spirv proc macro to replace build.rs --- Cargo.lock | 13 +- Cargo.toml | 1 + crates/spirv-builder-macros/Cargo.toml | 23 ++ crates/spirv-builder-macros/src/lib.rs | 243 +++++++++++++++++++++ examples/runners/wgpu/Cargo.toml | 8 +- examples/runners/wgpu/build.rs | 17 -- examples/runners/wgpu/src/lib.rs | 21 +- examples/shaders/compute-shader/Cargo.toml | 1 - examples/shaders/compute-shader/src/lib.rs | 3 +- 9 files changed, 299 insertions(+), 31 deletions(-) create mode 100644 crates/spirv-builder-macros/Cargo.toml create mode 100644 crates/spirv-builder-macros/src/lib.rs delete mode 100644 examples/runners/wgpu/build.rs diff --git a/Cargo.lock b/Cargo.lock index 5ced8a8d69..9d589f68e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -403,7 +403,6 @@ name = "compute-shader" version = "0.4.0-alpha.4" dependencies = [ "spirv-std", - "spirv-std-macros", ] [[package]] @@ -775,7 +774,7 @@ dependencies = [ "futures", "ndk-glue", "shared", - "spirv-builder", + "spirv-builder-macros", "strum", "wasm-bindgen-futures", "web-sys", @@ -2343,6 +2342,16 @@ dependencies = [ "serde_json", ] +[[package]] +name = "spirv-builder-macros" +version = "0.4.0-alpha.4" +dependencies = [ + "proc-macro2", + "quote", + "spirv-builder", + "syn", +] + [[package]] name = "spirv-std" version = "0.4.0-alpha.4" diff --git a/Cargo.toml b/Cargo.toml index 73a106efa4..469d4a5509 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ "crates/rustc_codegen_spirv", "crates/spirv-builder", + "crates/spirv-builder-macros", "crates/spirv-std", "tests", diff --git a/crates/spirv-builder-macros/Cargo.toml b/crates/spirv-builder-macros/Cargo.toml new file mode 100644 index 0000000000..7ac3a61cd5 --- /dev/null +++ b/crates/spirv-builder-macros/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "spirv-builder-macros" +version = "0.4.0-alpha.4" +authors = ["Embark "] +edition = "2018" +license = "MIT OR Apache-2.0" +repository = "https://github.com/EmbarkStudios/rust-gpu" +description = "Macros for spirv-builder" + +[lib] +proc-macro = true + +# 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] +spirv-builder = { path = "../spirv-builder", default-features = false } +proc-macro2 = "1.0.24" +quote = "1.0.8" +syn = { version = "1.0.58", features=["full"] } \ No newline at end of file diff --git a/crates/spirv-builder-macros/src/lib.rs b/crates/spirv-builder-macros/src/lib.rs new file mode 100644 index 0000000000..30e1fa5307 --- /dev/null +++ b/crates/spirv-builder-macros/src/lib.rs @@ -0,0 +1,243 @@ +// BEGIN - Embark standard lints v0.3 +// do not change or add/remove here, but one can add exceptions after this section +// for more info see: +#![deny(unsafe_code)] +#![warn( + clippy::all, + clippy::await_holding_lock, + clippy::dbg_macro, + clippy::debug_assert_with_mut_call, + clippy::doc_markdown, + clippy::empty_enum, + clippy::enum_glob_use, + clippy::exit, + clippy::explicit_into_iter_loop, + clippy::filter_map_next, + clippy::fn_params_excessive_bools, + clippy::if_let_mutex, + clippy::imprecise_flops, + clippy::inefficient_to_string, + clippy::large_types_passed_by_value, + clippy::let_unit_value, + clippy::linkedlist, + clippy::lossy_float_literal, + clippy::macro_use_imports, + clippy::map_err_ignore, + clippy::map_flatten, + clippy::map_unwrap_or, + clippy::match_on_vec_items, + clippy::match_same_arms, + clippy::match_wildcard_for_single_variants, + clippy::mem_forget, + clippy::mismatched_target_os, + clippy::needless_borrow, + clippy::needless_continue, + clippy::option_option, + clippy::pub_enum_variant_names, + clippy::ref_option_ref, + clippy::rest_pat_in_fully_bound_structs, + clippy::string_add_assign, + clippy::string_add, + clippy::string_to_string, + clippy::suboptimal_flops, + clippy::todo, + clippy::unimplemented, + clippy::unnested_or_patterns, + clippy::unused_self, + clippy::verbose_file_reads, + future_incompatible, + nonstandard_style, + rust_2018_idioms +)] +// END - Embark standard lints v0.3 +// crate-specific exceptions: +#![allow()] + +use std::{env, path::PathBuf}; + +use proc_macro::TokenStream; +use quote::quote; +use spirv_builder::{MemoryModel, SpirvBuilder}; +use syn::{ + parse::{Parse, ParseStream}, + Expr, ExprLit, ExprPath, FieldValue, Lit, Member, Path, Token, +}; + +#[proc_macro] +pub fn compile_spirv(input: TokenStream) -> TokenStream { + let cargo_target_dir = env::var_os("CARGO_TARGET_DIR").map(PathBuf::from); + let spirv_target_dir = if let Some(ref cargo_target_dir) = cargo_target_dir { + if !(cargo_target_dir.ends_with("spirv-builder")) { + cargo_target_dir.clone().join("spirv-builder") + } else { + cargo_target_dir.clone() + } + } else { + env::current_dir() + .expect("`compile_spirv!` macro uses the current directory to determine it's target directory.") + .join("target/spirv-builder") + }; + env::set_var("CARGO_TARGET_DIR", spirv_target_dir); + let args = syn::parse_macro_input!(input as SpirvBuilderArgs); + let spv_path = SpirvBuilder::new(args.path.as_str()) + .memory_model(args.memory_model) + .spirv_version(args.version.0, args.version.1) + .release(args.release) + .print_metadata(false) + .build() + .unwrap(); + if let Some(cargo_target_dir) = cargo_target_dir { + env::set_var("CARGO_TARGET_DIR", cargo_target_dir); + } else { + env::remove_var("CARGO_TARGET_DIR"); + }; + let spv_path = spv_path.to_str().unwrap(); + let out = quote! { + #spv_path + }; + TokenStream::from(out) +} + +struct SpirvBuilderArgs { + path: String, + memory_model: MemoryModel, + version: (u8, u8), + release: bool, +} + +impl Parse for SpirvBuilderArgs { + fn parse(input: ParseStream<'_>) -> syn::Result { + let mut path = None; + let mut memory_model = None; + let mut version = None; + let mut release = None; + for field in input.parse_terminated::(FieldValue::parse)? { + let arg = if let Member::Named(arg) = field.member { + arg + } else { + return Err(input.error("Unnamed member.")); + }; + if arg == "path" { + if path.is_some() { + return Err(input + .error("Duplicate `path` specification, please specify it exactly once.")); + } + if field.colon_token.is_none() { + return Err(input.error( + "Please provide the crate path as a string literal: `path: \"path/to/crate\"`." + )); + } + if let Expr::Lit(ExprLit { + lit: Lit::Str(string), + .. + }) = field.expr + { + path = Some(string.value()); + } else { + return Err(input.error( + "Please provide the crate path as a string literal: `path: \"path/to/crate\"`." + )); + } + } else if arg == "memory_model" { + if memory_model.is_some() { + return Err(input.error( + "Duplicate `memory_model` specification, please specify it at most once. \ + The default is `memory_model: Vulkan`.", + )); + } + if field.colon_token.is_none() { + return Err(input.error( + "Memory model must be specified as `memory_model: MemoryModel` eg. `memory_model: GLSL450`." + )); + } + match field.expr { + Expr::Path(ExprPath { + path: Path { + segments, + .. + }, + .. + }) if segments.len() == 1 => { + let ident = &segments.first().unwrap().ident; + if ident == "Vulkan" { + memory_model = Some(MemoryModel::Vulkan); + } else if ident == "GLSL450" { + memory_model = Some(MemoryModel::GLSL450); + } else if ident == "Simple" { + memory_model = Some(MemoryModel::Simple); + } else { + return Err(input.error( + "Invalid memory model, please specify one of `Vulkan`, `GLSL450`, or `Simple`." + )) + } + } + _ => return Err(input.error( + "Memory model must be specified as `memory_model: MemoryModel` eg. `memory_model: GLSL450`." + )), + } + } else if arg == "version" { + if version.is_some() { + return Err(input.error( + "Duplicate `version` specification, please specify it at most once. The default is `version: 1.3`." + )); + } + if field.colon_token.is_none() { + return Err(input.error( + "Version must be specified as `version: maj.min`. The default is `version: 1.3`." + )); + } + if let Expr::Lit(ExprLit { + lit: Lit::Float(float), + .. + }) = field.expr + { + let mut radix_split = float.base10_digits().split('.'); + let (major, minor) = (radix_split.next(), radix_split.next()); + let major = if major == Some("1") { + 1 + } else { + return Err(input.error("Major version must be 1.")); + }; + let minor = match minor { + Some("0") => 0, + Some("1") => 1, + Some("2") => 2, + Some("3") => 3, + Some("4") => 4, + Some("5") => 5, + _ => return Err(input.error("Minor version must be in [0, 5], inclusive.")), + }; + version = Some((major, minor)); + } else { + return Err(input.error( + "Version must be specified as `version: maj.min`, the default is `version: 1.3`." + )); + } + } else if arg == "release" || arg == "debug" { + if release.is_none() { + release = Some(arg == "release"); + } else { + return Err(input.error( + "Duplicate compile mode specification, please specify `release` or `debug` at most once. \ + If unspecified, the default is `release`." + )); + } + } else { + return Err(input.error(format!( + "Unknown parameter: `{:?}`, valid parameters are `path`, `memory_model`, \ + `release` and `debug`.", + arg, + ))); + } + } + if path.is_none() { + return Err(input.error("Please provide a crate path: `path: \"path/to/crate\"`.")); + } + Ok(Self { + path: path.unwrap(), + memory_model: memory_model.unwrap_or(MemoryModel::Vulkan), + version: version.unwrap_or((1, 3)), + release: release.unwrap_or(true), + }) + } +} diff --git a/examples/runners/wgpu/Cargo.toml b/examples/runners/wgpu/Cargo.toml index bd5134cf0e..a097cdb392 100644 --- a/examples/runners/wgpu/Cargo.toml +++ b/examples/runners/wgpu/Cargo.toml @@ -12,8 +12,8 @@ crate-type = ["lib", "cdylib"] # 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"] +use-installed-tools = ["spirv-builder-macros/use-installed-tools"] +use-compiled-tools = ["spirv-builder-macros/use-compiled-tools"] [dependencies] cfg-if = "1.0.0" @@ -23,9 +23,7 @@ wgpu = "0.7.0" winit = { version = "0.24", features = ["web-sys"] } clap = "3.0.0-beta.2" strum = { version = "0.20", default_features = false, features = ["derive"] } - -[build-dependencies] -spirv-builder = { path = "../../../crates/spirv-builder", default-features = false } +spirv-builder-macros = { path = "../../../crates/spirv-builder-macros", default-features = false } [target.'cfg(target_os = "android")'.dependencies] ndk-glue = "0.2" diff --git a/examples/runners/wgpu/build.rs b/examples/runners/wgpu/build.rs deleted file mode 100644 index 0e18815223..0000000000 --- a/examples/runners/wgpu/build.rs +++ /dev/null @@ -1,17 +0,0 @@ -use spirv_builder::SpirvBuilder; -use std::error::Error; - -fn build_shader(path_to_create: &str) -> Result<(), Box> { - SpirvBuilder::new(path_to_create) - .spirv_version(1, 0) - .build()?; - Ok(()) -} - -fn main() -> Result<(), Box> { - build_shader("../../shaders/sky-shader")?; - build_shader("../../shaders/simplest-shader")?; - build_shader("../../shaders/compute-shader")?; - build_shader("../../shaders/mouse-shader")?; - Ok(()) -} diff --git a/examples/runners/wgpu/src/lib.rs b/examples/runners/wgpu/src/lib.rs index a6675fffc1..73813afa7a 100644 --- a/examples/runners/wgpu/src/lib.rs +++ b/examples/runners/wgpu/src/lib.rs @@ -43,6 +43,7 @@ )] use clap::Clap; +use spirv_builder_macros::compile_spirv; use strum::{Display, EnumString}; mod compute; @@ -58,10 +59,22 @@ pub enum RustGPUShader { fn shader_module(shader: RustGPUShader) -> wgpu::ShaderModuleDescriptor<'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")), - RustGPUShader::Mouse => wgpu::include_spirv!(env!("mouse_shader.spv")), + RustGPUShader::Simplest => wgpu::include_spirv!(compile_spirv! { + path: "examples/shaders/simplest-shader", + version: 1.0, + }), + RustGPUShader::Sky => wgpu::include_spirv!(compile_spirv! { + path: "examples/shaders/sky-shader", + version: 1.0, + }), + RustGPUShader::Compute => wgpu::include_spirv!(compile_spirv! { + path: "examples/shaders/compute-shader", + version: 1.0, + }), + RustGPUShader::Mouse => wgpu::include_spirv!(compile_spirv! { + path: "examples/shaders/mouse-shader", + version: 1.0, + }), } } diff --git a/examples/shaders/compute-shader/Cargo.toml b/examples/shaders/compute-shader/Cargo.toml index af69deb107..ffe35a8164 100644 --- a/examples/shaders/compute-shader/Cargo.toml +++ b/examples/shaders/compute-shader/Cargo.toml @@ -9,5 +9,4 @@ license = "MIT OR Apache-2.0" crate-type = ["dylib"] [dependencies] -spirv-std-macros = { path = "../../../crates/spirv-std-macros" } spirv-std = { path = "../../../crates/spirv-std" } diff --git a/examples/shaders/compute-shader/src/lib.rs b/examples/shaders/compute-shader/src/lib.rs index 19b3435087..84cfb2366e 100644 --- a/examples/shaders/compute-shader/src/lib.rs +++ b/examples/shaders/compute-shader/src/lib.rs @@ -10,8 +10,7 @@ extern crate spirv_std; #[cfg(not(target_arch = "spirv"))] -#[macro_use] -pub extern crate spirv_std_macros; +use spirv_std::macros::spirv; // LocalSize/numthreads of (x = 32, y = 1, z = 1) #[spirv(compute(threads(32)))]