diff --git a/CHANGELOG_VULKANO.md b/CHANGELOG_VULKANO.md index 681b69f2..611ff553 100644 --- a/CHANGELOG_VULKANO.md +++ b/CHANGELOG_VULKANO.md @@ -3,8 +3,10 @@ Please add new changes at the bottom, preceded by a hyphen -. Breaking changes should be listed first, before other changes, and should be preceded by - **Breaking**. --> + - **Breaking** Vulkano-shaders now checks if the device supports the shader's SPIR-V version, when loading the shader. -- Added `DeviceExtensions::khr_spirv_1_4`, which allows SPIR-V 1.4 shaders in Vulkan versions below 1.2. +- **Breaking** (but unlikely) Vulkano-shaders now compiles to SPIR-V 1.0 by default. If your shader needs features only available in a higher version, you can specify the target version on the `shader!` macro with the new `vulkan_version: "major.minor"` and `spirv_version: "major.minor"` arguments. +- Added `DeviceExtensions::khr_spirv_1_4`, which allows SPIR-V 1.4 shaders in Vulkan 1.1. - Added `FunctionPointers::api_version` to query the highest supported instance version. - Added `Instance::api_version` and `Device::api_version` to return the actual supported Vulkan version. These may differ between instance and device, and be lower than what `FunctionPointers::api_version` and `PhysicalDevice::api_version` return (currently never higher than 1.1, but this may change in the future). - Fixed the issue when creating a buffer with exportable fd on Linux(see to #1545). diff --git a/vulkano-shaders/src/codegen.rs b/vulkano-shaders/src/codegen.rs index e7a20990..4869cfd3 100644 --- a/vulkano-shaders/src/codegen.rs +++ b/vulkano-shaders/src/codegen.rs @@ -7,31 +7,27 @@ // notice may not be copied, modified, or distributed except // according to those terms. +use crate::entry_point; +use crate::enums::Capability; +use crate::enums::StorageClass; +use crate::parse; +use crate::parse::Instruction; +pub use crate::parse::ParseError; +use crate::read_file_to_string; +use crate::spec_consts; +use crate::structs; +use crate::TypesMeta; +use proc_macro2::{Span, TokenStream}; +pub use shaderc::{CompilationArtifact, IncludeType, ResolvedInclude, ShaderKind}; +use shaderc::{CompileOptions, Compiler, EnvVersion, SpirvVersion, TargetEnv}; use std::iter::Iterator; use std::path::Path; use std::{ cell::{RefCell, RefMut}, io::Error as IoError, }; - -use proc_macro2::{Span, TokenStream}; -use shaderc::{CompileOptions, Compiler, TargetEnv}; use syn::Ident; -pub use crate::parse::ParseError; -pub use shaderc::{CompilationArtifact, IncludeType, ResolvedInclude, ShaderKind}; - -use crate::enums::Capability; -use crate::enums::StorageClass; -use crate::parse::Instruction; - -use crate::entry_point; -use crate::parse; -use crate::read_file_to_string; -use crate::spec_consts; -use crate::structs; -use crate::TypesMeta; - pub(super) fn path_to_str(path: &Path) -> &str { path.to_str().expect( "Could not stringify the file to be included. Make sure the path consists of \ @@ -157,12 +153,22 @@ pub fn compile( ty: ShaderKind, include_directories: &[impl AsRef], macro_defines: &[(impl AsRef, impl AsRef)], + vulkan_version: Option, + spirv_version: Option, ) -> Result<(CompilationArtifact, Vec), String> { let includes_tracker = RefCell::new(Vec::new()); let mut compiler = Compiler::new().ok_or("failed to create GLSL compiler")?; let mut compile_options = CompileOptions::new().ok_or("failed to initialize compile option")?; - const ENV_VULKAN_VERSION: u32 = (1 << 22) | (1 << 12); - compile_options.set_target_env(TargetEnv::Vulkan, ENV_VULKAN_VERSION); + + compile_options.set_target_env( + TargetEnv::Vulkan, + vulkan_version.unwrap_or(EnvVersion::Vulkan1_0) as u32, + ); + + if let Some(spirv_version) = spirv_version { + compile_options.set_target_spirv(spirv_version); + } + let root_source_path = if let &Some(ref path) = &path { path } else { @@ -619,6 +625,8 @@ mod tests { ShaderKind::Vertex, &includes, &defines, + None, + None, ) .unwrap(); let doc = parse::parse_spirv(comp.as_binary()).unwrap(); @@ -645,6 +653,8 @@ mod tests { ShaderKind::Vertex, &includes, &defines, + None, + None, ) .unwrap(); let doc = parse::parse_spirv(comp.as_binary()).unwrap(); @@ -675,6 +685,8 @@ mod tests { ShaderKind::Vertex, &includes, &defines, + None, + None, ) .unwrap(); let doc = parse::parse_spirv(comp.as_binary()).unwrap(); @@ -698,6 +710,8 @@ mod tests { ShaderKind::Vertex, &empty_includes, &defines, + None, + None, ) .expect("Cannot resolve include files"); @@ -716,6 +730,8 @@ mod tests { root_path.join("tests").join("include_dir_b"), ], &defines, + None, + None, ) .expect("Cannot resolve include files"); assert_eq!( @@ -741,6 +757,8 @@ mod tests { ShaderKind::Vertex, &[root_path.join("tests").join("include_dir_a")], &defines, + None, + None, ) .expect("Cannot resolve include files"); assert_eq!( @@ -776,6 +794,8 @@ mod tests { ShaderKind::Vertex, &empty_includes, &defines, + None, + None, ) .expect("Cannot resolve include files"); assert_eq!( @@ -800,6 +820,8 @@ mod tests { root_path.join("tests").join("include_dir_c"), ], &defines, + None, + None, ) .expect("Cannot resolve include files"); assert_eq!( @@ -834,6 +856,8 @@ mod tests { ShaderKind::Vertex, &empty_includes, &no_defines, + None, + None, ); assert!(compile_no_defines.is_err()); @@ -844,6 +868,8 @@ mod tests { ShaderKind::Vertex, &empty_includes, &defines, + None, + None, ); compile_defines.expect("Setting shader macros did not work"); } diff --git a/vulkano-shaders/src/descriptor_sets.rs b/vulkano-shaders/src/descriptor_sets.rs index 7538b6ea..69cec6dc 100644 --- a/vulkano-shaders/src/descriptor_sets.rs +++ b/vulkano-shaders/src/descriptor_sets.rs @@ -723,6 +723,8 @@ mod tests { ShaderKind::Vertex, &includes, &defines, + None, + None, ) .unwrap(); let doc = parse::parse_spirv(comp.as_binary()).unwrap(); diff --git a/vulkano-shaders/src/lib.rs b/vulkano-shaders/src/lib.rs index ef7eb125..a0b78190 100644 --- a/vulkano-shaders/src/lib.rs +++ b/vulkano-shaders/src/lib.rs @@ -151,6 +151,16 @@ //! Adds the given macro definitions to the pre-processor. This is equivalent to passing `-DNAME=VALUE` //! on the command line. //! +//! ## `vulkan_version: "major.minor"` and `spirv_version: "major.minor"` +//! +//! Sets the Vulkan and SPIR-V versions to compile into, respectively. These map directly to the +//! [`set_target_env`](shaderc::CompileOptions::set_target_env) and +//! [`set_target_spirv`](shaderc::CompileOptions::set_target_spirv) compile options. +//! If neither option is specified, then SPIR-V 1.0 code targeting Vulkan 1.0 will be generated. +//! +//! The generated code must be supported by the device at runtime. If not, then an error will be +//! returned when calling `Shader::load`. +//! //! ## `types_meta: { use a::b; #[derive(Clone, Default, PartialEq ...)] impl Eq }` //! //! Extends implementations of Rust structs that represent Shader structs. @@ -211,12 +221,14 @@ extern crate quote; extern crate syn; extern crate proc_macro; +use crate::codegen::ShaderKind; +use shaderc::{EnvVersion, SpirvVersion}; use std::fs; use std::fs::File; use std::io::{Read, Result as IoResult}; use std::path::Path; +use std::slice::from_raw_parts; use std::{env, iter::empty}; - use syn::parse::{Parse, ParseStream, Result}; use syn::{ Ident, ItemUse, LitBool, LitStr, Meta, MetaList, NestedMeta, Path as SynPath, TypeImplTrait, @@ -231,9 +243,6 @@ mod spec_consts; mod spirv_search; mod structs; -use crate::codegen::ShaderKind; -use std::slice::from_raw_parts; - enum SourceKind { Src(String), Path(String), @@ -287,63 +296,34 @@ impl TypesMeta { } struct MacroInput { - shader_kind: ShaderKind, - source_kind: SourceKind, + dump: bool, + exact_entrypoint_interface: bool, include_directories: Vec, macro_defines: Vec<(String, String)>, + shader_kind: ShaderKind, + source_kind: SourceKind, + spirv_version: Option, types_meta: TypesMeta, - exact_entrypoint_interface: bool, - dump: bool, + vulkan_version: Option, } impl Parse for MacroInput { fn parse(input: ParseStream) -> Result { let mut dump = None; - let mut shader_kind = None; - let mut source_kind = None; + let mut exact_entrypoint_interface = None; let mut include_directories = Vec::new(); let mut macro_defines = Vec::new(); + let mut shader_kind = None; + let mut source_kind = None; + let mut spirv_version = None; let mut types_meta = None; - let mut exact_entrypoint_interface = None; + let mut vulkan_version = None; while !input.is_empty() { let name: Ident = input.parse()?; input.parse::()?; match name.to_string().as_ref() { - "ty" => { - if shader_kind.is_some() { - panic!("Only one `ty` can be defined") - } - - let ty: LitStr = input.parse()?; - let ty = match ty.value().as_ref() { - "vertex" => ShaderKind::Vertex, - "fragment" => ShaderKind::Fragment, - "geometry" => ShaderKind::Geometry, - "tess_ctrl" => ShaderKind::TessControl, - "tess_eval" => ShaderKind::TessEvaluation, - "compute" => ShaderKind::Compute, - _ => panic!("Unexpected shader type, valid values: vertex, fragment, geometry, tess_ctrl, tess_eval, compute") - }; - shader_kind = Some(ty); - } - "src" => { - if source_kind.is_some() { - panic!("Only one of `src`, `path`, or `bytes` can be defined") - } - - let src: LitStr = input.parse()?; - source_kind = Some(SourceKind::Src(src.value())); - } - "path" => { - if source_kind.is_some() { - panic!("Only one of `src`, `path`, or `bytes` can be defined") - } - - let path: LitStr = input.parse()?; - source_kind = Some(SourceKind::Path(path.value())); - } "bytes" => { if source_kind.is_some() { panic!("Only one of `src`, `path`, or `bytes` can be defined") @@ -370,6 +350,20 @@ impl Parse for MacroInput { } } } + "dump" => { + if dump.is_some() { + panic!("Only one `dump` can be defined") + } + let dump_lit: LitBool = input.parse()?; + dump = Some(dump_lit.value); + } + "exact_entrypoint_interface" => { + if exact_entrypoint_interface.is_some() { + panic!("Only one `dump` can be defined") + } + let lit: LitBool = input.parse()?; + exact_entrypoint_interface = Some(lit.value); + } "include" => { let in_brackets; bracketed!(in_brackets in input); @@ -384,6 +378,51 @@ impl Parse for MacroInput { } } } + "path" => { + if source_kind.is_some() { + panic!("Only one of `src`, `path`, or `bytes` can be defined") + } + + let path: LitStr = input.parse()?; + source_kind = Some(SourceKind::Path(path.value())); + } + "spirv_version" => { + let version: LitStr = input.parse()?; + spirv_version = Some(match version.value().as_ref() { + "1.0" => SpirvVersion::V1_0, + "1.1" => SpirvVersion::V1_1, + "1.2" => SpirvVersion::V1_2, + "1.3" => SpirvVersion::V1_3, + "1.4" => SpirvVersion::V1_4, + "1.5" => SpirvVersion::V1_5, + _ => panic!("Unknown SPIR-V version: {}", version.value()), + }); + } + "src" => { + if source_kind.is_some() { + panic!("Only one of `src`, `path`, or `bytes` can be defined") + } + + let src: LitStr = input.parse()?; + source_kind = Some(SourceKind::Src(src.value())); + } + "ty" => { + if shader_kind.is_some() { + panic!("Only one `ty` can be defined") + } + + let ty: LitStr = input.parse()?; + let ty = match ty.value().as_ref() { + "vertex" => ShaderKind::Vertex, + "fragment" => ShaderKind::Fragment, + "geometry" => ShaderKind::Geometry, + "tess_ctrl" => ShaderKind::TessControl, + "tess_eval" => ShaderKind::TessEvaluation, + "compute" => ShaderKind::Compute, + _ => panic!("Unexpected shader type, valid values: vertex, fragment, geometry, tess_ctrl, tess_eval, compute") + }; + shader_kind = Some(ty); + } "types_meta" => { let in_braces; braced!(in_braces in input); @@ -522,19 +561,14 @@ impl Parse for MacroInput { types_meta = Some(meta); } - "exact_entrypoint_interface" => { - if exact_entrypoint_interface.is_some() { - panic!("Only one `dump` can be defined") - } - let lit: LitBool = input.parse()?; - exact_entrypoint_interface = Some(lit.value); - } - "dump" => { - if dump.is_some() { - panic!("Only one `dump` can be defined") - } - let dump_lit: LitBool = input.parse()?; - dump = Some(dump_lit.value); + "vulkan_version" => { + let version: LitStr = input.parse()?; + vulkan_version = Some(match version.value().as_ref() { + "1.0" => EnvVersion::Vulkan1_0, + "1.1" => EnvVersion::Vulkan1_1, + "1.2" => EnvVersion::Vulkan1_2, + _ => panic!("Unknown Vulkan version: {}", version.value()), + }); } name => panic!("Unknown field name: {}", name), } @@ -557,13 +591,15 @@ impl Parse for MacroInput { let dump = dump.unwrap_or(false); Ok(Self { + dump, + exact_entrypoint_interface: exact_entrypoint_interface.unwrap_or(false), + include_directories, + macro_defines, shader_kind, source_kind, - include_directories, - dump, - macro_defines, + spirv_version, types_meta: types_meta.unwrap_or_else(|| TypesMeta::default()), - exact_entrypoint_interface: exact_entrypoint_interface.unwrap_or(false), + vulkan_version, }) } } @@ -641,6 +677,8 @@ pub fn shader(input: proc_macro::TokenStream) -> proc_macro::TokenStream { input.shader_kind, &include_paths, &input.macro_defines, + input.vulkan_version, + input.spirv_version, ) { Ok(ok) => ok, Err(e) => panic!("{}", e.replace("(s): ", "(s):\n")),