Allow specifying the target Vulkan and SPIR-V versions in vulkano-shaders (#1584)

This commit is contained in:
Rua 2021-05-17 14:38:15 +02:00 committed by GitHub
parent 794d005351
commit 8bedccf9bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 149 additions and 81 deletions

View File

@ -3,8 +3,10 @@
Please add new changes at the bottom, preceded by a hyphen -. 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 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. - **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 `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). - 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). - Fixed the issue when creating a buffer with exportable fd on Linux(see to #1545).

View File

@ -7,31 +7,27 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // 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::iter::Iterator;
use std::path::Path; use std::path::Path;
use std::{ use std::{
cell::{RefCell, RefMut}, cell::{RefCell, RefMut},
io::Error as IoError, io::Error as IoError,
}; };
use proc_macro2::{Span, TokenStream};
use shaderc::{CompileOptions, Compiler, TargetEnv};
use syn::Ident; 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 { pub(super) fn path_to_str(path: &Path) -> &str {
path.to_str().expect( path.to_str().expect(
"Could not stringify the file to be included. Make sure the path consists of \ "Could not stringify the file to be included. Make sure the path consists of \
@ -157,12 +153,22 @@ pub fn compile(
ty: ShaderKind, ty: ShaderKind,
include_directories: &[impl AsRef<Path>], include_directories: &[impl AsRef<Path>],
macro_defines: &[(impl AsRef<str>, impl AsRef<str>)], macro_defines: &[(impl AsRef<str>, impl AsRef<str>)],
vulkan_version: Option<EnvVersion>,
spirv_version: Option<SpirvVersion>,
) -> Result<(CompilationArtifact, Vec<String>), String> { ) -> Result<(CompilationArtifact, Vec<String>), String> {
let includes_tracker = RefCell::new(Vec::new()); let includes_tracker = RefCell::new(Vec::new());
let mut compiler = Compiler::new().ok_or("failed to create GLSL compiler")?; 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")?; 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 { let root_source_path = if let &Some(ref path) = &path {
path path
} else { } else {
@ -619,6 +625,8 @@ mod tests {
ShaderKind::Vertex, ShaderKind::Vertex,
&includes, &includes,
&defines, &defines,
None,
None,
) )
.unwrap(); .unwrap();
let doc = parse::parse_spirv(comp.as_binary()).unwrap(); let doc = parse::parse_spirv(comp.as_binary()).unwrap();
@ -645,6 +653,8 @@ mod tests {
ShaderKind::Vertex, ShaderKind::Vertex,
&includes, &includes,
&defines, &defines,
None,
None,
) )
.unwrap(); .unwrap();
let doc = parse::parse_spirv(comp.as_binary()).unwrap(); let doc = parse::parse_spirv(comp.as_binary()).unwrap();
@ -675,6 +685,8 @@ mod tests {
ShaderKind::Vertex, ShaderKind::Vertex,
&includes, &includes,
&defines, &defines,
None,
None,
) )
.unwrap(); .unwrap();
let doc = parse::parse_spirv(comp.as_binary()).unwrap(); let doc = parse::parse_spirv(comp.as_binary()).unwrap();
@ -698,6 +710,8 @@ mod tests {
ShaderKind::Vertex, ShaderKind::Vertex,
&empty_includes, &empty_includes,
&defines, &defines,
None,
None,
) )
.expect("Cannot resolve include files"); .expect("Cannot resolve include files");
@ -716,6 +730,8 @@ mod tests {
root_path.join("tests").join("include_dir_b"), root_path.join("tests").join("include_dir_b"),
], ],
&defines, &defines,
None,
None,
) )
.expect("Cannot resolve include files"); .expect("Cannot resolve include files");
assert_eq!( assert_eq!(
@ -741,6 +757,8 @@ mod tests {
ShaderKind::Vertex, ShaderKind::Vertex,
&[root_path.join("tests").join("include_dir_a")], &[root_path.join("tests").join("include_dir_a")],
&defines, &defines,
None,
None,
) )
.expect("Cannot resolve include files"); .expect("Cannot resolve include files");
assert_eq!( assert_eq!(
@ -776,6 +794,8 @@ mod tests {
ShaderKind::Vertex, ShaderKind::Vertex,
&empty_includes, &empty_includes,
&defines, &defines,
None,
None,
) )
.expect("Cannot resolve include files"); .expect("Cannot resolve include files");
assert_eq!( assert_eq!(
@ -800,6 +820,8 @@ mod tests {
root_path.join("tests").join("include_dir_c"), root_path.join("tests").join("include_dir_c"),
], ],
&defines, &defines,
None,
None,
) )
.expect("Cannot resolve include files"); .expect("Cannot resolve include files");
assert_eq!( assert_eq!(
@ -834,6 +856,8 @@ mod tests {
ShaderKind::Vertex, ShaderKind::Vertex,
&empty_includes, &empty_includes,
&no_defines, &no_defines,
None,
None,
); );
assert!(compile_no_defines.is_err()); assert!(compile_no_defines.is_err());
@ -844,6 +868,8 @@ mod tests {
ShaderKind::Vertex, ShaderKind::Vertex,
&empty_includes, &empty_includes,
&defines, &defines,
None,
None,
); );
compile_defines.expect("Setting shader macros did not work"); compile_defines.expect("Setting shader macros did not work");
} }

View File

@ -723,6 +723,8 @@ mod tests {
ShaderKind::Vertex, ShaderKind::Vertex,
&includes, &includes,
&defines, &defines,
None,
None,
) )
.unwrap(); .unwrap();
let doc = parse::parse_spirv(comp.as_binary()).unwrap(); let doc = parse::parse_spirv(comp.as_binary()).unwrap();

View File

@ -151,6 +151,16 @@
//! Adds the given macro definitions to the pre-processor. This is equivalent to passing `-DNAME=VALUE` //! Adds the given macro definitions to the pre-processor. This is equivalent to passing `-DNAME=VALUE`
//! on the command line. //! 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 }` //! ## `types_meta: { use a::b; #[derive(Clone, Default, PartialEq ...)] impl Eq }`
//! //!
//! Extends implementations of Rust structs that represent Shader structs. //! Extends implementations of Rust structs that represent Shader structs.
@ -211,12 +221,14 @@ extern crate quote;
extern crate syn; extern crate syn;
extern crate proc_macro; extern crate proc_macro;
use crate::codegen::ShaderKind;
use shaderc::{EnvVersion, SpirvVersion};
use std::fs; use std::fs;
use std::fs::File; use std::fs::File;
use std::io::{Read, Result as IoResult}; use std::io::{Read, Result as IoResult};
use std::path::Path; use std::path::Path;
use std::slice::from_raw_parts;
use std::{env, iter::empty}; use std::{env, iter::empty};
use syn::parse::{Parse, ParseStream, Result}; use syn::parse::{Parse, ParseStream, Result};
use syn::{ use syn::{
Ident, ItemUse, LitBool, LitStr, Meta, MetaList, NestedMeta, Path as SynPath, TypeImplTrait, Ident, ItemUse, LitBool, LitStr, Meta, MetaList, NestedMeta, Path as SynPath, TypeImplTrait,
@ -231,9 +243,6 @@ mod spec_consts;
mod spirv_search; mod spirv_search;
mod structs; mod structs;
use crate::codegen::ShaderKind;
use std::slice::from_raw_parts;
enum SourceKind { enum SourceKind {
Src(String), Src(String),
Path(String), Path(String),
@ -287,63 +296,34 @@ impl TypesMeta {
} }
struct MacroInput { struct MacroInput {
shader_kind: ShaderKind, dump: bool,
source_kind: SourceKind, exact_entrypoint_interface: bool,
include_directories: Vec<String>, include_directories: Vec<String>,
macro_defines: Vec<(String, String)>, macro_defines: Vec<(String, String)>,
shader_kind: ShaderKind,
source_kind: SourceKind,
spirv_version: Option<SpirvVersion>,
types_meta: TypesMeta, types_meta: TypesMeta,
exact_entrypoint_interface: bool, vulkan_version: Option<EnvVersion>,
dump: bool,
} }
impl Parse for MacroInput { impl Parse for MacroInput {
fn parse(input: ParseStream) -> Result<Self> { fn parse(input: ParseStream) -> Result<Self> {
let mut dump = None; let mut dump = None;
let mut shader_kind = None; let mut exact_entrypoint_interface = None;
let mut source_kind = None;
let mut include_directories = Vec::new(); let mut include_directories = Vec::new();
let mut macro_defines = 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 types_meta = None;
let mut exact_entrypoint_interface = None; let mut vulkan_version = None;
while !input.is_empty() { while !input.is_empty() {
let name: Ident = input.parse()?; let name: Ident = input.parse()?;
input.parse::<Token![:]>()?; input.parse::<Token![:]>()?;
match name.to_string().as_ref() { 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" => { "bytes" => {
if source_kind.is_some() { if source_kind.is_some() {
panic!("Only one of `src`, `path`, or `bytes` can be defined") 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" => { "include" => {
let in_brackets; let in_brackets;
bracketed!(in_brackets in input); 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" => { "types_meta" => {
let in_braces; let in_braces;
braced!(in_braces in input); braced!(in_braces in input);
@ -522,19 +561,14 @@ impl Parse for MacroInput {
types_meta = Some(meta); types_meta = Some(meta);
} }
"exact_entrypoint_interface" => { "vulkan_version" => {
if exact_entrypoint_interface.is_some() { let version: LitStr = input.parse()?;
panic!("Only one `dump` can be defined") vulkan_version = Some(match version.value().as_ref() {
} "1.0" => EnvVersion::Vulkan1_0,
let lit: LitBool = input.parse()?; "1.1" => EnvVersion::Vulkan1_1,
exact_entrypoint_interface = Some(lit.value); "1.2" => EnvVersion::Vulkan1_2,
} _ => panic!("Unknown Vulkan version: {}", version.value()),
"dump" => { });
if dump.is_some() {
panic!("Only one `dump` can be defined")
}
let dump_lit: LitBool = input.parse()?;
dump = Some(dump_lit.value);
} }
name => panic!("Unknown field name: {}", name), name => panic!("Unknown field name: {}", name),
} }
@ -557,13 +591,15 @@ impl Parse for MacroInput {
let dump = dump.unwrap_or(false); let dump = dump.unwrap_or(false);
Ok(Self { Ok(Self {
dump,
exact_entrypoint_interface: exact_entrypoint_interface.unwrap_or(false),
include_directories,
macro_defines,
shader_kind, shader_kind,
source_kind, source_kind,
include_directories, spirv_version,
dump,
macro_defines,
types_meta: types_meta.unwrap_or_else(|| TypesMeta::default()), 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, input.shader_kind,
&include_paths, &include_paths,
&input.macro_defines, &input.macro_defines,
input.vulkan_version,
input.spirv_version,
) { ) {
Ok(ok) => ok, Ok(ok) => ok,
Err(e) => panic!("{}", e.replace("(s): ", "(s):\n")), Err(e) => panic!("{}", e.replace("(s): ", "(s):\n")),