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 -.
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).

View File

@ -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<Path>],
macro_defines: &[(impl AsRef<str>, impl AsRef<str>)],
vulkan_version: Option<EnvVersion>,
spirv_version: Option<SpirvVersion>,
) -> Result<(CompilationArtifact, Vec<String>), 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");
}

View File

@ -723,6 +723,8 @@ mod tests {
ShaderKind::Vertex,
&includes,
&defines,
None,
None,
)
.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`
//! 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<String>,
macro_defines: Vec<(String, String)>,
shader_kind: ShaderKind,
source_kind: SourceKind,
spirv_version: Option<SpirvVersion>,
types_meta: TypesMeta,
exact_entrypoint_interface: bool,
dump: bool,
vulkan_version: Option<EnvVersion>,
}
impl Parse for MacroInput {
fn parse(input: ParseStream) -> Result<Self> {
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::<Token![:]>()?;
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")),