Add exact_entrypoint_interface flag to shader! (Fixes #1556) (#1559)

* add `exact_entrypoint_interface` flag to vulkano-shaders

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>

* cargo fmt

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>

* oops

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>
This commit is contained in:
Arc'blroth 2021-04-26 07:32:52 -07:00 committed by GitHub
parent f1dad10cd4
commit a83f0fe489
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 54 additions and 9 deletions

View File

@ -37,6 +37,8 @@
- Traits that no longer make sense in this context have been removed: `FormatDesc`, the `Possible*FormatDesc` traits, `StrongStorage`. - Traits that no longer make sense in this context have been removed: `FormatDesc`, the `Possible*FormatDesc` traits, `StrongStorage`.
- In types that had a type parameter for the format type, it has been removed. - In types that had a type parameter for the format type, it has been removed.
- `AcceptsPixels` has been converted to `Pixel`, which is implemented on the pixel type rather than on the format type. - `AcceptsPixels` has been converted to `Pixel`, which is implemented on the pixel type rather than on the format type.
- **Breaking** `shader!` will generate descriptor information for all variables declared in the shader module, even if they are not used. *This reverts the default behavior from the last release.*
- **Breaking** Added the `exact_entrypoint_interface` option to `shader!` to force vulkano to only generate descriptor information for variables that are used. (the default behavior from the last release)
- Added two methods to `Format`: `planes` to query the number of planes in the format, and `aspects` to query what aspects an image of this type has. - Added two methods to `Format`: `planes` to query the number of planes in the format, and `aspects` to query what aspects an image of this type has.
- The deprecated `cause` trait function on Vulkano error types is replaced with `source`. - The deprecated `cause` trait function on Vulkano error types is replaced with `source`.
- Fixed bug in descriptor array layers check when the image is a cubemap. - Fixed bug in descriptor array layers check when the image is a cubemap.

View File

@ -204,6 +204,7 @@ pub(super) fn reflect<'a, I>(
spirv: &[u32], spirv: &[u32],
types_meta: TypesMeta, types_meta: TypesMeta,
input_paths: I, input_paths: I,
exact_entrypoint_interface: bool,
dump: bool, dump: bool,
) -> Result<TokenStream, Error> ) -> Result<TokenStream, Error>
where where
@ -263,8 +264,12 @@ where
let mut entry_points_outside_impl: Vec<TokenStream> = vec![]; let mut entry_points_outside_impl: Vec<TokenStream> = vec![];
for instruction in doc.instructions.iter() { for instruction in doc.instructions.iter() {
if let &Instruction::EntryPoint { .. } = instruction { if let &Instruction::EntryPoint { .. } = instruction {
let (outside, entry_point, descriptor_sets) = let (outside, entry_point, descriptor_sets) = entry_point::write_entry_point(
entry_point::write_entry_point(&doc, instruction, &types_meta); &doc,
instruction,
&types_meta,
exact_entrypoint_interface,
);
entry_points_inside_impl.push(entry_point); entry_points_inside_impl.push(entry_point);
entry_points_outside_impl.push(outside); entry_points_outside_impl.push(outside);
entry_points_outside_impl.push(descriptor_sets); entry_points_outside_impl.push(descriptor_sets);

View File

@ -31,11 +31,12 @@ pub(super) fn write_descriptor_sets(
entrypoint_id: u32, entrypoint_id: u32,
interface: &[u32], interface: &[u32],
types_meta: &TypesMeta, types_meta: &TypesMeta,
exact_entrypoint_interface: bool,
) -> TokenStream { ) -> TokenStream {
// TODO: somewhat implemented correctly // TODO: somewhat implemented correctly
// Finding all the descriptors. // Finding all the descriptors.
let descriptors = find_descriptors(doc, entrypoint_id, interface); let descriptors = find_descriptors(doc, entrypoint_id, interface, exact_entrypoint_interface);
// Looping to find all the push constant structs. // Looping to find all the push constant structs.
let mut push_constants_size = 0; let mut push_constants_size = 0;
@ -138,14 +139,19 @@ pub(super) fn write_descriptor_sets(
} }
} }
fn find_descriptors(doc: &Spirv, entrypoint_id: u32, interface: &[u32]) -> Vec<Descriptor> { fn find_descriptors(
doc: &Spirv,
entrypoint_id: u32,
interface: &[u32],
exact: bool,
) -> Vec<Descriptor> {
let mut descriptors = Vec::new(); let mut descriptors = Vec::new();
// For SPIR-V 1.4+, the entrypoint interface can specify variables of all storage classes, // For SPIR-V 1.4+, the entrypoint interface can specify variables of all storage classes,
// and most tools will put all used variables in the entrypoint interface. However, // and most tools will put all used variables in the entrypoint interface. However,
// SPIR-V 1.0-1.3 do not specify variables other than Input/Output ones in the interface, // SPIR-V 1.0-1.3 do not specify variables other than Input/Output ones in the interface,
// and instead the function itself must be inspected. // and instead the function itself must be inspected.
let variables = { let variables = if exact {
let mut found_variables: HashSet<u32> = interface.iter().cloned().collect(); let mut found_variables: HashSet<u32> = interface.iter().cloned().collect();
let mut inspected_functions: HashSet<u32> = HashSet::new(); let mut inspected_functions: HashSet<u32> = HashSet::new();
find_variables_in_function( find_variables_in_function(
@ -154,14 +160,16 @@ fn find_descriptors(doc: &Spirv, entrypoint_id: u32, interface: &[u32]) -> Vec<D
&mut inspected_functions, &mut inspected_functions,
&mut found_variables, &mut found_variables,
); );
found_variables Some(found_variables)
} else {
None
}; };
// Looping to find all the interface elements that have the `DescriptorSet` decoration. // Looping to find all the interface elements that have the `DescriptorSet` decoration.
for set_decoration in doc.get_decorations(Decoration::DecorationDescriptorSet) { for set_decoration in doc.get_decorations(Decoration::DecorationDescriptorSet) {
let variable_id = set_decoration.target_id; let variable_id = set_decoration.target_id;
if !variables.contains(&variable_id) { if exact && !variables.as_ref().unwrap().contains(&variable_id) {
continue; continue;
} }
@ -650,7 +658,7 @@ mod tests {
id, ref interface, .. id, ref interface, ..
} = instruction } = instruction
{ {
descriptors.push(find_descriptors(&doc, id, interface)); descriptors.push(find_descriptors(&doc, id, interface, true));
} }
} }
@ -724,7 +732,7 @@ mod tests {
id, ref interface, .. id, ref interface, ..
} = instruction } = instruction
{ {
let descriptors = find_descriptors(&doc, id, interface); let descriptors = find_descriptors(&doc, id, interface, true);
let mut bindings = Vec::new(); let mut bindings = Vec::new();
for d in descriptors { for d in descriptors {
bindings.push((d.set, d.binding)); bindings.push((d.set, d.binding));

View File

@ -19,6 +19,7 @@ pub(super) fn write_entry_point(
doc: &Spirv, doc: &Spirv,
instruction: &Instruction, instruction: &Instruction,
types_meta: &TypesMeta, types_meta: &TypesMeta,
exact_entrypoint_interface: bool,
) -> (TokenStream, TokenStream, TokenStream) { ) -> (TokenStream, TokenStream, TokenStream) {
let (execution, id, ep_name, interface) = match instruction { let (execution, id, ep_name, interface) = match instruction {
&Instruction::EntryPoint { &Instruction::EntryPoint {
@ -67,6 +68,7 @@ pub(super) fn write_entry_point(
id, id,
interface, interface,
&types_meta, &types_meta,
exact_entrypoint_interface,
); );
let spec_consts_struct = if crate::spec_consts::has_specialization_constants(doc) { let spec_consts_struct = if crate::spec_consts::has_specialization_constants(doc) {

View File

@ -134,6 +134,8 @@
//! Provides the path to precompiled SPIR-V bytecode, relative to `Cargo.toml`. //! Provides the path to precompiled SPIR-V bytecode, relative to `Cargo.toml`.
//! Cannot be used in conjunction with the `src` or `path` field. //! Cannot be used in conjunction with the `src` or `path` field.
//! This allows using shaders compiled through a separate build system. //! This allows using shaders compiled through a separate build system.
//! **Note**: If your shader contains multiple entrypoints with different
//! descriptor sets, you may also need to enable `exact_entrypoint_interface`.
//! //!
//! ## `include: ["...", "...", ..., "..."]` //! ## `include: ["...", "...", ..., "..."]`
//! //!
@ -172,6 +174,19 @@
//! final output of generated code the user can also use `dump` macro //! final output of generated code the user can also use `dump` macro
//! option(see below). //! option(see below).
//! //!
//! ## `exact_entrypoint_interface: true`
//!
//! By default, the macro assumes that all resources (Uniforms, Storage Buffers,
//! Images, Samplers, etc) need to be bound into a descriptor set, even if they are
//! not used in the shader code. However, shaders with multiple entrypoints may have
//! conflicting descriptor sets for each entrypoint. Enabling this option will force
//! the macro to only generate descriptor information for resources that are used
//! in each entrypoint.
//!
//! The macro determines which resources are used by looking at each entrypoint's
//! interface and bytecode. See [`src/descriptor_sets.rs`][descriptor_sets]
//! for the exact logic.
//!
//! ## `dump: true` //! ## `dump: true`
//! //!
//! The crate fails to compile but prints the generated rust code to stdout. //! The crate fails to compile but prints the generated rust code to stdout.
@ -186,6 +201,7 @@
//! [PipelineLayoutDesc]: https://docs.rs/vulkano/*/vulkano/descriptor/pipeline_layout/trait.PipelineLayoutDesc.html //! [PipelineLayoutDesc]: https://docs.rs/vulkano/*/vulkano/descriptor/pipeline_layout/trait.PipelineLayoutDesc.html
//! [SpecializationConstants]: https://docs.rs/vulkano/*/vulkano/pipeline/shader/trait.SpecializationConstants.html //! [SpecializationConstants]: https://docs.rs/vulkano/*/vulkano/pipeline/shader/trait.SpecializationConstants.html
//! [pipeline]: https://docs.rs/vulkano/*/vulkano/pipeline/index.html //! [pipeline]: https://docs.rs/vulkano/*/vulkano/pipeline/index.html
//! [descriptor_sets]: https://github.com/vulkano-rs/vulkano/blob/master/vulkano-shaders/src/descriptor_sets.rs#L142
#![doc(html_logo_url = "https://raw.githubusercontent.com/vulkano-rs/vulkano/master/logo.png")] #![doc(html_logo_url = "https://raw.githubusercontent.com/vulkano-rs/vulkano/master/logo.png")]
#![recursion_limit = "1024"] #![recursion_limit = "1024"]
@ -276,6 +292,7 @@ struct MacroInput {
include_directories: Vec<String>, include_directories: Vec<String>,
macro_defines: Vec<(String, String)>, macro_defines: Vec<(String, String)>,
types_meta: TypesMeta, types_meta: TypesMeta,
exact_entrypoint_interface: bool,
dump: bool, dump: bool,
} }
@ -287,6 +304,7 @@ impl Parse for MacroInput {
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 types_meta = None; let mut types_meta = None;
let mut exact_entrypoint_interface = None;
while !input.is_empty() { while !input.is_empty() {
let name: Ident = input.parse()?; let name: Ident = input.parse()?;
@ -504,6 +522,13 @@ impl Parse for MacroInput {
types_meta = Some(meta); 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" => { "dump" => {
if dump.is_some() { if dump.is_some() {
panic!("Only one `dump` can be defined") panic!("Only one `dump` can be defined")
@ -538,6 +563,7 @@ impl Parse for MacroInput {
dump, dump,
macro_defines, 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),
}) })
} }
} }
@ -575,6 +601,7 @@ pub fn shader(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
unsafe { from_raw_parts(bytes.as_slice().as_ptr() as *const u32, bytes.len() / 4) }, unsafe { from_raw_parts(bytes.as_slice().as_ptr() as *const u32, bytes.len() / 4) },
input.types_meta, input.types_meta,
empty(), empty(),
input.exact_entrypoint_interface,
input.dump, input.dump,
) )
.unwrap() .unwrap()
@ -631,6 +658,7 @@ pub fn shader(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
content.as_binary(), content.as_binary(),
input.types_meta, input.types_meta,
input_paths, input_paths,
input.exact_entrypoint_interface,
input.dump, input.dump,
) )
.unwrap() .unwrap()